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