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