1210284Sjmallett/***********************license start***************
2215990Sjmallett * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3215990Sjmallett * reserved.
4210284Sjmallett *
5210284Sjmallett *
6215990Sjmallett * Redistribution and use in source and binary forms, with or without
7215990Sjmallett * modification, are permitted provided that the following conditions are
8215990Sjmallett * met:
9210284Sjmallett *
10215990Sjmallett *   * Redistributions of source code must retain the above copyright
11215990Sjmallett *     notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13215990Sjmallett *   * Redistributions in binary form must reproduce the above
14215990Sjmallett *     copyright notice, this list of conditions and the following
15215990Sjmallett *     disclaimer in the documentation and/or other materials provided
16215990Sjmallett *     with the distribution.
17215990Sjmallett
18215990Sjmallett *   * Neither the name of Cavium Networks nor the names of
19215990Sjmallett *     its contributors may be used to endorse or promote products
20215990Sjmallett *     derived from this software without specific prior written
21215990Sjmallett *     permission.
22215990Sjmallett
23215990Sjmallett * This Software, including technical data, may be subject to U.S. export  control
24215990Sjmallett * laws, including the U.S. Export Administration Act and its  associated
25215990Sjmallett * regulations, and may be subject to export or import  regulations in other
26215990Sjmallett * countries.
27215990Sjmallett
28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29215990Sjmallett * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38210284Sjmallett ***********************license end**************************************/
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett
44210284Sjmallett
45215990Sjmallett
46210284Sjmallett/**
47210284Sjmallett * @file
48210284Sjmallett *
49210284Sjmallett * Support library for the SPI
50210284Sjmallett *
51215990Sjmallett * <hr>$Revision: 49448 $<hr>
52210284Sjmallett */
53215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
54215990Sjmallett#include <linux/module.h>
55215990Sjmallett#include <asm/octeon/cvmx.h>
56215990Sjmallett#include <asm/octeon/cvmx-config.h>
57215990Sjmallett#include <asm/octeon/cvmx-spxx-defs.h>
58215990Sjmallett#include <asm/octeon/cvmx-stxx-defs.h>
59215990Sjmallett#include <asm/octeon/cvmx-srxx-defs.h>
60215990Sjmallett#include <asm/octeon/cvmx-pko.h>
61215990Sjmallett#include <asm/octeon/cvmx-spi.h>
62215990Sjmallett#include <asm/octeon/cvmx-clock.h>
63215990Sjmallett#else
64210284Sjmallett#include "cvmx.h"
65215990Sjmallett#if !defined(__FreeBSD__) || !defined(_KERNEL)
66215990Sjmallett#include "cvmx-config.h"
67215990Sjmallett#endif
68215990Sjmallett#include "cvmx-sysinfo.h"
69210284Sjmallett#include "cvmx-pko.h"
70210284Sjmallett#include "cvmx-spi.h"
71215990Sjmallett#include "cvmx-clock.h"
72215990Sjmallett#endif
73210284Sjmallett
74215990Sjmallett
75210284Sjmallett#define INVOKE_CB(function_p, args...) \
76210284Sjmallett        do { \
77210284Sjmallett            if (function_p) { \
78210284Sjmallett                res = function_p(args); \
79210284Sjmallett                if (res) \
80210284Sjmallett                    return res; \
81210284Sjmallett            } \
82210284Sjmallett        } while (0)
83210284Sjmallett
84210284Sjmallett#if CVMX_ENABLE_DEBUG_PRINTS
85210284Sjmallettstatic const char *modes[] = {"UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex"};
86210284Sjmallett#endif
87210284Sjmallett
88210284Sjmallett/* Default callbacks, can be overridden
89210284Sjmallett *  using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks
90210284Sjmallett */
91210284Sjmallettstatic cvmx_spi_callbacks_t cvmx_spi_callbacks = {
92210284Sjmallett  .reset_cb            = cvmx_spi_reset_cb,
93210284Sjmallett  .calendar_setup_cb   = cvmx_spi_calendar_setup_cb,
94210284Sjmallett  .clock_detect_cb     = cvmx_spi_clock_detect_cb,
95210284Sjmallett  .training_cb         = cvmx_spi_training_cb,
96210284Sjmallett  .calendar_sync_cb    = cvmx_spi_calendar_sync_cb,
97210284Sjmallett  .interface_up_cb     = cvmx_spi_interface_up_cb
98210284Sjmallett};
99210284Sjmallett
100210284Sjmallett/**
101210284Sjmallett * Get current SPI4 initialization callbacks
102210284Sjmallett *
103210284Sjmallett * @param callbacks  Pointer to the callbacks structure.to fill
104210284Sjmallett *
105210284Sjmallett * @return Pointer to cvmx_spi_callbacks_t structure.
106210284Sjmallett */
107210284Sjmallettvoid cvmx_spi_get_callbacks(cvmx_spi_callbacks_t * callbacks)
108210284Sjmallett{
109210284Sjmallett    memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks));
110210284Sjmallett}
111210284Sjmallett
112210284Sjmallett/**
113210284Sjmallett * Set new SPI4 initialization callbacks
114210284Sjmallett *
115210284Sjmallett * @param new_callbacks  Pointer to an updated callbacks structure.
116210284Sjmallett */
117210284Sjmallettvoid cvmx_spi_set_callbacks(cvmx_spi_callbacks_t * new_callbacks)
118210284Sjmallett{
119210284Sjmallett    memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks));
120210284Sjmallett}
121210284Sjmallett
122210284Sjmallett/**
123210284Sjmallett * Initialize and start the SPI interface.
124210284Sjmallett *
125210284Sjmallett * @param interface The identifier of the packet interface to configure and
126210284Sjmallett *                  use as a SPI interface.
127210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
128210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
129210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
130210284Sjmallett *                  active or the Rx data path is active, but not both).
131210284Sjmallett * @param timeout   Timeout to wait for clock synchronization in seconds
132210284Sjmallett * @param num_ports Number of SPI ports to configure
133210284Sjmallett *
134210284Sjmallett * @return Zero on success, negative of failure.
135210284Sjmallett */
136210284Sjmallettint cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, int num_ports)
137210284Sjmallett{
138210284Sjmallett    int res = -1;
139210284Sjmallett
140210284Sjmallett    if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
141210284Sjmallett        return res;
142210284Sjmallett
143210284Sjmallett    // Callback to perform SPI4 reset
144210284Sjmallett    INVOKE_CB( cvmx_spi_callbacks.reset_cb, interface, mode);
145210284Sjmallett
146210284Sjmallett    // Callback to perform calendar setup
147210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode, num_ports);
148210284Sjmallett
149210284Sjmallett    // Callback to perform clock detection
150210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
151210284Sjmallett
152210284Sjmallett    // Callback to perform SPI4 link training
153210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
154210284Sjmallett
155210284Sjmallett    // Callback to perform calendar sync
156210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout);
157210284Sjmallett
158210284Sjmallett    // Callback to handle interface coming up
159210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
160210284Sjmallett
161210284Sjmallett    return res;
162210284Sjmallett}
163210284Sjmallett
164210284Sjmallett/**
165210284Sjmallett * This routine restarts the SPI interface after it has lost synchronization
166210284Sjmallett * with its correspondent system.
167210284Sjmallett *
168210284Sjmallett * @param interface The identifier of the packet interface to configure and
169210284Sjmallett *                  use as a SPI interface.
170210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
171210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
172210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
173210284Sjmallett *                  active or the Rx data path is active, but not both).
174210284Sjmallett * @param timeout   Timeout to wait for clock synchronization in seconds
175210284Sjmallett * @return Zero on success, negative of failure.
176210284Sjmallett */
177210284Sjmallettint cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout)
178210284Sjmallett{
179210284Sjmallett    int res = -1;
180210284Sjmallett
181210284Sjmallett
182210284Sjmallett    if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
183210284Sjmallett        return res;
184210284Sjmallett
185215990Sjmallett#if CVMX_ENABLE_DEBUG_PRINTS
186210284Sjmallett    cvmx_dprintf ("SPI%d: Restart %s\n", interface, modes[mode]);
187215990Sjmallett#endif
188210284Sjmallett
189210284Sjmallett    // Callback to perform SPI4 reset
190210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface,mode);
191210284Sjmallett
192210284Sjmallett    // NOTE: Calendar setup is not performed during restart
193210284Sjmallett    //       Refer to cvmx_spi_start_interface() for the full sequence
194210284Sjmallett
195210284Sjmallett    // Callback to perform clock detection
196210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
197210284Sjmallett
198210284Sjmallett    // Callback to perform SPI4 link training
199210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
200210284Sjmallett
201210284Sjmallett    // Callback to perform calendar sync
202210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout);
203210284Sjmallett
204210284Sjmallett    // Callback to handle interface coming up
205210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
206210284Sjmallett
207210284Sjmallett    return res;
208210284Sjmallett}
209215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
210215990SjmallettEXPORT_SYMBOL(cvmx_spi_restart_interface);
211215990Sjmallett#endif
212210284Sjmallett
213210284Sjmallett/**
214210284Sjmallett * Callback to perform SPI4 reset
215210284Sjmallett *
216210284Sjmallett * @param interface The identifier of the packet interface to configure and
217210284Sjmallett *                  use as a SPI interface.
218210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
219210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
220210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
221210284Sjmallett *                  active or the Rx data path is active, but not both).
222210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
223210284Sjmallett */
224210284Sjmallettint cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
225210284Sjmallett{
226210284Sjmallett    cvmx_spxx_dbg_deskew_ctl_t spxx_dbg_deskew_ctl;
227210284Sjmallett    cvmx_spxx_clk_ctl_t spxx_clk_ctl;
228210284Sjmallett    cvmx_spxx_bist_stat_t spxx_bist_stat;
229210284Sjmallett    cvmx_spxx_int_msk_t spxx_int_msk;
230210284Sjmallett    cvmx_stxx_int_msk_t stxx_int_msk;
231210284Sjmallett    cvmx_spxx_trn4_ctl_t spxx_trn4_ctl;
232210284Sjmallett    int index;
233215990Sjmallett    uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
234210284Sjmallett
235210284Sjmallett    /* Disable SPI error events while we run BIST */
236210284Sjmallett    spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
237210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
238210284Sjmallett    stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
239210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
240210284Sjmallett
241210284Sjmallett    /* Run BIST in the SPI interface */
242210284Sjmallett    cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
243210284Sjmallett    cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
244210284Sjmallett    spxx_clk_ctl.u64 = 0;
245210284Sjmallett    spxx_clk_ctl.s.runbist = 1;
246210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
247210284Sjmallett    cvmx_wait (10 * MS);
248210284Sjmallett    spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
249210284Sjmallett    if (spxx_bist_stat.s.stat0)
250210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on receive datapath FIFO\n", interface);
251210284Sjmallett    if (spxx_bist_stat.s.stat1)
252210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", interface);
253210284Sjmallett    if (spxx_bist_stat.s.stat2)
254210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", interface);
255210284Sjmallett
256210284Sjmallett    /* Clear the calendar table after BIST to fix parity errors */
257210284Sjmallett    for (index=0; index<32; index++)
258210284Sjmallett    {
259210284Sjmallett        cvmx_srxx_spi4_calx_t srxx_spi4_calx;
260210284Sjmallett        cvmx_stxx_spi4_calx_t stxx_spi4_calx;
261210284Sjmallett
262210284Sjmallett        srxx_spi4_calx.u64 = 0;
263210284Sjmallett        srxx_spi4_calx.s.oddpar = 1;
264210284Sjmallett        cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
265210284Sjmallett
266210284Sjmallett        stxx_spi4_calx.u64 = 0;
267210284Sjmallett        stxx_spi4_calx.s.oddpar = 1;
268210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
269210284Sjmallett    }
270210284Sjmallett
271210284Sjmallett    /* Re enable reporting of error interrupts */
272210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_REG(interface), cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
273210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
274210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_REG(interface), cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
275210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
276210284Sjmallett
277210284Sjmallett    // Setup the CLKDLY right in the middle
278210284Sjmallett    spxx_clk_ctl.u64 = 0;
279210284Sjmallett    spxx_clk_ctl.s.seetrn = 0;
280210284Sjmallett    spxx_clk_ctl.s.clkdly = 0x10;
281210284Sjmallett    spxx_clk_ctl.s.runbist = 0;
282210284Sjmallett    spxx_clk_ctl.s.statdrv = 0;
283215990Sjmallett    spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
284210284Sjmallett    spxx_clk_ctl.s.sndtrn = 0;
285210284Sjmallett    spxx_clk_ctl.s.drptrn = 0;
286210284Sjmallett    spxx_clk_ctl.s.rcvtrn = 0;
287210284Sjmallett    spxx_clk_ctl.s.srxdlck = 0;
288210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
289210284Sjmallett    cvmx_wait (100 * MS);
290210284Sjmallett
291210284Sjmallett    // Reset SRX0 DLL
292210284Sjmallett    spxx_clk_ctl.s.srxdlck = 1;
293210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
294210284Sjmallett
295210284Sjmallett    // Waiting for Inf0 Spi4 RX DLL to lock
296210284Sjmallett    cvmx_wait (100 * MS);
297210284Sjmallett
298210284Sjmallett    // Enable dynamic alignment
299210284Sjmallett    spxx_trn4_ctl.s.trntest = 0;
300210284Sjmallett    spxx_trn4_ctl.s.jitter = 1;
301210284Sjmallett    spxx_trn4_ctl.s.clr_boot = 1;
302210284Sjmallett    spxx_trn4_ctl.s.set_boot = 0;
303210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN58XX))
304210284Sjmallett        spxx_trn4_ctl.s.maxdist = 3;
305210284Sjmallett    else
306210284Sjmallett        spxx_trn4_ctl.s.maxdist = 8;
307210284Sjmallett    spxx_trn4_ctl.s.macro_en = 1;
308210284Sjmallett    spxx_trn4_ctl.s.mux_en = 1;
309210284Sjmallett    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
310210284Sjmallett
311210284Sjmallett    spxx_dbg_deskew_ctl.u64 = 0;
312210284Sjmallett    cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), spxx_dbg_deskew_ctl.u64);
313210284Sjmallett
314210284Sjmallett    return 0;
315210284Sjmallett}
316210284Sjmallett
317210284Sjmallett/**
318210284Sjmallett * Callback to setup calendar and miscellaneous settings before clock detection
319210284Sjmallett *
320210284Sjmallett * @param interface The identifier of the packet interface to configure and
321210284Sjmallett *                  use as a SPI interface.
322210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
323210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
324210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
325210284Sjmallett *                  active or the Rx data path is active, but not both).
326210284Sjmallett * @param num_ports Number of ports to configure on SPI
327210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
328210284Sjmallett */
329210284Sjmallettint cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, int num_ports)
330210284Sjmallett{
331210284Sjmallett    int port;
332210284Sjmallett    int index;
333210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX)
334210284Sjmallett    {
335210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
336210284Sjmallett        cvmx_srxx_spi4_stat_t srxx_spi4_stat;
337210284Sjmallett
338210284Sjmallett        // SRX0 number of Ports
339210284Sjmallett        srxx_com_ctl.u64 = 0;
340210284Sjmallett        srxx_com_ctl.s.prts = num_ports - 1;
341210284Sjmallett        srxx_com_ctl.s.st_en = 0;
342210284Sjmallett        srxx_com_ctl.s.inf_en = 0;
343210284Sjmallett        cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
344210284Sjmallett
345210284Sjmallett        // SRX0 Calendar Table. This round robbins through all ports
346210284Sjmallett        port = 0;
347210284Sjmallett        index = 0;
348210284Sjmallett        while (port < num_ports)
349210284Sjmallett        {
350210284Sjmallett            cvmx_srxx_spi4_calx_t srxx_spi4_calx;
351210284Sjmallett            srxx_spi4_calx.u64 = 0;
352210284Sjmallett            srxx_spi4_calx.s.prt0 = port++;
353210284Sjmallett            srxx_spi4_calx.s.prt1 = port++;
354210284Sjmallett            srxx_spi4_calx.s.prt2 = port++;
355210284Sjmallett            srxx_spi4_calx.s.prt3 = port++;
356210284Sjmallett            srxx_spi4_calx.s.oddpar = ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
357210284Sjmallett            cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
358210284Sjmallett            index++;
359210284Sjmallett        }
360210284Sjmallett        srxx_spi4_stat.u64 = 0;
361210284Sjmallett        srxx_spi4_stat.s.len = num_ports;
362210284Sjmallett        srxx_spi4_stat.s.m = 1;
363210284Sjmallett        cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), srxx_spi4_stat.u64);
364210284Sjmallett    }
365210284Sjmallett
366210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX)
367210284Sjmallett    {
368210284Sjmallett        cvmx_stxx_arb_ctl_t stxx_arb_ctl;
369210284Sjmallett        cvmx_gmxx_tx_spi_max_t gmxx_tx_spi_max;
370210284Sjmallett        cvmx_gmxx_tx_spi_thresh_t gmxx_tx_spi_thresh;
371210284Sjmallett        cvmx_gmxx_tx_spi_ctl_t gmxx_tx_spi_ctl;
372210284Sjmallett        cvmx_stxx_spi4_stat_t stxx_spi4_stat;
373210284Sjmallett        cvmx_stxx_spi4_dat_t stxx_spi4_dat;
374210284Sjmallett
375210284Sjmallett        // STX0 Config
376210284Sjmallett        stxx_arb_ctl.u64 = 0;
377210284Sjmallett        stxx_arb_ctl.s.igntpa = 0;
378210284Sjmallett        stxx_arb_ctl.s.mintrn = 0;
379210284Sjmallett        cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
380210284Sjmallett
381210284Sjmallett        gmxx_tx_spi_max.u64 = 0;
382210284Sjmallett        gmxx_tx_spi_max.s.max1 = 8;
383210284Sjmallett        gmxx_tx_spi_max.s.max2 = 4;
384210284Sjmallett        gmxx_tx_spi_max.s.slice = 0;
385210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), gmxx_tx_spi_max.u64);
386210284Sjmallett
387210284Sjmallett        gmxx_tx_spi_thresh.u64 = 0;
388210284Sjmallett        gmxx_tx_spi_thresh.s.thresh = 4;
389210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), gmxx_tx_spi_thresh.u64);
390210284Sjmallett
391210284Sjmallett        gmxx_tx_spi_ctl.u64 = 0;
392210284Sjmallett        gmxx_tx_spi_ctl.s.tpa_clr = 0;
393210284Sjmallett        gmxx_tx_spi_ctl.s.cont_pkt = 0;
394210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), gmxx_tx_spi_ctl.u64);
395210284Sjmallett
396210284Sjmallett        // STX0 Training Control
397210284Sjmallett        stxx_spi4_dat.u64 = 0;
398210284Sjmallett        stxx_spi4_dat.s.alpha = 32;    /*Minimum needed by dynamic alignment*/
399210284Sjmallett        stxx_spi4_dat.s.max_t = 0xFFFF;  /*Minimum interval is 0x20*/
400210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64);
401210284Sjmallett
402210284Sjmallett        // STX0 Calendar Table. This round robbins through all ports
403210284Sjmallett        port = 0;
404210284Sjmallett        index = 0;
405210284Sjmallett        while (port < num_ports)
406210284Sjmallett        {
407210284Sjmallett            cvmx_stxx_spi4_calx_t stxx_spi4_calx;
408210284Sjmallett            stxx_spi4_calx.u64 = 0;
409210284Sjmallett            stxx_spi4_calx.s.prt0 = port++;
410210284Sjmallett            stxx_spi4_calx.s.prt1 = port++;
411210284Sjmallett            stxx_spi4_calx.s.prt2 = port++;
412210284Sjmallett            stxx_spi4_calx.s.prt3 = port++;
413210284Sjmallett            stxx_spi4_calx.s.oddpar = ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
414210284Sjmallett            cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
415210284Sjmallett            index++;
416210284Sjmallett        }
417210284Sjmallett        stxx_spi4_stat.u64 = 0;
418210284Sjmallett        stxx_spi4_stat.s.len = num_ports;
419210284Sjmallett        stxx_spi4_stat.s.m = 1;
420210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), stxx_spi4_stat.u64);
421210284Sjmallett    }
422210284Sjmallett
423210284Sjmallett    return 0;
424210284Sjmallett}
425210284Sjmallett
426210284Sjmallett/**
427210284Sjmallett * Callback to perform clock detection
428210284Sjmallett *
429210284Sjmallett * @param interface The identifier of the packet interface to configure and
430210284Sjmallett *                  use as a SPI interface.
431210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
432210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
433210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
434210284Sjmallett *                  active or the Rx data path is active, but not both).
435210284Sjmallett * @param timeout   Timeout to wait for clock synchronization in seconds
436210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
437210284Sjmallett */
438210284Sjmallettint cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
439210284Sjmallett{
440210284Sjmallett    int                          clock_transitions;
441210284Sjmallett    cvmx_spxx_clk_stat_t         stat;
442210284Sjmallett    uint64_t                     timeout_time;
443215990Sjmallett    uint64_t                     MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
444210284Sjmallett
445210284Sjmallett    /* Regardless of operating mode, both Tx and Rx clocks must be present
446210284Sjmallett        for the SPI interface to operate. */
447210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting to see TsClk...\n", interface);
448210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
449210284Sjmallett    /* Require 100 clock transitions in order to avoid any noise in the
450210284Sjmallett        beginning  */
451210284Sjmallett    clock_transitions = 100;
452210284Sjmallett    do
453210284Sjmallett    {
454210284Sjmallett        stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
455210284Sjmallett        if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions)
456210284Sjmallett        {
457210284Sjmallett            /* We've seen a clock transition, so decrement the number we still
458210284Sjmallett                need */
459210284Sjmallett            clock_transitions--;
460210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
461210284Sjmallett            stat.s.s4clk0 = 0;
462210284Sjmallett            stat.s.s4clk1 = 0;
463210284Sjmallett        }
464210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
465210284Sjmallett        {
466210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
467210284Sjmallett            return -1;
468210284Sjmallett        }
469210284Sjmallett    } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
470210284Sjmallett
471210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting to see RsClk...\n", interface);
472210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
473210284Sjmallett    /* Require 100 clock transitions in order to avoid any noise in the
474210284Sjmallett        beginning  */
475210284Sjmallett    clock_transitions = 100;
476210284Sjmallett    do
477210284Sjmallett    {
478210284Sjmallett        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
479210284Sjmallett        if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions)
480210284Sjmallett        {
481210284Sjmallett            /* We've seen a clock transition, so decrement the number we still
482210284Sjmallett                need */
483210284Sjmallett            clock_transitions--;
484210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
485210284Sjmallett            stat.s.d4clk0 = 0;
486210284Sjmallett            stat.s.d4clk1 = 0;
487210284Sjmallett        }
488210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
489210284Sjmallett        {
490210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
491210284Sjmallett            return -1;
492210284Sjmallett        }
493210284Sjmallett    } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
494210284Sjmallett
495210284Sjmallett    return 0;
496210284Sjmallett}
497210284Sjmallett
498210284Sjmallett/**
499210284Sjmallett * Callback to perform link training
500210284Sjmallett *
501210284Sjmallett * @param interface The identifier of the packet interface to configure and
502210284Sjmallett *                  use as a SPI interface.
503210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
504210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
505210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
506210284Sjmallett *                  active or the Rx data path is active, but not both).
507210284Sjmallett * @param timeout   Timeout to wait for link to be trained (in seconds)
508210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
509210284Sjmallett */
510210284Sjmallettint cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
511210284Sjmallett{
512210284Sjmallett    cvmx_spxx_trn4_ctl_t         spxx_trn4_ctl;
513210284Sjmallett    cvmx_spxx_clk_stat_t         stat;
514215990Sjmallett    uint64_t                     MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
515210284Sjmallett    uint64_t                     timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
516210284Sjmallett    int                          rx_training_needed;
517210284Sjmallett
518210284Sjmallett    // SRX0 & STX0 Inf0 Links are configured - begin training
519210284Sjmallett    cvmx_spxx_clk_ctl_t spxx_clk_ctl;
520210284Sjmallett    spxx_clk_ctl.u64 = 0;
521210284Sjmallett    spxx_clk_ctl.s.seetrn = 0;
522210284Sjmallett    spxx_clk_ctl.s.clkdly = 0x10;
523210284Sjmallett    spxx_clk_ctl.s.runbist = 0;
524210284Sjmallett    spxx_clk_ctl.s.statdrv = 0;
525215990Sjmallett    spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
526210284Sjmallett    spxx_clk_ctl.s.sndtrn = 1;
527210284Sjmallett    spxx_clk_ctl.s.drptrn = 1;
528210284Sjmallett    spxx_clk_ctl.s.rcvtrn = 1;
529210284Sjmallett    spxx_clk_ctl.s.srxdlck = 1;
530210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
531210284Sjmallett    cvmx_wait (1000 * MS);
532210284Sjmallett
533210284Sjmallett    // SRX0 clear the boot bit
534210284Sjmallett    spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
535210284Sjmallett    spxx_trn4_ctl.s.clr_boot = 1;
536210284Sjmallett    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
537210284Sjmallett
538210284Sjmallett    // Wait for the training sequence to complete
539210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting for training\n", interface);
540210284Sjmallett    cvmx_wait (1000 * MS);
541212844Sjmallett#if !defined(OCTEON_VENDOR_LANNER)
542210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;  /* Wait a really long time here */
543212844Sjmallett#else
544212844Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * 10;
545212844Sjmallett#endif
546210284Sjmallett    /* The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
547210284Sjmallett        We'll be pessimistic and wait for a lot more */
548210284Sjmallett    rx_training_needed = 500;
549210284Sjmallett    do {
550210284Sjmallett        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
551210284Sjmallett        if (stat.s.srxtrn && rx_training_needed)
552210284Sjmallett        {
553210284Sjmallett            rx_training_needed--;
554210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
555210284Sjmallett            stat.s.srxtrn = 0;
556210284Sjmallett        }
557210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
558210284Sjmallett        {
559210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
560210284Sjmallett            return -1;
561210284Sjmallett        }
562210284Sjmallett    } while (stat.s.srxtrn == 0);
563210284Sjmallett
564210284Sjmallett    return 0;
565210284Sjmallett}
566210284Sjmallett
567210284Sjmallett/**
568210284Sjmallett * Callback to perform calendar data synchronization
569210284Sjmallett *
570210284Sjmallett * @param interface The identifier of the packet interface to configure and
571210284Sjmallett *                  use as a SPI interface.
572210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
573210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
574210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
575210284Sjmallett *                  active or the Rx data path is active, but not both).
576210284Sjmallett * @param timeout   Timeout to wait for calendar data in seconds
577210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
578210284Sjmallett */
579210284Sjmallettint cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
580210284Sjmallett{
581215990Sjmallett    uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
582210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
583210284Sjmallett        // SRX0 interface should be good, send calendar data
584210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
585210284Sjmallett        cvmx_dprintf ("SPI%d: Rx is synchronized, start sending calendar data\n", interface);
586210284Sjmallett        srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
587210284Sjmallett        srxx_com_ctl.s.inf_en = 1;
588210284Sjmallett        srxx_com_ctl.s.st_en  = 1;
589210284Sjmallett        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
590210284Sjmallett    }
591210284Sjmallett
592210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
593210284Sjmallett        // STX0 has achieved sync
594210284Sjmallett        // The corespondant board should be sending calendar data
595210284Sjmallett        // Enable the STX0 STAT receiver.
596210284Sjmallett        cvmx_spxx_clk_stat_t stat;
597210284Sjmallett        uint64_t timeout_time;
598210284Sjmallett        cvmx_stxx_com_ctl_t stxx_com_ctl;
599210284Sjmallett        stxx_com_ctl.u64 = 0;
600210284Sjmallett        stxx_com_ctl.s.st_en = 1;
601210284Sjmallett        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
602210284Sjmallett
603210284Sjmallett        // Waiting for calendar sync on STX0 STAT
604210284Sjmallett        cvmx_dprintf ("SPI%d: Waiting to sync on STX[%d] STAT\n", interface, interface);
605210284Sjmallett        timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
606210284Sjmallett        // SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10)
607210284Sjmallett        do {
608210284Sjmallett            stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT (interface));
609210284Sjmallett            if (cvmx_get_cycle() > timeout_time)
610210284Sjmallett            {
611210284Sjmallett                cvmx_dprintf ("SPI%d: Timeout\n", interface);
612210284Sjmallett                return -1;
613210284Sjmallett            }
614210284Sjmallett        } while (stat.s.stxcal == 0);
615210284Sjmallett    }
616210284Sjmallett
617210284Sjmallett    return 0;
618210284Sjmallett}
619210284Sjmallett
620210284Sjmallett/**
621210284Sjmallett * Callback to handle interface up
622210284Sjmallett *
623210284Sjmallett * @param interface The identifier of the packet interface to configure and
624210284Sjmallett *                  use as a SPI interface.
625210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
626210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
627210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
628210284Sjmallett *                  active or the Rx data path is active, but not both).
629210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
630210284Sjmallett */
631210284Sjmallettint cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
632210284Sjmallett{
633210284Sjmallett    cvmx_gmxx_rxx_frm_min_t gmxx_rxx_frm_min;
634210284Sjmallett    cvmx_gmxx_rxx_frm_max_t gmxx_rxx_frm_max;
635210284Sjmallett    cvmx_gmxx_rxx_jabber_t gmxx_rxx_jabber;
636210284Sjmallett
637210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
638210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
639210284Sjmallett        srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
640210284Sjmallett        srxx_com_ctl.s.inf_en = 1;
641210284Sjmallett        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
642210284Sjmallett        cvmx_dprintf ("SPI%d: Rx is now up\n", interface);
643210284Sjmallett    }
644210284Sjmallett
645210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
646210284Sjmallett        cvmx_stxx_com_ctl_t stxx_com_ctl;
647210284Sjmallett        stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
648210284Sjmallett        stxx_com_ctl.s.inf_en = 1;
649210284Sjmallett        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
650210284Sjmallett        cvmx_dprintf ("SPI%d: Tx is now up\n", interface);
651210284Sjmallett    }
652210284Sjmallett
653210284Sjmallett    gmxx_rxx_frm_min.u64 = 0;
654210284Sjmallett    gmxx_rxx_frm_min.s.len = 64;
655210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0,interface), gmxx_rxx_frm_min.u64);
656210284Sjmallett    gmxx_rxx_frm_max.u64 = 0;
657210284Sjmallett    gmxx_rxx_frm_max.s.len = 64*1024 - 4;
658210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0,interface), gmxx_rxx_frm_max.u64);
659210284Sjmallett    gmxx_rxx_jabber.u64 = 0;
660210284Sjmallett    gmxx_rxx_jabber.s.cnt = 64*1024 - 4;
661210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0,interface), gmxx_rxx_jabber.u64);
662210284Sjmallett
663210284Sjmallett    return 0;
664210284Sjmallett}
665210284Sjmallett
666