freebsd-skq/sys/contrib/octeon-sdk/cvmx-spi.c
Juli Mallett cea2b8b915 Update the port of FreeBSD to Cavium Octeon to use the Cavium Simple Executive
library:
o) Increase inline unit / large function growth limits for MIPS to accommodate
   the needs of the Simple Executive, which uses a shocking amount of inlining.
o) Remove TARGET_OCTEON and use CPU_CNMIPS to do things required by cnMIPS and
   the Octeon SoC.
o) Add OCTEON_VENDOR_LANNER to use Lanner's allocation of vendor-specific
   board numbers, specifically to support the MR320.
o) Add OCTEON_BOARD_CAPK_0100ND to hard-wire configuration for the CAPK-0100nd,
   which improperly uses an evaluation board's board number and breaks board
   detection at runtime.  This board is sold by Portwell as the CAM-0100.
o) Add support for the RTC available on some Octeon boards.
o) Add support for the Octeon PCI bus.  Note that rman_[sg]et_virtual for IO
   ports can not work unless building for n64.
o) Clean up the CompactFlash driver to use Simple Executive macros and
   structures where possible (it would be advisable to use the Simple Executive
   API to set the PIO mode, too, but that is not done presently.)  Also use
   structures from FreeBSD's ATA layer rather than structures copied from
   Linux.
o) Print available Octeon SoC features on boot.
o) Add support for the Octeon timecounter.
o) Use the Simple Executive's routines rather than local copies for doing reads
   and writes to 64-bit addresses and use its macros for various device
   addresses rather than using local copies.
o) Rename octeon_board_real to octeon_is_simulation to reduce differences with
   Cavium-provided code originally written for Linux.  Also make it use the
   same simplified test that the Simple Executive and Linux both use rather
   than our complex one.
o) Add support for the Octeon CIU, which is the main interrupt unit, as a bus
   to use normal interrupt allocation and setup routines.
o) Use the Simple Executive's bootmem facility to allocate physical memory for
   the kernel, rather than assuming we know which addresses we can steal.
   NB: This may reduce the amount of RAM the kernel reports you as having if
       you are leaving large temporary allocations made by U-Boot allocated
       when starting FreeBSD.
o) Add a port of the Cavium-provided Ethernet driver for Linux.  This changes
   Ethernet interface naming from rgmxN to octeN.  The new driver has vast
   improvements over the old one, both in performance and functionality, but
   does still have some features which have not been ported entirely and there
   may be unimplemented code that can be hit in everyday use.  I will make
   every effort to correct those as they are reported.
o) Support loading the kernel on non-contiguous cores.
o) Add very conservative support for harvesting randomness from the Octeon
   random number device.
o) Turn SMP on by default.
o) Clean up the style of the Octeon kernel configurations a little and make
   them compile with -march=octeon.
o) Add support for the Lanner MR320 and the CAPK-0100nd to the Simple
   Executive.
o) Modify the Simple Executive to build on FreeBSD and to build without
   executive-config.h or cvmx-config.h.  In the future we may want to
   revert part of these changes and supply executive-config.h and
   cvmx-config.h and access to the options contained in those files via
   kernel configuration files.
o) Modify the Simple Executive USB routines to support getting and setting
   of the USB PID.
2010-07-20 19:25:11 +00:00

639 lines
24 KiB
C

