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