cvmx-spi.c 22 KB

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