/***********************license start***************
* Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
* reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Cavium Networks nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
* AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
* OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
* RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
* POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT
* OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
*
*
* For any questions regarding licensing please contact marketing@caviumnetworks.com
*
***********************license end**************************************/
/**
* @file
*
* Support library for the SPI
*
* <hr>$Revision: 41586 $<hr>
*/
#include "cvmx.h"
#include "cvmx-mio.h"
#include "cvmx-pko.h"
#include "cvmx-spi.h"
#include "cvmx-sysinfo.h"
#define INVOKE_CB(function_p, args...) \
do { \
if (function_p) { \
res = function_p(args); \
if (res) \
return res; \
} \
} while (0)
#if CVMX_ENABLE_DEBUG_PRINTS
static const char *modes[] = {"UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex"};
#endif
/* Default callbacks, can be overridden
* using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks
*/
static cvmx_spi_callbacks_t cvmx_spi_callbacks = {
.reset_cb = cvmx_spi_reset_cb,
.calendar_setup_cb = cvmx_spi_calendar_setup_cb,
.clock_detect_cb = cvmx_spi_clock_detect_cb,
.training_cb = cvmx_spi_training_cb,
.calendar_sync_cb = cvmx_spi_calendar_sync_cb,
.interface_up_cb = cvmx_spi_interface_up_cb
};
/**
* Get current SPI4 initialization callbacks
*
* @param callbacks Pointer to the callbacks structure.to fill
*
* @return Pointer to cvmx_spi_callbacks_t structure.
*/
void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t * callbacks)
{
memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks));
}
/**
* Set new SPI4 initialization callbacks
*
* @param new_callbacks Pointer to an updated callbacks structure.
*/
void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t * new_callbacks)
{
memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks));
}
/**
* Initialize and start the SPI interface.
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param timeout Timeout to wait for clock synchronization in seconds
* @param num_ports Number of SPI ports to configure
*
* @return Zero on success, negative of failure.
*/
int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, int num_ports)
{
int res = -1;
if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
return res;
// Callback to perform SPI4 reset
INVOKE_CB( cvmx_spi_callbacks.reset_cb, interface, mode);
// Callback to perform calendar setup
INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode, num_ports);
// Callback to perform clock detection
INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
// Callback to perform SPI4 link training
INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
// Callback to perform calendar sync
INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout);
// Callback to handle interface coming up
INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
return res;
}
/**
* This routine restarts the SPI interface after it has lost synchronization
* with its correspondent system.
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param timeout Timeout to wait for clock synchronization in seconds
* @return Zero on success, negative of failure.
*/
int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout)
{
int res = -1;
if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)))
return res;
cvmx_dprintf ("SPI%d: Restart %s\n", interface, modes[mode]);
// Callback to perform SPI4 reset
INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface,mode);
// NOTE: Calendar setup is not performed during restart
// Refer to cvmx_spi_start_interface() for the full sequence
// Callback to perform clock detection
INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout);
// Callback to perform SPI4 link training
INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout);
// Callback to perform calendar sync
INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout);
// Callback to handle interface coming up
INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode);
return res;
}
/**
* Callback to perform SPI4 reset
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
{
cvmx_spxx_dbg_deskew_ctl_t spxx_dbg_deskew_ctl;
cvmx_spxx_clk_ctl_t spxx_clk_ctl;
cvmx_spxx_bist_stat_t spxx_bist_stat;
cvmx_spxx_int_msk_t spxx_int_msk;
cvmx_stxx_int_msk_t stxx_int_msk;
cvmx_spxx_trn4_ctl_t spxx_trn4_ctl;
int index;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
/* Disable SPI error events while we run BIST */
spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
/* Run BIST in the SPI interface */
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.runbist = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait (10 * MS);
spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
if (spxx_bist_stat.s.stat0)
cvmx_dprintf("ERROR SPI%d: BIST failed on receive datapath FIFO\n", interface);
if (spxx_bist_stat.s.stat1)
cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", interface);
if (spxx_bist_stat.s.stat2)
cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", interface);
/* Clear the calendar table after BIST to fix parity errors */
for (index=0; index<32; index++)
{
cvmx_srxx_spi4_calx_t srxx_spi4_calx;
cvmx_stxx_spi4_calx_t stxx_spi4_calx;
srxx_spi4_calx.u64 = 0;
srxx_spi4_calx.s.oddpar = 1;
cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
stxx_spi4_calx.u64 = 0;
stxx_spi4_calx.s.oddpar = 1;
cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
}
/* Re enable reporting of error interrupts */
cvmx_write_csr(CVMX_SPXX_INT_REG(interface), cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
cvmx_write_csr(CVMX_STXX_INT_REG(interface), cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
// Setup the CLKDLY right in the middle
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.seetrn = 0;
spxx_clk_ctl.s.clkdly = 0x10;
spxx_clk_ctl.s.runbist = 0;
spxx_clk_ctl.s.statdrv = 0;
spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
spxx_clk_ctl.s.sndtrn = 0;
spxx_clk_ctl.s.drptrn = 0;
spxx_clk_ctl.s.rcvtrn = 0;
spxx_clk_ctl.s.srxdlck = 0;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait (100 * MS);
// Reset SRX0 DLL
spxx_clk_ctl.s.srxdlck = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
// Waiting for Inf0 Spi4 RX DLL to lock
cvmx_wait (100 * MS);
// Enable dynamic alignment
spxx_trn4_ctl.s.trntest = 0;
spxx_trn4_ctl.s.jitter = 1;
spxx_trn4_ctl.s.clr_boot = 1;
spxx_trn4_ctl.s.set_boot = 0;
if (OCTEON_IS_MODEL(OCTEON_CN58XX))
spxx_trn4_ctl.s.maxdist = 3;
else
spxx_trn4_ctl.s.maxdist = 8;
spxx_trn4_ctl.s.macro_en = 1;
spxx_trn4_ctl.s.mux_en = 1;
cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
spxx_dbg_deskew_ctl.u64 = 0;
cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), spxx_dbg_deskew_ctl.u64);
return 0;
}
/**
* Callback to setup calendar and miscellaneous settings before clock detection
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param num_ports Number of ports to configure on SPI
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, int num_ports)
{
int port;
int index;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX)
{
cvmx_srxx_com_ctl_t srxx_com_ctl;
cvmx_srxx_spi4_stat_t srxx_spi4_stat;
// SRX0 number of Ports
srxx_com_ctl.u64 = 0;
srxx_com_ctl.s.prts = num_ports - 1;
srxx_com_ctl.s.st_en = 0;
srxx_com_ctl.s.inf_en = 0;
cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
// SRX0 Calendar Table. This round robbins through all ports
port = 0;
index = 0;
while (port < num_ports)
{
cvmx_srxx_spi4_calx_t srxx_spi4_calx;
srxx_spi4_calx.u64 = 0;
srxx_spi4_calx.s.prt0 = port++;
srxx_spi4_calx.s.prt1 = port++;
srxx_spi4_calx.s.prt2 = port++;
srxx_spi4_calx.s.prt3 = port++;
srxx_spi4_calx.s.oddpar = ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64);
index++;
}
srxx_spi4_stat.u64 = 0;
srxx_spi4_stat.s.len = num_ports;
srxx_spi4_stat.s.m = 1;
cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), srxx_spi4_stat.u64);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX)
{
cvmx_stxx_arb_ctl_t stxx_arb_ctl;
cvmx_gmxx_tx_spi_max_t gmxx_tx_spi_max;
cvmx_gmxx_tx_spi_thresh_t gmxx_tx_spi_thresh;
cvmx_gmxx_tx_spi_ctl_t gmxx_tx_spi_ctl;
cvmx_stxx_spi4_stat_t stxx_spi4_stat;
cvmx_stxx_spi4_dat_t stxx_spi4_dat;
// STX0 Config
stxx_arb_ctl.u64 = 0;
stxx_arb_ctl.s.igntpa = 0;
stxx_arb_ctl.s.mintrn = 0;
cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
gmxx_tx_spi_max.u64 = 0;
gmxx_tx_spi_max.s.max1 = 8;
gmxx_tx_spi_max.s.max2 = 4;
gmxx_tx_spi_max.s.slice = 0;
cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), gmxx_tx_spi_max.u64);
gmxx_tx_spi_thresh.u64 = 0;
gmxx_tx_spi_thresh.s.thresh = 4;
cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), gmxx_tx_spi_thresh.u64);
gmxx_tx_spi_ctl.u64 = 0;
gmxx_tx_spi_ctl.s.tpa_clr = 0;
gmxx_tx_spi_ctl.s.cont_pkt = 0;
cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), gmxx_tx_spi_ctl.u64);
// STX0 Training Control
stxx_spi4_dat.u64 = 0;
stxx_spi4_dat.s.alpha = 32; /*Minimum needed by dynamic alignment*/
stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20*/
cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64);
// STX0 Calendar Table. This round robbins through all ports
port = 0;
index = 0;
while (port < num_ports)
{
cvmx_stxx_spi4_calx_t stxx_spi4_calx;
stxx_spi4_calx.u64 = 0;
stxx_spi4_calx.s.prt0 = port++;
stxx_spi4_calx.s.prt1 = port++;
stxx_spi4_calx.s.prt2 = port++;
stxx_spi4_calx.s.prt3 = port++;
stxx_spi4_calx.s.oddpar = ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64);
index++;
}
stxx_spi4_stat.u64 = 0;
stxx_spi4_stat.s.len = num_ports;
stxx_spi4_stat.s.m = 1;
cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), stxx_spi4_stat.u64);
}
return 0;
}
/**
* Callback to perform clock detection
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param timeout Timeout to wait for clock synchronization in seconds
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
int clock_transitions;
cvmx_spxx_clk_stat_t stat;
uint64_t timeout_time;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
/* Regardless of operating mode, both Tx and Rx clocks must be present
for the SPI interface to operate. */
cvmx_dprintf ("SPI%d: Waiting to see TsClk...\n", interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
/* Require 100 clock transitions in order to avoid any noise in the
beginning */
clock_transitions = 100;
do
{
stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions)
{
/* We've seen a clock transition, so decrement the number we still
need */
clock_transitions--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.s4clk0 = 0;
stat.s.s4clk1 = 0;
}
if (cvmx_get_cycle() > timeout_time)
{
cvmx_dprintf ("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
cvmx_dprintf ("SPI%d: Waiting to see RsClk...\n", interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
/* Require 100 clock transitions in order to avoid any noise in the
beginning */
clock_transitions = 100;
do
{
stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions)
{
/* We've seen a clock transition, so decrement the number we still
need */
clock_transitions--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.d4clk0 = 0;
stat.s.d4clk1 = 0;
}
if (cvmx_get_cycle() > timeout_time)
{
cvmx_dprintf ("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
return 0;
}
/**
* Callback to perform link training
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param timeout Timeout to wait for link to be trained (in seconds)
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
cvmx_spxx_trn4_ctl_t spxx_trn4_ctl;
cvmx_spxx_clk_stat_t stat;
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
int rx_training_needed;
// SRX0 & STX0 Inf0 Links are configured - begin training
cvmx_spxx_clk_ctl_t spxx_clk_ctl;
spxx_clk_ctl.u64 = 0;
spxx_clk_ctl.s.seetrn = 0;
spxx_clk_ctl.s.clkdly = 0x10;
spxx_clk_ctl.s.runbist = 0;
spxx_clk_ctl.s.statdrv = 0;
spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */
spxx_clk_ctl.s.sndtrn = 1;
spxx_clk_ctl.s.drptrn = 1;
spxx_clk_ctl.s.rcvtrn = 1;
spxx_clk_ctl.s.srxdlck = 1;
cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
cvmx_wait (1000 * MS);
// SRX0 clear the boot bit
spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
spxx_trn4_ctl.s.clr_boot = 1;
cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
// Wait for the training sequence to complete
cvmx_dprintf ("SPI%d: Waiting for training\n", interface);
cvmx_wait (1000 * MS);
timeout_time = cvmx_get_cycle() + 1000ull * MS * 600; /* Wait a really long time here */
/* The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
We'll be pessimistic and wait for a lot more */
rx_training_needed = 500;
do {
stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface));
if (stat.s.srxtrn && rx_training_needed)
{
rx_training_needed--;
cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
stat.s.srxtrn = 0;
}
if (cvmx_get_cycle() > timeout_time)
{
cvmx_dprintf ("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.srxtrn == 0);
return 0;
}
/**
* Callback to perform calendar data synchronization
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @param timeout Timeout to wait for calendar data in seconds
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
{
uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
// SRX0 interface should be good, send calendar data
cvmx_srxx_com_ctl_t srxx_com_ctl;
cvmx_dprintf ("SPI%d: Rx is synchronized, start sending calendar data\n", interface);
srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
srxx_com_ctl.s.inf_en = 1;
srxx_com_ctl.s.st_en = 1;
cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
// STX0 has achieved sync
// The corespondant board should be sending calendar data
// Enable the STX0 STAT receiver.
cvmx_spxx_clk_stat_t stat;
uint64_t timeout_time;
cvmx_stxx_com_ctl_t stxx_com_ctl;
stxx_com_ctl.u64 = 0;
stxx_com_ctl.s.st_en = 1;
cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
// Waiting for calendar sync on STX0 STAT
cvmx_dprintf ("SPI%d: Waiting to sync on STX[%d] STAT\n", interface, interface);
timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
// SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10)
do {
stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT (interface));
if (cvmx_get_cycle() > timeout_time)
{
cvmx_dprintf ("SPI%d: Timeout\n", interface);
return -1;
}
} while (stat.s.stxcal == 0);
}
return 0;
}
/**
* Callback to handle interface up
*
* @param interface The identifier of the packet interface to configure and
* use as a SPI interface.
* @param mode The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
* active or the Rx data path is active, but not both).
* @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort)
*/
int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
{
cvmx_gmxx_rxx_frm_min_t gmxx_rxx_frm_min;
cvmx_gmxx_rxx_frm_max_t gmxx_rxx_frm_max;
cvmx_gmxx_rxx_jabber_t gmxx_rxx_jabber;
if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
cvmx_srxx_com_ctl_t srxx_com_ctl;
srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
srxx_com_ctl.s.inf_en = 1;
cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
cvmx_dprintf ("SPI%d: Rx is now up\n", interface);
}
if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
cvmx_stxx_com_ctl_t stxx_com_ctl;
stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
stxx_com_ctl.s.inf_en = 1;
cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
cvmx_dprintf ("SPI%d: Tx is now up\n", interface);
}
gmxx_rxx_frm_min.u64 = 0;
gmxx_rxx_frm_min.s.len = 64;
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0,interface), gmxx_rxx_frm_min.u64);
gmxx_rxx_frm_max.u64 = 0;
gmxx_rxx_frm_max.s.len = 64*1024 - 4;
cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0,interface), gmxx_rxx_frm_max.u64);
gmxx_rxx_jabber.u64 = 0;
gmxx_rxx_jabber.s.cnt = 64*1024 - 4;
cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0,interface), gmxx_rxx_jabber.u64);
return 0;
}