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