1210284Sjmallett/***********************license start***************
2232812Sjmallett * Copyright (c) 2003-2010  Cavium Inc. (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
18232812Sjmallett *   * Neither the name of Cavium Inc. 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"
29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. 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 *
51232812Sjmallett * <hr>$Revision: 70030 $<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
185210284Sjmallett    cvmx_dprintf ("SPI%d: Restart %s\n", interface, modes[mode]);
186210284Sjmallett
187210284Sjmallett    // Callback to perform SPI4 reset
188210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface,mode);
189210284Sjmallett
190210284Sjmallett    // NOTE: Calendar setup is not performed during restart
191210284Sjmallett    //       Refer to cvmx_spi_start_interface() for the full sequence
192210284Sjmallett
193210284Sjmallett    // Callback to perform clock detection
194210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
195210284Sjmallett
196210284Sjmallett    // Callback to perform SPI4 link training
197210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
198210284Sjmallett
199210284Sjmallett    // Callback to perform calendar sync
200210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout);
201210284Sjmallett
202210284Sjmallett    // Callback to handle interface coming up
203210284Sjmallett    INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
204210284Sjmallett
205210284Sjmallett    return res;
206210284Sjmallett}
207215990Sjmallett#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
208215990SjmallettEXPORT_SYMBOL(cvmx_spi_restart_interface);
209215990Sjmallett#endif
210210284Sjmallett
211210284Sjmallett/**
212210284Sjmallett * Callback to perform SPI4 reset
213210284Sjmallett *
214210284Sjmallett * @param interface The identifier of the packet interface to configure and
215210284Sjmallett *                  use as a SPI interface.
216210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
217210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
218210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
219210284Sjmallett *                  active or the Rx data path is active, but not both).
220210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
221210284Sjmallett */
222210284Sjmallettint cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
223210284Sjmallett{
224210284Sjmallett    cvmx_spxx_dbg_deskew_ctl_t spxx_dbg_deskew_ctl;
225210284Sjmallett    cvmx_spxx_clk_ctl_t spxx_clk_ctl;
226210284Sjmallett    cvmx_spxx_bist_stat_t spxx_bist_stat;
227210284Sjmallett    cvmx_spxx_int_msk_t spxx_int_msk;
228210284Sjmallett    cvmx_stxx_int_msk_t stxx_int_msk;
229210284Sjmallett    cvmx_spxx_trn4_ctl_t spxx_trn4_ctl;
230210284Sjmallett    int index;
231215990Sjmallett    uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
232210284Sjmallett
233210284Sjmallett    /* Disable SPI error events while we run BIST */
234210284Sjmallett    spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
235210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
236210284Sjmallett    stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
237210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
238210284Sjmallett
239210284Sjmallett    /* Run BIST in the SPI interface */
240210284Sjmallett    cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
241210284Sjmallett    cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
242210284Sjmallett    spxx_clk_ctl.u64 = 0;
243210284Sjmallett    spxx_clk_ctl.s.runbist = 1;
244210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
245210284Sjmallett    cvmx_wait (10 * MS);
246210284Sjmallett    spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
247210284Sjmallett    if (spxx_bist_stat.s.stat0)
248210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on receive datapath FIFO\n", interface);
249210284Sjmallett    if (spxx_bist_stat.s.stat1)
250210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", interface);
251210284Sjmallett    if (spxx_bist_stat.s.stat2)
252210284Sjmallett        cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", interface);
253210284Sjmallett
254210284Sjmallett    /* Clear the calendar table after BIST to fix parity errors */
255210284Sjmallett    for (index=0; index<32; index++)
256210284Sjmallett    {
257210284Sjmallett        cvmx_srxx_spi4_calx_t srxx_spi4_calx;
258210284Sjmallett        cvmx_stxx_spi4_calx_t stxx_spi4_calx;
259210284Sjmallett
260210284Sjmallett        srxx_spi4_calx.u64 = 0;
261210284Sjmallett        srxx_spi4_calx.s.oddpar = 1;
262210284Sjmallett        cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
263210284Sjmallett
264210284Sjmallett        stxx_spi4_calx.u64 = 0;
265210284Sjmallett        stxx_spi4_calx.s.oddpar = 1;
266210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
267210284Sjmallett    }
268210284Sjmallett
269210284Sjmallett    /* Re enable reporting of error interrupts */
270210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_REG(interface), cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
271210284Sjmallett    cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
272210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_REG(interface), cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
273210284Sjmallett    cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
274210284Sjmallett
275210284Sjmallett    // Setup the CLKDLY right in the middle
276210284Sjmallett    spxx_clk_ctl.u64 = 0;
277210284Sjmallett    spxx_clk_ctl.s.seetrn = 0;
278210284Sjmallett    spxx_clk_ctl.s.clkdly = 0x10;
279210284Sjmallett    spxx_clk_ctl.s.runbist = 0;
280210284Sjmallett    spxx_clk_ctl.s.statdrv = 0;
281215990Sjmallett    spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
282210284Sjmallett    spxx_clk_ctl.s.sndtrn = 0;
283210284Sjmallett    spxx_clk_ctl.s.drptrn = 0;
284210284Sjmallett    spxx_clk_ctl.s.rcvtrn = 0;
285210284Sjmallett    spxx_clk_ctl.s.srxdlck = 0;
286210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
287210284Sjmallett    cvmx_wait (100 * MS);
288210284Sjmallett
289210284Sjmallett    // Reset SRX0 DLL
290210284Sjmallett    spxx_clk_ctl.s.srxdlck = 1;
291210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
292210284Sjmallett
293210284Sjmallett    // Waiting for Inf0 Spi4 RX DLL to lock
294210284Sjmallett    cvmx_wait (100 * MS);
295210284Sjmallett
296210284Sjmallett    // Enable dynamic alignment
297210284Sjmallett    spxx_trn4_ctl.s.trntest = 0;
298210284Sjmallett    spxx_trn4_ctl.s.jitter = 1;
299210284Sjmallett    spxx_trn4_ctl.s.clr_boot = 1;
300210284Sjmallett    spxx_trn4_ctl.s.set_boot = 0;
301210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN58XX))
302210284Sjmallett        spxx_trn4_ctl.s.maxdist = 3;
303210284Sjmallett    else
304210284Sjmallett        spxx_trn4_ctl.s.maxdist = 8;
305210284Sjmallett    spxx_trn4_ctl.s.macro_en = 1;
306210284Sjmallett    spxx_trn4_ctl.s.mux_en = 1;
307210284Sjmallett    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
308210284Sjmallett
309210284Sjmallett    spxx_dbg_deskew_ctl.u64 = 0;
310210284Sjmallett    cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), spxx_dbg_deskew_ctl.u64);
311210284Sjmallett
312210284Sjmallett    return 0;
313210284Sjmallett}
314210284Sjmallett
315210284Sjmallett/**
316210284Sjmallett * Callback to setup calendar and miscellaneous settings before clock detection
317210284Sjmallett *
318210284Sjmallett * @param interface The identifier of the packet interface to configure and
319210284Sjmallett *                  use as a SPI interface.
320210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
321210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
322210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
323210284Sjmallett *                  active or the Rx data path is active, but not both).
324210284Sjmallett * @param num_ports Number of ports to configure on SPI
325210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
326210284Sjmallett */
327210284Sjmallettint cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, int num_ports)
328210284Sjmallett{
329210284Sjmallett    int port;
330210284Sjmallett    int index;
331210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX)
332210284Sjmallett    {
333210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
334210284Sjmallett        cvmx_srxx_spi4_stat_t srxx_spi4_stat;
335210284Sjmallett
336210284Sjmallett        // SRX0 number of Ports
337210284Sjmallett        srxx_com_ctl.u64 = 0;
338210284Sjmallett        srxx_com_ctl.s.prts = num_ports - 1;
339210284Sjmallett        srxx_com_ctl.s.st_en = 0;
340210284Sjmallett        srxx_com_ctl.s.inf_en = 0;
341210284Sjmallett        cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
342210284Sjmallett
343210284Sjmallett        // SRX0 Calendar Table. This round robbins through all ports
344210284Sjmallett        port = 0;
345210284Sjmallett        index = 0;
346210284Sjmallett        while (port < num_ports)
347210284Sjmallett        {
348210284Sjmallett            cvmx_srxx_spi4_calx_t srxx_spi4_calx;
349210284Sjmallett            srxx_spi4_calx.u64 = 0;
350210284Sjmallett            srxx_spi4_calx.s.prt0 = port++;
351210284Sjmallett            srxx_spi4_calx.s.prt1 = port++;
352210284Sjmallett            srxx_spi4_calx.s.prt2 = port++;
353210284Sjmallett            srxx_spi4_calx.s.prt3 = port++;
354210284Sjmallett            srxx_spi4_calx.s.oddpar = ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
355210284Sjmallett            cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
356210284Sjmallett            index++;
357210284Sjmallett        }
358210284Sjmallett        srxx_spi4_stat.u64 = 0;
359210284Sjmallett        srxx_spi4_stat.s.len = num_ports;
360210284Sjmallett        srxx_spi4_stat.s.m = 1;
361210284Sjmallett        cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), srxx_spi4_stat.u64);
362210284Sjmallett    }
363210284Sjmallett
364210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX)
365210284Sjmallett    {
366210284Sjmallett        cvmx_stxx_arb_ctl_t stxx_arb_ctl;
367210284Sjmallett        cvmx_gmxx_tx_spi_max_t gmxx_tx_spi_max;
368210284Sjmallett        cvmx_gmxx_tx_spi_thresh_t gmxx_tx_spi_thresh;
369210284Sjmallett        cvmx_gmxx_tx_spi_ctl_t gmxx_tx_spi_ctl;
370210284Sjmallett        cvmx_stxx_spi4_stat_t stxx_spi4_stat;
371210284Sjmallett        cvmx_stxx_spi4_dat_t stxx_spi4_dat;
372210284Sjmallett
373210284Sjmallett        // STX0 Config
374210284Sjmallett        stxx_arb_ctl.u64 = 0;
375210284Sjmallett        stxx_arb_ctl.s.igntpa = 0;
376210284Sjmallett        stxx_arb_ctl.s.mintrn = 0;
377210284Sjmallett        cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
378210284Sjmallett
379210284Sjmallett        gmxx_tx_spi_max.u64 = 0;
380210284Sjmallett        gmxx_tx_spi_max.s.max1 = 8;
381210284Sjmallett        gmxx_tx_spi_max.s.max2 = 4;
382210284Sjmallett        gmxx_tx_spi_max.s.slice = 0;
383210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), gmxx_tx_spi_max.u64);
384210284Sjmallett
385210284Sjmallett        gmxx_tx_spi_thresh.u64 = 0;
386210284Sjmallett        gmxx_tx_spi_thresh.s.thresh = 4;
387210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), gmxx_tx_spi_thresh.u64);
388210284Sjmallett
389210284Sjmallett        gmxx_tx_spi_ctl.u64 = 0;
390210284Sjmallett        gmxx_tx_spi_ctl.s.tpa_clr = 0;
391210284Sjmallett        gmxx_tx_spi_ctl.s.cont_pkt = 0;
392210284Sjmallett        cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), gmxx_tx_spi_ctl.u64);
393210284Sjmallett
394210284Sjmallett        // STX0 Training Control
395210284Sjmallett        stxx_spi4_dat.u64 = 0;
396210284Sjmallett        stxx_spi4_dat.s.alpha = 32;    /*Minimum needed by dynamic alignment*/
397210284Sjmallett        stxx_spi4_dat.s.max_t = 0xFFFF;  /*Minimum interval is 0x20*/
398210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64);
399210284Sjmallett
400210284Sjmallett        // STX0 Calendar Table. This round robbins through all ports
401210284Sjmallett        port = 0;
402210284Sjmallett        index = 0;
403210284Sjmallett        while (port < num_ports)
404210284Sjmallett        {
405210284Sjmallett            cvmx_stxx_spi4_calx_t stxx_spi4_calx;
406210284Sjmallett            stxx_spi4_calx.u64 = 0;
407210284Sjmallett            stxx_spi4_calx.s.prt0 = port++;
408210284Sjmallett            stxx_spi4_calx.s.prt1 = port++;
409210284Sjmallett            stxx_spi4_calx.s.prt2 = port++;
410210284Sjmallett            stxx_spi4_calx.s.prt3 = port++;
411210284Sjmallett            stxx_spi4_calx.s.oddpar = ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
412210284Sjmallett            cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
413210284Sjmallett            index++;
414210284Sjmallett        }
415210284Sjmallett        stxx_spi4_stat.u64 = 0;
416210284Sjmallett        stxx_spi4_stat.s.len = num_ports;
417210284Sjmallett        stxx_spi4_stat.s.m = 1;
418210284Sjmallett        cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), stxx_spi4_stat.u64);
419210284Sjmallett    }
420210284Sjmallett
421210284Sjmallett    return 0;
422210284Sjmallett}
423210284Sjmallett
424210284Sjmallett/**
425210284Sjmallett * Callback to perform clock detection
426210284Sjmallett *
427210284Sjmallett * @param interface The identifier of the packet interface to configure and
428210284Sjmallett *                  use as a SPI interface.
429210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
430210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
431210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
432210284Sjmallett *                  active or the Rx data path is active, but not both).
433210284Sjmallett * @param timeout   Timeout to wait for clock synchronization in seconds
434210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
435210284Sjmallett */
436210284Sjmallettint cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
437210284Sjmallett{
438210284Sjmallett    int                          clock_transitions;
439210284Sjmallett    cvmx_spxx_clk_stat_t         stat;
440210284Sjmallett    uint64_t                     timeout_time;
441215990Sjmallett    uint64_t                     MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
442210284Sjmallett
443210284Sjmallett    /* Regardless of operating mode, both Tx and Rx clocks must be present
444210284Sjmallett        for the SPI interface to operate. */
445210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting to see TsClk...\n", interface);
446210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
447210284Sjmallett    /* Require 100 clock transitions in order to avoid any noise in the
448210284Sjmallett        beginning  */
449210284Sjmallett    clock_transitions = 100;
450210284Sjmallett    do
451210284Sjmallett    {
452210284Sjmallett        stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
453210284Sjmallett        if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions)
454210284Sjmallett        {
455210284Sjmallett            /* We've seen a clock transition, so decrement the number we still
456210284Sjmallett                need */
457210284Sjmallett            clock_transitions--;
458210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
459210284Sjmallett            stat.s.s4clk0 = 0;
460210284Sjmallett            stat.s.s4clk1 = 0;
461210284Sjmallett        }
462210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
463210284Sjmallett        {
464210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
465210284Sjmallett            return -1;
466210284Sjmallett        }
467210284Sjmallett    } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
468210284Sjmallett
469210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting to see RsClk...\n", interface);
470210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
471210284Sjmallett    /* Require 100 clock transitions in order to avoid any noise in the
472210284Sjmallett        beginning  */
473210284Sjmallett    clock_transitions = 100;
474210284Sjmallett    do
475210284Sjmallett    {
476210284Sjmallett        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
477210284Sjmallett        if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions)
478210284Sjmallett        {
479210284Sjmallett            /* We've seen a clock transition, so decrement the number we still
480210284Sjmallett                need */
481210284Sjmallett            clock_transitions--;
482210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
483210284Sjmallett            stat.s.d4clk0 = 0;
484210284Sjmallett            stat.s.d4clk1 = 0;
485210284Sjmallett        }
486210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
487210284Sjmallett        {
488210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
489210284Sjmallett            return -1;
490210284Sjmallett        }
491210284Sjmallett    } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
492210284Sjmallett
493210284Sjmallett    return 0;
494210284Sjmallett}
495210284Sjmallett
496210284Sjmallett/**
497210284Sjmallett * Callback to perform link training
498210284Sjmallett *
499210284Sjmallett * @param interface The identifier of the packet interface to configure and
500210284Sjmallett *                  use as a SPI interface.
501210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
502210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
503210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
504210284Sjmallett *                  active or the Rx data path is active, but not both).
505210284Sjmallett * @param timeout   Timeout to wait for link to be trained (in seconds)
506210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
507210284Sjmallett */
508210284Sjmallettint cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
509210284Sjmallett{
510210284Sjmallett    cvmx_spxx_trn4_ctl_t         spxx_trn4_ctl;
511210284Sjmallett    cvmx_spxx_clk_stat_t         stat;
512215990Sjmallett    uint64_t                     MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
513210284Sjmallett    uint64_t                     timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
514210284Sjmallett    int                          rx_training_needed;
515210284Sjmallett
516210284Sjmallett    // SRX0 & STX0 Inf0 Links are configured - begin training
517210284Sjmallett    cvmx_spxx_clk_ctl_t spxx_clk_ctl;
518210284Sjmallett    spxx_clk_ctl.u64 = 0;
519210284Sjmallett    spxx_clk_ctl.s.seetrn = 0;
520210284Sjmallett    spxx_clk_ctl.s.clkdly = 0x10;
521210284Sjmallett    spxx_clk_ctl.s.runbist = 0;
522210284Sjmallett    spxx_clk_ctl.s.statdrv = 0;
523215990Sjmallett    spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
524210284Sjmallett    spxx_clk_ctl.s.sndtrn = 1;
525210284Sjmallett    spxx_clk_ctl.s.drptrn = 1;
526210284Sjmallett    spxx_clk_ctl.s.rcvtrn = 1;
527210284Sjmallett    spxx_clk_ctl.s.srxdlck = 1;
528210284Sjmallett    cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
529210284Sjmallett    cvmx_wait (1000 * MS);
530210284Sjmallett
531210284Sjmallett    // SRX0 clear the boot bit
532210284Sjmallett    spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
533210284Sjmallett    spxx_trn4_ctl.s.clr_boot = 1;
534210284Sjmallett    cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
535210284Sjmallett
536210284Sjmallett    // Wait for the training sequence to complete
537210284Sjmallett    cvmx_dprintf ("SPI%d: Waiting for training\n", interface);
538210284Sjmallett    cvmx_wait (1000 * MS);
539212844Sjmallett#if !defined(OCTEON_VENDOR_LANNER)
540210284Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;  /* Wait a really long time here */
541212844Sjmallett#else
542212844Sjmallett    timeout_time = cvmx_get_cycle() + 1000ull * MS * 10;
543212844Sjmallett#endif
544210284Sjmallett    /* The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
545210284Sjmallett        We'll be pessimistic and wait for a lot more */
546210284Sjmallett    rx_training_needed = 500;
547210284Sjmallett    do {
548210284Sjmallett        stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
549210284Sjmallett        if (stat.s.srxtrn && rx_training_needed)
550210284Sjmallett        {
551210284Sjmallett            rx_training_needed--;
552210284Sjmallett            cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
553210284Sjmallett            stat.s.srxtrn = 0;
554210284Sjmallett        }
555210284Sjmallett        if (cvmx_get_cycle() > timeout_time)
556210284Sjmallett        {
557210284Sjmallett            cvmx_dprintf ("SPI%d: Timeout\n", interface);
558210284Sjmallett            return -1;
559210284Sjmallett        }
560210284Sjmallett    } while (stat.s.srxtrn == 0);
561210284Sjmallett
562210284Sjmallett    return 0;
563210284Sjmallett}
564210284Sjmallett
565210284Sjmallett/**
566210284Sjmallett * Callback to perform calendar data synchronization
567210284Sjmallett *
568210284Sjmallett * @param interface The identifier of the packet interface to configure and
569210284Sjmallett *                  use as a SPI interface.
570210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
571210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
572210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
573210284Sjmallett *                  active or the Rx data path is active, but not both).
574210284Sjmallett * @param timeout   Timeout to wait for calendar data in seconds
575210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
576210284Sjmallett */
577210284Sjmallettint cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
578210284Sjmallett{
579215990Sjmallett    uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000;
580210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
581210284Sjmallett        // SRX0 interface should be good, send calendar data
582210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
583210284Sjmallett        cvmx_dprintf ("SPI%d: Rx is synchronized, start sending calendar data\n", interface);
584210284Sjmallett        srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
585210284Sjmallett        srxx_com_ctl.s.inf_en = 1;
586210284Sjmallett        srxx_com_ctl.s.st_en  = 1;
587210284Sjmallett        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
588210284Sjmallett    }
589210284Sjmallett
590210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
591210284Sjmallett        // STX0 has achieved sync
592210284Sjmallett        // The corespondant board should be sending calendar data
593210284Sjmallett        // Enable the STX0 STAT receiver.
594210284Sjmallett        cvmx_spxx_clk_stat_t stat;
595210284Sjmallett        uint64_t timeout_time;
596210284Sjmallett        cvmx_stxx_com_ctl_t stxx_com_ctl;
597210284Sjmallett        stxx_com_ctl.u64 = 0;
598210284Sjmallett        stxx_com_ctl.s.st_en = 1;
599210284Sjmallett        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
600210284Sjmallett
601210284Sjmallett        // Waiting for calendar sync on STX0 STAT
602210284Sjmallett        cvmx_dprintf ("SPI%d: Waiting to sync on STX[%d] STAT\n", interface, interface);
603210284Sjmallett        timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
604210284Sjmallett        // SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10)
605210284Sjmallett        do {
606210284Sjmallett            stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT (interface));
607210284Sjmallett            if (cvmx_get_cycle() > timeout_time)
608210284Sjmallett            {
609210284Sjmallett                cvmx_dprintf ("SPI%d: Timeout\n", interface);
610210284Sjmallett                return -1;
611210284Sjmallett            }
612210284Sjmallett        } while (stat.s.stxcal == 0);
613210284Sjmallett    }
614210284Sjmallett
615210284Sjmallett    return 0;
616210284Sjmallett}
617210284Sjmallett
618210284Sjmallett/**
619210284Sjmallett * Callback to handle interface up
620210284Sjmallett *
621210284Sjmallett * @param interface The identifier of the packet interface to configure and
622210284Sjmallett *                  use as a SPI interface.
623210284Sjmallett * @param mode      The operating mode for the SPI interface. The interface
624210284Sjmallett *                  can operate as a full duplex (both Tx and Rx data paths
625210284Sjmallett *                  active) or as a halfplex (either the Tx data path is
626210284Sjmallett *                  active or the Rx data path is active, but not both).
627210284Sjmallett * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
628210284Sjmallett */
629210284Sjmallettint cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
630210284Sjmallett{
631210284Sjmallett    cvmx_gmxx_rxx_frm_min_t gmxx_rxx_frm_min;
632210284Sjmallett    cvmx_gmxx_rxx_frm_max_t gmxx_rxx_frm_max;
633210284Sjmallett    cvmx_gmxx_rxx_jabber_t gmxx_rxx_jabber;
634210284Sjmallett
635210284Sjmallett    if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
636210284Sjmallett        cvmx_srxx_com_ctl_t srxx_com_ctl;
637210284Sjmallett        srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
638210284Sjmallett        srxx_com_ctl.s.inf_en = 1;
639210284Sjmallett        cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
640210284Sjmallett        cvmx_dprintf ("SPI%d: Rx is now up\n", interface);
641210284Sjmallett    }
642210284Sjmallett
643210284Sjmallett    if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
644210284Sjmallett        cvmx_stxx_com_ctl_t stxx_com_ctl;
645210284Sjmallett        stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
646210284Sjmallett        stxx_com_ctl.s.inf_en = 1;
647210284Sjmallett        cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
648210284Sjmallett        cvmx_dprintf ("SPI%d: Tx is now up\n", interface);
649210284Sjmallett    }
650210284Sjmallett
651210284Sjmallett    gmxx_rxx_frm_min.u64 = 0;
652210284Sjmallett    gmxx_rxx_frm_min.s.len = 64;
653243260Sjmallett#ifdef OCTEON_VENDOR_RADISYS
654243260Sjmallett    /*
655243260Sjmallett     * Incoming packets on the RSYS4GBE have the FCS stripped.
656243260Sjmallett     */
657243260Sjmallett    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CUST_RADISYS_RSYS4GBE)
658243260Sjmallett	    gmxx_rxx_frm_min.s.len -= 4;
659243260Sjmallett#endif
660210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0,interface), gmxx_rxx_frm_min.u64);
661210284Sjmallett    gmxx_rxx_frm_max.u64 = 0;
662210284Sjmallett    gmxx_rxx_frm_max.s.len = 64*1024 - 4;
663210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0,interface), gmxx_rxx_frm_max.u64);
664210284Sjmallett    gmxx_rxx_jabber.u64 = 0;
665210284Sjmallett    gmxx_rxx_jabber.s.cnt = 64*1024 - 4;
666210284Sjmallett    cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0,interface), gmxx_rxx_jabber.u64);
667210284Sjmallett
668210284Sjmallett    return 0;
669210284Sjmallett}
670210284Sjmallett
671