cvmx-spi.c revision 215990
1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights 3 * reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 18 * * Neither the name of Cavium Networks nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written 21 * permission. 22 23 * 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 NETWORKS 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: 49448 $<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#if CVMX_ENABLE_DEBUG_PRINTS 186 cvmx_dprintf ("SPI%d: Restart %s\n", interface, modes[mode]); 187#endif 188 189 // Callback to perform SPI4 reset 190 INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface,mode); 191 192 // NOTE: Calendar setup is not performed during restart 193 // Refer to cvmx_spi_start_interface() for the full sequence 194 195 // Callback to perform clock detection 196 INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout); 197 198 // Callback to perform SPI4 link training 199 INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout); 200 201 // Callback to perform calendar sync 202 INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, timeout); 203 204 // Callback to handle interface coming up 205 INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode); 206 207 return res; 208} 209#ifdef CVMX_BUILD_FOR_LINUX_KERNEL 210EXPORT_SYMBOL(cvmx_spi_restart_interface); 211#endif 212 213/** 214 * Callback to perform SPI4 reset 215 * 216 * @param interface The identifier of the packet interface to configure and 217 * use as a SPI interface. 218 * @param mode The operating mode for the SPI interface. The interface 219 * can operate as a full duplex (both Tx and Rx data paths 220 * active) or as a halfplex (either the Tx data path is 221 * active or the Rx data path is active, but not both). 222 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 223 */ 224int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) 225{ 226 cvmx_spxx_dbg_deskew_ctl_t spxx_dbg_deskew_ctl; 227 cvmx_spxx_clk_ctl_t spxx_clk_ctl; 228 cvmx_spxx_bist_stat_t spxx_bist_stat; 229 cvmx_spxx_int_msk_t spxx_int_msk; 230 cvmx_stxx_int_msk_t stxx_int_msk; 231 cvmx_spxx_trn4_ctl_t spxx_trn4_ctl; 232 int index; 233 uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000; 234 235 /* Disable SPI error events while we run BIST */ 236 spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); 237 cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); 238 stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); 239 cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); 240 241 /* Run BIST in the SPI interface */ 242 cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0); 243 cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0); 244 spxx_clk_ctl.u64 = 0; 245 spxx_clk_ctl.s.runbist = 1; 246 cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); 247 cvmx_wait (10 * MS); 248 spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface)); 249 if (spxx_bist_stat.s.stat0) 250 cvmx_dprintf("ERROR SPI%d: BIST failed on receive datapath FIFO\n", interface); 251 if (spxx_bist_stat.s.stat1) 252 cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", interface); 253 if (spxx_bist_stat.s.stat2) 254 cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", interface); 255 256 /* Clear the calendar table after BIST to fix parity errors */ 257 for (index=0; index<32; index++) 258 { 259 cvmx_srxx_spi4_calx_t srxx_spi4_calx; 260 cvmx_stxx_spi4_calx_t stxx_spi4_calx; 261 262 srxx_spi4_calx.u64 = 0; 263 srxx_spi4_calx.s.oddpar = 1; 264 cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64); 265 266 stxx_spi4_calx.u64 = 0; 267 stxx_spi4_calx.s.oddpar = 1; 268 cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64); 269 } 270 271 /* Re enable reporting of error interrupts */ 272 cvmx_write_csr(CVMX_SPXX_INT_REG(interface), cvmx_read_csr(CVMX_SPXX_INT_REG(interface))); 273 cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); 274 cvmx_write_csr(CVMX_STXX_INT_REG(interface), cvmx_read_csr(CVMX_STXX_INT_REG(interface))); 275 cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); 276 277 // Setup the CLKDLY right in the middle 278 spxx_clk_ctl.u64 = 0; 279 spxx_clk_ctl.s.seetrn = 0; 280 spxx_clk_ctl.s.clkdly = 0x10; 281 spxx_clk_ctl.s.runbist = 0; 282 spxx_clk_ctl.s.statdrv = 0; 283 spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */ 284 spxx_clk_ctl.s.sndtrn = 0; 285 spxx_clk_ctl.s.drptrn = 0; 286 spxx_clk_ctl.s.rcvtrn = 0; 287 spxx_clk_ctl.s.srxdlck = 0; 288 cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); 289 cvmx_wait (100 * MS); 290 291 // Reset SRX0 DLL 292 spxx_clk_ctl.s.srxdlck = 1; 293 cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); 294 295 // Waiting for Inf0 Spi4 RX DLL to lock 296 cvmx_wait (100 * MS); 297 298 // Enable dynamic alignment 299 spxx_trn4_ctl.s.trntest = 0; 300 spxx_trn4_ctl.s.jitter = 1; 301 spxx_trn4_ctl.s.clr_boot = 1; 302 spxx_trn4_ctl.s.set_boot = 0; 303 if (OCTEON_IS_MODEL(OCTEON_CN58XX)) 304 spxx_trn4_ctl.s.maxdist = 3; 305 else 306 spxx_trn4_ctl.s.maxdist = 8; 307 spxx_trn4_ctl.s.macro_en = 1; 308 spxx_trn4_ctl.s.mux_en = 1; 309 cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); 310 311 spxx_dbg_deskew_ctl.u64 = 0; 312 cvmx_write_csr (CVMX_SPXX_DBG_DESKEW_CTL(interface), spxx_dbg_deskew_ctl.u64); 313 314 return 0; 315} 316 317/** 318 * Callback to setup calendar and miscellaneous settings before clock detection 319 * 320 * @param interface The identifier of the packet interface to configure and 321 * use as a SPI interface. 322 * @param mode The operating mode for the SPI interface. The interface 323 * can operate as a full duplex (both Tx and Rx data paths 324 * active) or as a halfplex (either the Tx data path is 325 * active or the Rx data path is active, but not both). 326 * @param num_ports Number of ports to configure on SPI 327 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 328 */ 329int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, int num_ports) 330{ 331 int port; 332 int index; 333 if (mode & CVMX_SPI_MODE_RX_HALFPLEX) 334 { 335 cvmx_srxx_com_ctl_t srxx_com_ctl; 336 cvmx_srxx_spi4_stat_t srxx_spi4_stat; 337 338 // SRX0 number of Ports 339 srxx_com_ctl.u64 = 0; 340 srxx_com_ctl.s.prts = num_ports - 1; 341 srxx_com_ctl.s.st_en = 0; 342 srxx_com_ctl.s.inf_en = 0; 343 cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); 344 345 // SRX0 Calendar Table. This round robbins through all ports 346 port = 0; 347 index = 0; 348 while (port < num_ports) 349 { 350 cvmx_srxx_spi4_calx_t srxx_spi4_calx; 351 srxx_spi4_calx.u64 = 0; 352 srxx_spi4_calx.s.prt0 = port++; 353 srxx_spi4_calx.s.prt1 = port++; 354 srxx_spi4_calx.s.prt2 = port++; 355 srxx_spi4_calx.s.prt3 = port++; 356 srxx_spi4_calx.s.oddpar = ~(cvmx_dpop(srxx_spi4_calx.u64) & 1); 357 cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), srxx_spi4_calx.u64); 358 index++; 359 } 360 srxx_spi4_stat.u64 = 0; 361 srxx_spi4_stat.s.len = num_ports; 362 srxx_spi4_stat.s.m = 1; 363 cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), srxx_spi4_stat.u64); 364 } 365 366 if (mode & CVMX_SPI_MODE_TX_HALFPLEX) 367 { 368 cvmx_stxx_arb_ctl_t stxx_arb_ctl; 369 cvmx_gmxx_tx_spi_max_t gmxx_tx_spi_max; 370 cvmx_gmxx_tx_spi_thresh_t gmxx_tx_spi_thresh; 371 cvmx_gmxx_tx_spi_ctl_t gmxx_tx_spi_ctl; 372 cvmx_stxx_spi4_stat_t stxx_spi4_stat; 373 cvmx_stxx_spi4_dat_t stxx_spi4_dat; 374 375 // STX0 Config 376 stxx_arb_ctl.u64 = 0; 377 stxx_arb_ctl.s.igntpa = 0; 378 stxx_arb_ctl.s.mintrn = 0; 379 cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64); 380 381 gmxx_tx_spi_max.u64 = 0; 382 gmxx_tx_spi_max.s.max1 = 8; 383 gmxx_tx_spi_max.s.max2 = 4; 384 gmxx_tx_spi_max.s.slice = 0; 385 cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), gmxx_tx_spi_max.u64); 386 387 gmxx_tx_spi_thresh.u64 = 0; 388 gmxx_tx_spi_thresh.s.thresh = 4; 389 cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), gmxx_tx_spi_thresh.u64); 390 391 gmxx_tx_spi_ctl.u64 = 0; 392 gmxx_tx_spi_ctl.s.tpa_clr = 0; 393 gmxx_tx_spi_ctl.s.cont_pkt = 0; 394 cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), gmxx_tx_spi_ctl.u64); 395 396 // STX0 Training Control 397 stxx_spi4_dat.u64 = 0; 398 stxx_spi4_dat.s.alpha = 32; /*Minimum needed by dynamic alignment*/ 399 stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20*/ 400 cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), stxx_spi4_dat.u64); 401 402 // STX0 Calendar Table. This round robbins through all ports 403 port = 0; 404 index = 0; 405 while (port < num_ports) 406 { 407 cvmx_stxx_spi4_calx_t stxx_spi4_calx; 408 stxx_spi4_calx.u64 = 0; 409 stxx_spi4_calx.s.prt0 = port++; 410 stxx_spi4_calx.s.prt1 = port++; 411 stxx_spi4_calx.s.prt2 = port++; 412 stxx_spi4_calx.s.prt3 = port++; 413 stxx_spi4_calx.s.oddpar = ~(cvmx_dpop(stxx_spi4_calx.u64) & 1); 414 cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), stxx_spi4_calx.u64); 415 index++; 416 } 417 stxx_spi4_stat.u64 = 0; 418 stxx_spi4_stat.s.len = num_ports; 419 stxx_spi4_stat.s.m = 1; 420 cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), stxx_spi4_stat.u64); 421 } 422 423 return 0; 424} 425 426/** 427 * Callback to perform clock detection 428 * 429 * @param interface The identifier of the packet interface to configure and 430 * use as a SPI interface. 431 * @param mode The operating mode for the SPI interface. The interface 432 * can operate as a full duplex (both Tx and Rx data paths 433 * active) or as a halfplex (either the Tx data path is 434 * active or the Rx data path is active, but not both). 435 * @param timeout Timeout to wait for clock synchronization in seconds 436 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 437 */ 438int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) 439{ 440 int clock_transitions; 441 cvmx_spxx_clk_stat_t stat; 442 uint64_t timeout_time; 443 uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000; 444 445 /* Regardless of operating mode, both Tx and Rx clocks must be present 446 for the SPI interface to operate. */ 447 cvmx_dprintf ("SPI%d: Waiting to see TsClk...\n", interface); 448 timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; 449 /* Require 100 clock transitions in order to avoid any noise in the 450 beginning */ 451 clock_transitions = 100; 452 do 453 { 454 stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); 455 if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) 456 { 457 /* We've seen a clock transition, so decrement the number we still 458 need */ 459 clock_transitions--; 460 cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); 461 stat.s.s4clk0 = 0; 462 stat.s.s4clk1 = 0; 463 } 464 if (cvmx_get_cycle() > timeout_time) 465 { 466 cvmx_dprintf ("SPI%d: Timeout\n", interface); 467 return -1; 468 } 469 } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0); 470 471 cvmx_dprintf ("SPI%d: Waiting to see RsClk...\n", interface); 472 timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; 473 /* Require 100 clock transitions in order to avoid any noise in the 474 beginning */ 475 clock_transitions = 100; 476 do 477 { 478 stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface)); 479 if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) 480 { 481 /* We've seen a clock transition, so decrement the number we still 482 need */ 483 clock_transitions--; 484 cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); 485 stat.s.d4clk0 = 0; 486 stat.s.d4clk1 = 0; 487 } 488 if (cvmx_get_cycle() > timeout_time) 489 { 490 cvmx_dprintf ("SPI%d: Timeout\n", interface); 491 return -1; 492 } 493 } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0); 494 495 return 0; 496} 497 498/** 499 * Callback to perform link training 500 * 501 * @param interface The identifier of the packet interface to configure and 502 * use as a SPI interface. 503 * @param mode The operating mode for the SPI interface. The interface 504 * can operate as a full duplex (both Tx and Rx data paths 505 * active) or as a halfplex (either the Tx data path is 506 * active or the Rx data path is active, but not both). 507 * @param timeout Timeout to wait for link to be trained (in seconds) 508 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 509 */ 510int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) 511{ 512 cvmx_spxx_trn4_ctl_t spxx_trn4_ctl; 513 cvmx_spxx_clk_stat_t stat; 514 uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000; 515 uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; 516 int rx_training_needed; 517 518 // SRX0 & STX0 Inf0 Links are configured - begin training 519 cvmx_spxx_clk_ctl_t spxx_clk_ctl; 520 spxx_clk_ctl.u64 = 0; 521 spxx_clk_ctl.s.seetrn = 0; 522 spxx_clk_ctl.s.clkdly = 0x10; 523 spxx_clk_ctl.s.runbist = 0; 524 spxx_clk_ctl.s.statdrv = 0; 525 spxx_clk_ctl.s.statrcv = 1; /* This should always be on the opposite edge as statdrv */ 526 spxx_clk_ctl.s.sndtrn = 1; 527 spxx_clk_ctl.s.drptrn = 1; 528 spxx_clk_ctl.s.rcvtrn = 1; 529 spxx_clk_ctl.s.srxdlck = 1; 530 cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); 531 cvmx_wait (1000 * MS); 532 533 // SRX0 clear the boot bit 534 spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface)); 535 spxx_trn4_ctl.s.clr_boot = 1; 536 cvmx_write_csr (CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); 537 538 // Wait for the training sequence to complete 539 cvmx_dprintf ("SPI%d: Waiting for training\n", interface); 540 cvmx_wait (1000 * MS); 541#if !defined(OCTEON_VENDOR_LANNER) 542 timeout_time = cvmx_get_cycle() + 1000ull * MS * 600; /* Wait a really long time here */ 543#else 544 timeout_time = cvmx_get_cycle() + 1000ull * MS * 10; 545#endif 546 /* The HRM says we must wait for 34 + 16 * MAXDIST training sequences. 547 We'll be pessimistic and wait for a lot more */ 548 rx_training_needed = 500; 549 do { 550 stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT(interface)); 551 if (stat.s.srxtrn && rx_training_needed) 552 { 553 rx_training_needed--; 554 cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); 555 stat.s.srxtrn = 0; 556 } 557 if (cvmx_get_cycle() > timeout_time) 558 { 559 cvmx_dprintf ("SPI%d: Timeout\n", interface); 560 return -1; 561 } 562 } while (stat.s.srxtrn == 0); 563 564 return 0; 565} 566 567/** 568 * Callback to perform calendar data synchronization 569 * 570 * @param interface The identifier of the packet interface to configure and 571 * use as a SPI interface. 572 * @param mode The operating mode for the SPI interface. The interface 573 * can operate as a full duplex (both Tx and Rx data paths 574 * active) or as a halfplex (either the Tx data path is 575 * active or the Rx data path is active, but not both). 576 * @param timeout Timeout to wait for calendar data in seconds 577 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 578 */ 579int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) 580{ 581 uint64_t MS = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 1000; 582 if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { 583 // SRX0 interface should be good, send calendar data 584 cvmx_srxx_com_ctl_t srxx_com_ctl; 585 cvmx_dprintf ("SPI%d: Rx is synchronized, start sending calendar data\n", interface); 586 srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); 587 srxx_com_ctl.s.inf_en = 1; 588 srxx_com_ctl.s.st_en = 1; 589 cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); 590 } 591 592 if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { 593 // STX0 has achieved sync 594 // The corespondant board should be sending calendar data 595 // Enable the STX0 STAT receiver. 596 cvmx_spxx_clk_stat_t stat; 597 uint64_t timeout_time; 598 cvmx_stxx_com_ctl_t stxx_com_ctl; 599 stxx_com_ctl.u64 = 0; 600 stxx_com_ctl.s.st_en = 1; 601 cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); 602 603 // Waiting for calendar sync on STX0 STAT 604 cvmx_dprintf ("SPI%d: Waiting to sync on STX[%d] STAT\n", interface, interface); 605 timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; 606 // SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) 607 do { 608 stat.u64 = cvmx_read_csr (CVMX_SPXX_CLK_STAT (interface)); 609 if (cvmx_get_cycle() > timeout_time) 610 { 611 cvmx_dprintf ("SPI%d: Timeout\n", interface); 612 return -1; 613 } 614 } while (stat.s.stxcal == 0); 615 } 616 617 return 0; 618} 619 620/** 621 * Callback to handle interface up 622 * 623 * @param interface The identifier of the packet interface to configure and 624 * use as a SPI interface. 625 * @param mode The operating mode for the SPI interface. The interface 626 * can operate as a full duplex (both Tx and Rx data paths 627 * active) or as a halfplex (either the Tx data path is 628 * active or the Rx data path is active, but not both). 629 * @return Zero on success, non-zero error code on failure (will cause SPI initialization to abort) 630 */ 631int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode) 632{ 633 cvmx_gmxx_rxx_frm_min_t gmxx_rxx_frm_min; 634 cvmx_gmxx_rxx_frm_max_t gmxx_rxx_frm_max; 635 cvmx_gmxx_rxx_jabber_t gmxx_rxx_jabber; 636 637 if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { 638 cvmx_srxx_com_ctl_t srxx_com_ctl; 639 srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); 640 srxx_com_ctl.s.inf_en = 1; 641 cvmx_write_csr (CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); 642 cvmx_dprintf ("SPI%d: Rx is now up\n", interface); 643 } 644 645 if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { 646 cvmx_stxx_com_ctl_t stxx_com_ctl; 647 stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface)); 648 stxx_com_ctl.s.inf_en = 1; 649 cvmx_write_csr (CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); 650 cvmx_dprintf ("SPI%d: Tx is now up\n", interface); 651 } 652 653 gmxx_rxx_frm_min.u64 = 0; 654 gmxx_rxx_frm_min.s.len = 64; 655 cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0,interface), gmxx_rxx_frm_min.u64); 656 gmxx_rxx_frm_max.u64 = 0; 657 gmxx_rxx_frm_max.s.len = 64*1024 - 4; 658 cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0,interface), gmxx_rxx_frm_max.u64); 659 gmxx_rxx_jabber.u64 = 0; 660 gmxx_rxx_jabber.s.cnt = 64*1024 - 4; 661 cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0,interface), gmxx_rxx_jabber.u64); 662 663 return 0; 664} 665 666