cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cvmx-spi.c (22046B)


      1/***********************license start***************
      2 * Author: Cavium Networks
      3 *
      4 * Contact: support@caviumnetworks.com
      5 * This file is part of the OCTEON SDK
      6 *
      7 * Copyright (c) 2003-2008 Cavium Networks
      8 *
      9 * This file is free software; you can redistribute it and/or modify
     10 * it under the terms of the GNU General Public License, Version 2, as
     11 * published by the Free Software Foundation.
     12 *
     13 * This file is distributed in the hope that it will be useful, but
     14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
     15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
     16 * NONINFRINGEMENT.  See the GNU General Public License for more
     17 * details.
     18 *
     19 * You should have received a copy of the GNU General Public License
     20 * along with this file; if not, write to the Free Software
     21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
     22 * or visit http://www.gnu.org/licenses/.
     23 *
     24 * This file may also be available under a different license from Cavium.
     25 * Contact Cavium Networks for more information
     26 ***********************license end**************************************/
     27
     28/*
     29 *
     30 * Support library for the SPI
     31 */
     32#include <asm/octeon/octeon.h>
     33
     34#include <asm/octeon/cvmx-config.h>
     35
     36#include <asm/octeon/cvmx-pko.h>
     37#include <asm/octeon/cvmx-spi.h>
     38
     39#include <asm/octeon/cvmx-spxx-defs.h>
     40#include <asm/octeon/cvmx-stxx-defs.h>
     41#include <asm/octeon/cvmx-srxx-defs.h>
     42
     43#define INVOKE_CB(function_p, args...)		\
     44	do {					\
     45		if (function_p) {		\
     46			res = function_p(args); \
     47			if (res)		\
     48				return res;	\
     49		}				\
     50	} while (0)
     51
     52#if CVMX_ENABLE_DEBUG_PRINTS
     53static const char *modes[] =
     54    { "UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex" };
     55#endif
     56
     57/* Default callbacks, can be overridden
     58 *  using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks
     59 */
     60static cvmx_spi_callbacks_t cvmx_spi_callbacks = {
     61	.reset_cb = cvmx_spi_reset_cb,
     62	.calendar_setup_cb = cvmx_spi_calendar_setup_cb,
     63	.clock_detect_cb = cvmx_spi_clock_detect_cb,
     64	.training_cb = cvmx_spi_training_cb,
     65	.calendar_sync_cb = cvmx_spi_calendar_sync_cb,
     66	.interface_up_cb = cvmx_spi_interface_up_cb
     67};
     68
     69/*
     70 * Get current SPI4 initialization callbacks
     71 *
     72 * @callbacks:	Pointer to the callbacks structure.to fill
     73 *
     74 * Returns Pointer to cvmx_spi_callbacks_t structure.
     75 */
     76void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t *callbacks)
     77{
     78	memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks));
     79}
     80
     81/*
     82 * Set new SPI4 initialization callbacks
     83 *
     84 * @new_callbacks:  Pointer to an updated callbacks structure.
     85 */
     86void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks)
     87{
     88	memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks));
     89}
     90
     91/*
     92 * Initialize and start the SPI interface.
     93 *
     94 * @interface: The identifier of the packet interface to configure and
     95 *		    use as a SPI interface.
     96 * @mode:      The operating mode for the SPI interface. The interface
     97 *		    can operate as a full duplex (both Tx and Rx data paths
     98 *		    active) or as a halfplex (either the Tx data path is
     99 *		    active or the Rx data path is active, but not both).
    100 * @timeout:   Timeout to wait for clock synchronization in seconds
    101 * @num_ports: Number of SPI ports to configure
    102 *
    103 * Returns Zero on success, negative of failure.
    104 */
    105int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout,
    106			     int num_ports)
    107{
    108	int res = -1;
    109
    110	if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
    111		return res;
    112
    113	/* Callback to perform SPI4 reset */
    114	INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
    115
    116	/* Callback to perform calendar setup */
    117	INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode,
    118		  num_ports);
    119
    120	/* Callback to perform clock detection */
    121	INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
    122
    123	/* Callback to perform SPI4 link training */
    124	INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
    125
    126	/* Callback to perform calendar sync */
    127	INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
    128		  timeout);
    129
    130	/* Callback to handle interface coming up */
    131	INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
    132
    133	return res;
    134}
    135
    136/*
    137 * This routine restarts the SPI interface after it has lost synchronization
    138 * with its correspondent system.
    139 *
    140 * @interface: The identifier of the packet interface to configure and
    141 *		    use as a SPI interface.
    142 * @mode:      The operating mode for the SPI interface. The interface
    143 *		    can operate as a full duplex (both Tx and Rx data paths
    144 *		    active) or as a halfplex (either the Tx data path is
    145 *		    active or the Rx data path is active, but not both).
    146 * @timeout:   Timeout to wait for clock synchronization in seconds
    147 *
    148 * Returns Zero on success, negative of failure.
    149 */
    150int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout)
    151{
    152	int res = -1;
    153
    154	if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
    155		return res;
    156
    157	cvmx_dprintf("SPI%d: Restart %s\n", interface, modes[mode]);
    158
    159	/* Callback to perform SPI4 reset */
    160	INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode);
    161
    162	/* NOTE: Calendar setup is not performed during restart */
    163	/*	 Refer to cvmx_spi_start_interface() for the full sequence */
    164
    165	/* Callback to perform clock detection */
    166	INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
    167
    168	/* Callback to perform SPI4 link training */
    169	INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
    170
    171	/* Callback to perform calendar sync */
    172	INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode,
    173		  timeout);
    174
    175	/* Callback to handle interface coming up */
    176	INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
    177
    178	return res;
    179}
    180EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface);
    181
    182/*
    183 * Callback to perform SPI4 reset
    184 *
    185 * @interface: The identifier of the packet interface to configure and
    186 *		    use as a SPI interface.
    187 * @mode:      The operating mode for the SPI interface. The interface
    188 *		    can operate as a full duplex (both Tx and Rx data paths
    189 *		    active) or as a halfplex (either the Tx data path is
    190 *		    active or the Rx data path is active, but not both).
    191 *
    192 * Returns Zero on success, non-zero error code on failure (will cause
    193 * SPI initialization to abort)
    194 */
    195int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
    196{
    197	union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl;
    198	union cvmx_spxx_clk_ctl spxx_clk_ctl;
    199	union cvmx_spxx_bist_stat spxx_bist_stat;
    200	union cvmx_spxx_int_msk spxx_int_msk;
    201	union cvmx_stxx_int_msk stxx_int_msk;
    202	union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
    203	int index;
    204	uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
    205
    206	/* Disable SPI error events while we run BIST */
    207	spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
    208	cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
    209	stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
    210	cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
    211
    212	/* Run BIST in the SPI interface */
    213	cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
    214	cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
    215	spxx_clk_ctl.u64 = 0;
    216	spxx_clk_ctl.s.runbist = 1;
    217	cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
    218	__delay(10 * MS);
    219	spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
    220	if (spxx_bist_stat.s.stat0)
    221		cvmx_dprintf
    222		    ("ERROR SPI%d: BIST failed on receive datapath FIFO\n",
    223		     interface);
    224	if (spxx_bist_stat.s.stat1)
    225		cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n",
    226			     interface);
    227	if (spxx_bist_stat.s.stat2)
    228		cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n",
    229			     interface);
    230
    231	/* Clear the calendar table after BIST to fix parity errors */
    232	for (index = 0; index < 32; index++) {
    233		union cvmx_srxx_spi4_calx srxx_spi4_calx;
    234		union cvmx_stxx_spi4_calx stxx_spi4_calx;
    235
    236		srxx_spi4_calx.u64 = 0;
    237		srxx_spi4_calx.s.oddpar = 1;
    238		cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
    239			       srxx_spi4_calx.u64);
    240
    241		stxx_spi4_calx.u64 = 0;
    242		stxx_spi4_calx.s.oddpar = 1;
    243		cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
    244			       stxx_spi4_calx.u64);
    245	}
    246
    247	/* Re enable reporting of error interrupts */
    248	cvmx_write_csr(CVMX_SPXX_INT_REG(interface),
    249		       cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
    250	cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
    251	cvmx_write_csr(CVMX_STXX_INT_REG(interface),
    252		       cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
    253	cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
    254
    255	/* Setup the CLKDLY right in the middle */
    256	spxx_clk_ctl.u64 = 0;
    257	spxx_clk_ctl.s.seetrn = 0;
    258	spxx_clk_ctl.s.clkdly = 0x10;
    259	spxx_clk_ctl.s.runbist = 0;
    260	spxx_clk_ctl.s.statdrv = 0;
    261	/* This should always be on the opposite edge as statdrv */
    262	spxx_clk_ctl.s.statrcv = 1;
    263	spxx_clk_ctl.s.sndtrn = 0;
    264	spxx_clk_ctl.s.drptrn = 0;
    265	spxx_clk_ctl.s.rcvtrn = 0;
    266	spxx_clk_ctl.s.srxdlck = 0;
    267	cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
    268	__delay(100 * MS);
    269
    270	/* Reset SRX0 DLL */
    271	spxx_clk_ctl.s.srxdlck = 1;
    272	cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
    273
    274	/* Waiting for Inf0 Spi4 RX DLL to lock */
    275	__delay(100 * MS);
    276
    277	/* Enable dynamic alignment */
    278	spxx_trn4_ctl.s.trntest = 0;
    279	spxx_trn4_ctl.s.jitter = 1;
    280	spxx_trn4_ctl.s.clr_boot = 1;
    281	spxx_trn4_ctl.s.set_boot = 0;
    282	if (OCTEON_IS_MODEL(OCTEON_CN58XX))
    283		spxx_trn4_ctl.s.maxdist = 3;
    284	else
    285		spxx_trn4_ctl.s.maxdist = 8;
    286	spxx_trn4_ctl.s.macro_en = 1;
    287	spxx_trn4_ctl.s.mux_en = 1;
    288	cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
    289
    290	spxx_dbg_deskew_ctl.u64 = 0;
    291	cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface),
    292		       spxx_dbg_deskew_ctl.u64);
    293
    294	return 0;
    295}
    296
    297/*
    298 * Callback to setup calendar and miscellaneous settings before clock detection
    299 *
    300 * @interface: The identifier of the packet interface to configure and
    301 *		    use as a SPI interface.
    302 * @mode:      The operating mode for the SPI interface. The interface
    303 *		    can operate as a full duplex (both Tx and Rx data paths
    304 *		    active) or as a halfplex (either the Tx data path is
    305 *		    active or the Rx data path is active, but not both).
    306 * @num_ports: Number of ports to configure on SPI
    307 *
    308 * Returns Zero on success, non-zero error code on failure (will cause
    309 * SPI initialization to abort)
    310 */
    311int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
    312			       int num_ports)
    313{
    314	int port;
    315	int index;
    316	if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
    317		union cvmx_srxx_com_ctl srxx_com_ctl;
    318		union cvmx_srxx_spi4_stat srxx_spi4_stat;
    319
    320		/* SRX0 number of Ports */
    321		srxx_com_ctl.u64 = 0;
    322		srxx_com_ctl.s.prts = num_ports - 1;
    323		srxx_com_ctl.s.st_en = 0;
    324		srxx_com_ctl.s.inf_en = 0;
    325		cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
    326
    327		/* SRX0 Calendar Table. This round robbins through all ports */
    328		port = 0;
    329		index = 0;
    330		while (port < num_ports) {
    331			union cvmx_srxx_spi4_calx srxx_spi4_calx;
    332			srxx_spi4_calx.u64 = 0;
    333			srxx_spi4_calx.s.prt0 = port++;
    334			srxx_spi4_calx.s.prt1 = port++;
    335			srxx_spi4_calx.s.prt2 = port++;
    336			srxx_spi4_calx.s.prt3 = port++;
    337			srxx_spi4_calx.s.oddpar =
    338			    ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
    339			cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
    340				       srxx_spi4_calx.u64);
    341			index++;
    342		}
    343		srxx_spi4_stat.u64 = 0;
    344		srxx_spi4_stat.s.len = num_ports;
    345		srxx_spi4_stat.s.m = 1;
    346		cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
    347			       srxx_spi4_stat.u64);
    348	}
    349
    350	if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
    351		union cvmx_stxx_arb_ctl stxx_arb_ctl;
    352		union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
    353		union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
    354		union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
    355		union cvmx_stxx_spi4_stat stxx_spi4_stat;
    356		union cvmx_stxx_spi4_dat stxx_spi4_dat;
    357
    358		/* STX0 Config */
    359		stxx_arb_ctl.u64 = 0;
    360		stxx_arb_ctl.s.igntpa = 0;
    361		stxx_arb_ctl.s.mintrn = 0;
    362		cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
    363
    364		gmxx_tx_spi_max.u64 = 0;
    365		gmxx_tx_spi_max.s.max1 = 8;
    366		gmxx_tx_spi_max.s.max2 = 4;
    367		gmxx_tx_spi_max.s.slice = 0;
    368		cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
    369			       gmxx_tx_spi_max.u64);
    370
    371		gmxx_tx_spi_thresh.u64 = 0;
    372		gmxx_tx_spi_thresh.s.thresh = 4;
    373		cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
    374			       gmxx_tx_spi_thresh.u64);
    375
    376		gmxx_tx_spi_ctl.u64 = 0;
    377		gmxx_tx_spi_ctl.s.tpa_clr = 0;
    378		gmxx_tx_spi_ctl.s.cont_pkt = 0;
    379		cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
    380			       gmxx_tx_spi_ctl.u64);
    381
    382		/* STX0 Training Control */
    383		stxx_spi4_dat.u64 = 0;
    384		/*Minimum needed by dynamic alignment */
    385		stxx_spi4_dat.s.alpha = 32;
    386		stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */
    387		cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
    388			       stxx_spi4_dat.u64);
    389
    390		/* STX0 Calendar Table. This round robbins through all ports */
    391		port = 0;
    392		index = 0;
    393		while (port < num_ports) {
    394			union cvmx_stxx_spi4_calx stxx_spi4_calx;
    395			stxx_spi4_calx.u64 = 0;
    396			stxx_spi4_calx.s.prt0 = port++;
    397			stxx_spi4_calx.s.prt1 = port++;
    398			stxx_spi4_calx.s.prt2 = port++;
    399			stxx_spi4_calx.s.prt3 = port++;
    400			stxx_spi4_calx.s.oddpar =
    401			    ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
    402			cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
    403				       stxx_spi4_calx.u64);
    404			index++;
    405		}
    406		stxx_spi4_stat.u64 = 0;
    407		stxx_spi4_stat.s.len = num_ports;
    408		stxx_spi4_stat.s.m = 1;
    409		cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
    410			       stxx_spi4_stat.u64);
    411	}
    412
    413	return 0;
    414}
    415
    416/*
    417 * Callback to perform clock detection
    418 *
    419 * @interface: The identifier of the packet interface to configure and
    420 *		    use as a SPI interface.
    421 * @mode:      The operating mode for the SPI interface. The interface
    422 *		    can operate as a full duplex (both Tx and Rx data paths
    423 *		    active) or as a halfplex (either the Tx data path is
    424 *		    active or the Rx data path is active, but not both).
    425 * @timeout:   Timeout to wait for clock synchronization in seconds
    426 *
    427 * Returns Zero on success, non-zero error code on failure (will cause
    428 * SPI initialization to abort)
    429 */
    430int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
    431{
    432	int clock_transitions;
    433	union cvmx_spxx_clk_stat stat;
    434	uint64_t timeout_time;
    435	uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
    436
    437	/*
    438	 * Regardless of operating mode, both Tx and Rx clocks must be
    439	 * present for the SPI interface to operate.
    440	 */
    441	cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface);
    442	timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    443	/*
    444	 * Require 100 clock transitions in order to avoid any noise
    445	 * in the beginning.
    446	 */
    447	clock_transitions = 100;
    448	do {
    449		stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
    450		if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) {
    451			/*
    452			 * We've seen a clock transition, so decrement
    453			 * the number we still need.
    454			 */
    455			clock_transitions--;
    456			cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
    457			stat.s.s4clk0 = 0;
    458			stat.s.s4clk1 = 0;
    459		}
    460		if (cvmx_get_cycle() > timeout_time) {
    461			cvmx_dprintf("SPI%d: Timeout\n", interface);
    462			return -1;
    463		}
    464	} while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
    465
    466	cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface);
    467	timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    468	/*
    469	 * Require 100 clock transitions in order to avoid any noise in the
    470	 * beginning.
    471	 */
    472	clock_transitions = 100;
    473	do {
    474		stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
    475		if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) {
    476			/*
    477			 * We've seen a clock transition, so decrement
    478			 * the number we still need
    479			 */
    480			clock_transitions--;
    481			cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
    482			stat.s.d4clk0 = 0;
    483			stat.s.d4clk1 = 0;
    484		}
    485		if (cvmx_get_cycle() > timeout_time) {
    486			cvmx_dprintf("SPI%d: Timeout\n", interface);
    487			return -1;
    488		}
    489	} while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
    490
    491	return 0;
    492}
    493
    494/*
    495 * Callback to perform link training
    496 *
    497 * @interface: The identifier of the packet interface to configure and
    498 *		    use as a SPI interface.
    499 * @mode:      The operating mode for the SPI interface. The interface
    500 *		    can operate as a full duplex (both Tx and Rx data paths
    501 *		    active) or as a halfplex (either the Tx data path is
    502 *		    active or the Rx data path is active, but not both).
    503 * @timeout:   Timeout to wait for link to be trained (in seconds)
    504 *
    505 * Returns Zero on success, non-zero error code on failure (will cause
    506 * SPI initialization to abort)
    507 */
    508int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
    509{
    510	union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
    511	union cvmx_spxx_clk_stat stat;
    512	uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
    513	uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    514	int rx_training_needed;
    515
    516	/* SRX0 & STX0 Inf0 Links are configured - begin training */
    517	union cvmx_spxx_clk_ctl spxx_clk_ctl;
    518	spxx_clk_ctl.u64 = 0;
    519	spxx_clk_ctl.s.seetrn = 0;
    520	spxx_clk_ctl.s.clkdly = 0x10;
    521	spxx_clk_ctl.s.runbist = 0;
    522	spxx_clk_ctl.s.statdrv = 0;
    523	/* This should always be on the opposite edge as statdrv */
    524	spxx_clk_ctl.s.statrcv = 1;
    525	spxx_clk_ctl.s.sndtrn = 1;
    526	spxx_clk_ctl.s.drptrn = 1;
    527	spxx_clk_ctl.s.rcvtrn = 1;
    528	spxx_clk_ctl.s.srxdlck = 1;
    529	cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
    530	__delay(1000 * MS);
    531
    532	/* SRX0 clear the boot bit */
    533	spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
    534	spxx_trn4_ctl.s.clr_boot = 1;
    535	cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
    536
    537	/* Wait for the training sequence to complete */
    538	cvmx_dprintf("SPI%d: Waiting for training\n", interface);
    539	__delay(1000 * MS);
    540	/* Wait a really long time here */
    541	timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;
    542	/*
    543	 * The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
    544	 * We'll be pessimistic and wait for a lot more.
    545	 */
    546	rx_training_needed = 500;
    547	do {
    548		stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
    549		if (stat.s.srxtrn && rx_training_needed) {
    550			rx_training_needed--;
    551			cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
    552			stat.s.srxtrn = 0;
    553		}
    554		if (cvmx_get_cycle() > timeout_time) {
    555			cvmx_dprintf("SPI%d: Timeout\n", interface);
    556			return -1;
    557		}
    558	} while (stat.s.srxtrn == 0);
    559
    560	return 0;
    561}
    562
    563/*
    564 * Callback to perform calendar data synchronization
    565 *
    566 * @interface: The identifier of the packet interface to configure and
    567 *		    use as a SPI interface.
    568 * @mode:      The operating mode for the SPI interface. The interface
    569 *		    can operate as a full duplex (both Tx and Rx data paths
    570 *		    active) or as a halfplex (either the Tx data path is
    571 *		    active or the Rx data path is active, but not both).
    572 * @timeout:   Timeout to wait for calendar data in seconds
    573 *
    574 * Returns Zero on success, non-zero error code on failure (will cause
    575 * SPI initialization to abort)
    576 */
    577int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
    578{
    579	uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
    580	if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
    581		/* SRX0 interface should be good, send calendar data */
    582		union cvmx_srxx_com_ctl srxx_com_ctl;
    583		cvmx_dprintf
    584		    ("SPI%d: Rx is synchronized, start sending calendar data\n",
    585		     interface);
    586		srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
    587		srxx_com_ctl.s.inf_en = 1;
    588		srxx_com_ctl.s.st_en = 1;
    589		cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
    590	}
    591
    592	if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
    593		/* STX0 has achieved sync */
    594		/* The corespondant board should be sending calendar data */
    595		/* Enable the STX0 STAT receiver. */
    596		union cvmx_spxx_clk_stat stat;
    597		uint64_t timeout_time;
    598		union cvmx_stxx_com_ctl stxx_com_ctl;
    599		stxx_com_ctl.u64 = 0;
    600		stxx_com_ctl.s.st_en = 1;
    601		cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
    602
    603		/* Waiting for calendar sync on STX0 STAT */
    604		cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n",
    605			     interface, interface);
    606		timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
    607		/* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */
    608		do {
    609			stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
    610			if (cvmx_get_cycle() > timeout_time) {
    611				cvmx_dprintf("SPI%d: Timeout\n", interface);
    612				return -1;
    613			}
    614		} while (stat.s.stxcal == 0);
    615	}
    616
    617	return 0;
    618}
    619
    620/*
    621 * Callback to handle interface up
    622 *
    623 * @interface: The identifier of the packet interface to configure and
    624 *		    use as a SPI interface.
    625 * @mode:      The operating mode for the SPI interface. The interface
    626 *		    can operate as a full duplex (both Tx and Rx data paths
    627 *		    active) or as a halfplex (either the Tx data path is
    628 *		    active or the Rx data path is active, but not both).
    629 *
    630 * Returns Zero on success, non-zero error code on failure (will cause
    631 * SPI initialization to abort)
    632 */
    633int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
    634{
    635	union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min;
    636	union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max;
    637	union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber;
    638
    639	if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
    640		union cvmx_srxx_com_ctl srxx_com_ctl;
    641		srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
    642		srxx_com_ctl.s.inf_en = 1;
    643		cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
    644		cvmx_dprintf("SPI%d: Rx is now up\n", interface);
    645	}
    646
    647	if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
    648		union cvmx_stxx_com_ctl stxx_com_ctl;
    649		stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
    650		stxx_com_ctl.s.inf_en = 1;
    651		cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
    652		cvmx_dprintf("SPI%d: Tx is now up\n", interface);
    653	}
    654
    655	gmxx_rxx_frm_min.u64 = 0;
    656	gmxx_rxx_frm_min.s.len = 64;
    657	cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface),
    658		       gmxx_rxx_frm_min.u64);
    659	gmxx_rxx_frm_max.u64 = 0;
    660	gmxx_rxx_frm_max.s.len = 64 * 1024 - 4;
    661	cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface),
    662		       gmxx_rxx_frm_max.u64);
    663	gmxx_rxx_jabber.u64 = 0;
    664	gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4;
    665	cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64);
    666
    667	return 0;
    668}