cvmx-spi.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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 <asm/octeon/cvmx-config.h>
  33. #include <asm/octeon/cvmx-pko.h>
  34. #include <asm/octeon/cvmx-spi.h>
  35. #include <asm/octeon/cvmx-spxx-defs.h>
  36. #include <asm/octeon/cvmx-stxx-defs.h>
  37. #include <asm/octeon/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. EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface);
  153. /**
  154. * Callback to perform SPI4 reset
  155. *
  156. * @interface: The identifier of the packet interface to configure and
  157. * use as a SPI interface.
  158. * @mode: The operating mode for the SPI interface. The interface
  159. * can operate as a full duplex (both Tx and Rx data paths
  160. * active) or as a halfplex (either the Tx data path is
  161. * active or the Rx data path is active, but not both).
  162. *
  163. * Returns Zero on success, non-zero error code on failure (will cause
  164. * SPI initialization to abort)
  165. */
  166. int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
  167. {
  168. union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl;
  169. union cvmx_spxx_clk_ctl spxx_clk_ctl;
  170. union cvmx_spxx_bist_stat spxx_bist_stat;
  171. union cvmx_spxx_int_msk spxx_int_msk;
  172. union cvmx_stxx_int_msk stxx_int_msk;
  173. union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
  174. int index;
  175. uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
  176. /* Disable SPI error events while we run BIST */
  177. spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
  178. cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
  179. stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
  180. cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
  181. /* Run BIST in the SPI interface */
  182. cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0);
  183. cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0);
  184. spxx_clk_ctl.u64 = 0;
  185. spxx_clk_ctl.s.runbist = 1;
  186. cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
  187. cvmx_wait(10 * MS);
  188. spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface));
  189. if (spxx_bist_stat.s.stat0)
  190. cvmx_dprintf
  191. ("ERROR SPI%d: BIST failed on receive datapath FIFO\n",
  192. interface);
  193. if (spxx_bist_stat.s.stat1)
  194. cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n",
  195. interface);
  196. if (spxx_bist_stat.s.stat2)
  197. cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n",
  198. interface);
  199. /* Clear the calendar table after BIST to fix parity errors */
  200. for (index = 0; index < 32; index++) {
  201. union cvmx_srxx_spi4_calx srxx_spi4_calx;
  202. union cvmx_stxx_spi4_calx stxx_spi4_calx;
  203. srxx_spi4_calx.u64 = 0;
  204. srxx_spi4_calx.s.oddpar = 1;
  205. cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
  206. srxx_spi4_calx.u64);
  207. stxx_spi4_calx.u64 = 0;
  208. stxx_spi4_calx.s.oddpar = 1;
  209. cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
  210. stxx_spi4_calx.u64);
  211. }
  212. /* Re enable reporting of error interrupts */
  213. cvmx_write_csr(CVMX_SPXX_INT_REG(interface),
  214. cvmx_read_csr(CVMX_SPXX_INT_REG(interface)));
  215. cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
  216. cvmx_write_csr(CVMX_STXX_INT_REG(interface),
  217. cvmx_read_csr(CVMX_STXX_INT_REG(interface)));
  218. cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
  219. /* Setup the CLKDLY right in the middle */
  220. spxx_clk_ctl.u64 = 0;
  221. spxx_clk_ctl.s.seetrn = 0;
  222. spxx_clk_ctl.s.clkdly = 0x10;
  223. spxx_clk_ctl.s.runbist = 0;
  224. spxx_clk_ctl.s.statdrv = 0;
  225. /* This should always be on the opposite edge as statdrv */
  226. spxx_clk_ctl.s.statrcv = 1;
  227. spxx_clk_ctl.s.sndtrn = 0;
  228. spxx_clk_ctl.s.drptrn = 0;
  229. spxx_clk_ctl.s.rcvtrn = 0;
  230. spxx_clk_ctl.s.srxdlck = 0;
  231. cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
  232. cvmx_wait(100 * MS);
  233. /* Reset SRX0 DLL */
  234. spxx_clk_ctl.s.srxdlck = 1;
  235. cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
  236. /* Waiting for Inf0 Spi4 RX DLL to lock */
  237. cvmx_wait(100 * MS);
  238. /* Enable dynamic alignment */
  239. spxx_trn4_ctl.s.trntest = 0;
  240. spxx_trn4_ctl.s.jitter = 1;
  241. spxx_trn4_ctl.s.clr_boot = 1;
  242. spxx_trn4_ctl.s.set_boot = 0;
  243. if (OCTEON_IS_MODEL(OCTEON_CN58XX))
  244. spxx_trn4_ctl.s.maxdist = 3;
  245. else
  246. spxx_trn4_ctl.s.maxdist = 8;
  247. spxx_trn4_ctl.s.macro_en = 1;
  248. spxx_trn4_ctl.s.mux_en = 1;
  249. cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
  250. spxx_dbg_deskew_ctl.u64 = 0;
  251. cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface),
  252. spxx_dbg_deskew_ctl.u64);
  253. return 0;
  254. }
  255. /**
  256. * Callback to setup calendar and miscellaneous settings before clock detection
  257. *
  258. * @interface: The identifier of the packet interface to configure and
  259. * use as a SPI interface.
  260. * @mode: The operating mode for the SPI interface. The interface
  261. * can operate as a full duplex (both Tx and Rx data paths
  262. * active) or as a halfplex (either the Tx data path is
  263. * active or the Rx data path is active, but not both).
  264. * @num_ports: Number of ports to configure on SPI
  265. *
  266. * Returns Zero on success, non-zero error code on failure (will cause
  267. * SPI initialization to abort)
  268. */
  269. int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
  270. int num_ports)
  271. {
  272. int port;
  273. int index;
  274. if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
  275. union cvmx_srxx_com_ctl srxx_com_ctl;
  276. union cvmx_srxx_spi4_stat srxx_spi4_stat;
  277. /* SRX0 number of Ports */
  278. srxx_com_ctl.u64 = 0;
  279. srxx_com_ctl.s.prts = num_ports - 1;
  280. srxx_com_ctl.s.st_en = 0;
  281. srxx_com_ctl.s.inf_en = 0;
  282. cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
  283. /* SRX0 Calendar Table. This round robbins through all ports */
  284. port = 0;
  285. index = 0;
  286. while (port < num_ports) {
  287. union cvmx_srxx_spi4_calx srxx_spi4_calx;
  288. srxx_spi4_calx.u64 = 0;
  289. srxx_spi4_calx.s.prt0 = port++;
  290. srxx_spi4_calx.s.prt1 = port++;
  291. srxx_spi4_calx.s.prt2 = port++;
  292. srxx_spi4_calx.s.prt3 = port++;
  293. srxx_spi4_calx.s.oddpar =
  294. ~(cvmx_dpop(srxx_spi4_calx.u64) & 1);
  295. cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface),
  296. srxx_spi4_calx.u64);
  297. index++;
  298. }
  299. srxx_spi4_stat.u64 = 0;
  300. srxx_spi4_stat.s.len = num_ports;
  301. srxx_spi4_stat.s.m = 1;
  302. cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface),
  303. srxx_spi4_stat.u64);
  304. }
  305. if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
  306. union cvmx_stxx_arb_ctl stxx_arb_ctl;
  307. union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max;
  308. union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh;
  309. union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl;
  310. union cvmx_stxx_spi4_stat stxx_spi4_stat;
  311. union cvmx_stxx_spi4_dat stxx_spi4_dat;
  312. /* STX0 Config */
  313. stxx_arb_ctl.u64 = 0;
  314. stxx_arb_ctl.s.igntpa = 0;
  315. stxx_arb_ctl.s.mintrn = 0;
  316. cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64);
  317. gmxx_tx_spi_max.u64 = 0;
  318. gmxx_tx_spi_max.s.max1 = 8;
  319. gmxx_tx_spi_max.s.max2 = 4;
  320. gmxx_tx_spi_max.s.slice = 0;
  321. cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface),
  322. gmxx_tx_spi_max.u64);
  323. gmxx_tx_spi_thresh.u64 = 0;
  324. gmxx_tx_spi_thresh.s.thresh = 4;
  325. cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface),
  326. gmxx_tx_spi_thresh.u64);
  327. gmxx_tx_spi_ctl.u64 = 0;
  328. gmxx_tx_spi_ctl.s.tpa_clr = 0;
  329. gmxx_tx_spi_ctl.s.cont_pkt = 0;
  330. cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface),
  331. gmxx_tx_spi_ctl.u64);
  332. /* STX0 Training Control */
  333. stxx_spi4_dat.u64 = 0;
  334. /*Minimum needed by dynamic alignment */
  335. stxx_spi4_dat.s.alpha = 32;
  336. stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */
  337. cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface),
  338. stxx_spi4_dat.u64);
  339. /* STX0 Calendar Table. This round robbins through all ports */
  340. port = 0;
  341. index = 0;
  342. while (port < num_ports) {
  343. union cvmx_stxx_spi4_calx stxx_spi4_calx;
  344. stxx_spi4_calx.u64 = 0;
  345. stxx_spi4_calx.s.prt0 = port++;
  346. stxx_spi4_calx.s.prt1 = port++;
  347. stxx_spi4_calx.s.prt2 = port++;
  348. stxx_spi4_calx.s.prt3 = port++;
  349. stxx_spi4_calx.s.oddpar =
  350. ~(cvmx_dpop(stxx_spi4_calx.u64) & 1);
  351. cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface),
  352. stxx_spi4_calx.u64);
  353. index++;
  354. }
  355. stxx_spi4_stat.u64 = 0;
  356. stxx_spi4_stat.s.len = num_ports;
  357. stxx_spi4_stat.s.m = 1;
  358. cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface),
  359. stxx_spi4_stat.u64);
  360. }
  361. return 0;
  362. }
  363. /**
  364. * Callback to perform clock detection
  365. *
  366. * @interface: The identifier of the packet interface to configure and
  367. * use as a SPI interface.
  368. * @mode: The operating mode for the SPI interface. The interface
  369. * can operate as a full duplex (both Tx and Rx data paths
  370. * active) or as a halfplex (either the Tx data path is
  371. * active or the Rx data path is active, but not both).
  372. * @timeout: Timeout to wait for clock synchronization in seconds
  373. *
  374. * Returns Zero on success, non-zero error code on failure (will cause
  375. * SPI initialization to abort)
  376. */
  377. int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
  378. {
  379. int clock_transitions;
  380. union cvmx_spxx_clk_stat stat;
  381. uint64_t timeout_time;
  382. uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
  383. /*
  384. * Regardless of operating mode, both Tx and Rx clocks must be
  385. * present for the SPI interface to operate.
  386. */
  387. cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface);
  388. timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
  389. /*
  390. * Require 100 clock transitions in order to avoid any noise
  391. * in the beginning.
  392. */
  393. clock_transitions = 100;
  394. do {
  395. stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
  396. if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) {
  397. /*
  398. * We've seen a clock transition, so decrement
  399. * the number we still need.
  400. */
  401. clock_transitions--;
  402. cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
  403. stat.s.s4clk0 = 0;
  404. stat.s.s4clk1 = 0;
  405. }
  406. if (cvmx_get_cycle() > timeout_time) {
  407. cvmx_dprintf("SPI%d: Timeout\n", interface);
  408. return -1;
  409. }
  410. } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0);
  411. cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface);
  412. timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
  413. /*
  414. * Require 100 clock transitions in order to avoid any noise in the
  415. * beginning.
  416. */
  417. clock_transitions = 100;
  418. do {
  419. stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
  420. if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) {
  421. /*
  422. * We've seen a clock transition, so decrement
  423. * the number we still need
  424. */
  425. clock_transitions--;
  426. cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
  427. stat.s.d4clk0 = 0;
  428. stat.s.d4clk1 = 0;
  429. }
  430. if (cvmx_get_cycle() > timeout_time) {
  431. cvmx_dprintf("SPI%d: Timeout\n", interface);
  432. return -1;
  433. }
  434. } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0);
  435. return 0;
  436. }
  437. /**
  438. * Callback to perform link training
  439. *
  440. * @interface: The identifier of the packet interface to configure and
  441. * use as a SPI interface.
  442. * @mode: The operating mode for the SPI interface. The interface
  443. * can operate as a full duplex (both Tx and Rx data paths
  444. * active) or as a halfplex (either the Tx data path is
  445. * active or the Rx data path is active, but not both).
  446. * @timeout: Timeout to wait for link to be trained (in seconds)
  447. *
  448. * Returns Zero on success, non-zero error code on failure (will cause
  449. * SPI initialization to abort)
  450. */
  451. int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
  452. {
  453. union cvmx_spxx_trn4_ctl spxx_trn4_ctl;
  454. union cvmx_spxx_clk_stat stat;
  455. uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
  456. uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
  457. int rx_training_needed;
  458. /* SRX0 & STX0 Inf0 Links are configured - begin training */
  459. union cvmx_spxx_clk_ctl spxx_clk_ctl;
  460. spxx_clk_ctl.u64 = 0;
  461. spxx_clk_ctl.s.seetrn = 0;
  462. spxx_clk_ctl.s.clkdly = 0x10;
  463. spxx_clk_ctl.s.runbist = 0;
  464. spxx_clk_ctl.s.statdrv = 0;
  465. /* This should always be on the opposite edge as statdrv */
  466. spxx_clk_ctl.s.statrcv = 1;
  467. spxx_clk_ctl.s.sndtrn = 1;
  468. spxx_clk_ctl.s.drptrn = 1;
  469. spxx_clk_ctl.s.rcvtrn = 1;
  470. spxx_clk_ctl.s.srxdlck = 1;
  471. cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64);
  472. cvmx_wait(1000 * MS);
  473. /* SRX0 clear the boot bit */
  474. spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface));
  475. spxx_trn4_ctl.s.clr_boot = 1;
  476. cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64);
  477. /* Wait for the training sequence to complete */
  478. cvmx_dprintf("SPI%d: Waiting for training\n", interface);
  479. cvmx_wait(1000 * MS);
  480. /* Wait a really long time here */
  481. timeout_time = cvmx_get_cycle() + 1000ull * MS * 600;
  482. /*
  483. * The HRM says we must wait for 34 + 16 * MAXDIST training sequences.
  484. * We'll be pessimistic and wait for a lot more.
  485. */
  486. rx_training_needed = 500;
  487. do {
  488. stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
  489. if (stat.s.srxtrn && rx_training_needed) {
  490. rx_training_needed--;
  491. cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64);
  492. stat.s.srxtrn = 0;
  493. }
  494. if (cvmx_get_cycle() > timeout_time) {
  495. cvmx_dprintf("SPI%d: Timeout\n", interface);
  496. return -1;
  497. }
  498. } while (stat.s.srxtrn == 0);
  499. return 0;
  500. }
  501. /**
  502. * Callback to perform calendar data synchronization
  503. *
  504. * @interface: The identifier of the packet interface to configure and
  505. * use as a SPI interface.
  506. * @mode: The operating mode for the SPI interface. The interface
  507. * can operate as a full duplex (both Tx and Rx data paths
  508. * active) or as a halfplex (either the Tx data path is
  509. * active or the Rx data path is active, but not both).
  510. * @timeout: Timeout to wait for calendar data in seconds
  511. *
  512. * Returns Zero on success, non-zero error code on failure (will cause
  513. * SPI initialization to abort)
  514. */
  515. int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
  516. {
  517. uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000;
  518. if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
  519. /* SRX0 interface should be good, send calendar data */
  520. union cvmx_srxx_com_ctl srxx_com_ctl;
  521. cvmx_dprintf
  522. ("SPI%d: Rx is synchronized, start sending calendar data\n",
  523. interface);
  524. srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
  525. srxx_com_ctl.s.inf_en = 1;
  526. srxx_com_ctl.s.st_en = 1;
  527. cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
  528. }
  529. if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
  530. /* STX0 has achieved sync */
  531. /* The corespondant board should be sending calendar data */
  532. /* Enable the STX0 STAT receiver. */
  533. union cvmx_spxx_clk_stat stat;
  534. uint64_t timeout_time;
  535. union cvmx_stxx_com_ctl stxx_com_ctl;
  536. stxx_com_ctl.u64 = 0;
  537. stxx_com_ctl.s.st_en = 1;
  538. cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
  539. /* Waiting for calendar sync on STX0 STAT */
  540. cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n",
  541. interface, interface);
  542. timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout;
  543. /* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */
  544. do {
  545. stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface));
  546. if (cvmx_get_cycle() > timeout_time) {
  547. cvmx_dprintf("SPI%d: Timeout\n", interface);
  548. return -1;
  549. }
  550. } while (stat.s.stxcal == 0);
  551. }
  552. return 0;
  553. }
  554. /**
  555. * Callback to handle interface up
  556. *
  557. * @interface: The identifier of the packet interface to configure and
  558. * use as a SPI interface.
  559. * @mode: The operating mode for the SPI interface. The interface
  560. * can operate as a full duplex (both Tx and Rx data paths
  561. * active) or as a halfplex (either the Tx data path is
  562. * active or the Rx data path is active, but not both).
  563. *
  564. * Returns Zero on success, non-zero error code on failure (will cause
  565. * SPI initialization to abort)
  566. */
  567. int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode)
  568. {
  569. union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min;
  570. union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max;
  571. union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber;
  572. if (mode & CVMX_SPI_MODE_RX_HALFPLEX) {
  573. union cvmx_srxx_com_ctl srxx_com_ctl;
  574. srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface));
  575. srxx_com_ctl.s.inf_en = 1;
  576. cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64);
  577. cvmx_dprintf("SPI%d: Rx is now up\n", interface);
  578. }
  579. if (mode & CVMX_SPI_MODE_TX_HALFPLEX) {
  580. union cvmx_stxx_com_ctl stxx_com_ctl;
  581. stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface));
  582. stxx_com_ctl.s.inf_en = 1;
  583. cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64);
  584. cvmx_dprintf("SPI%d: Tx is now up\n", interface);
  585. }
  586. gmxx_rxx_frm_min.u64 = 0;
  587. gmxx_rxx_frm_min.s.len = 64;
  588. cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface),
  589. gmxx_rxx_frm_min.u64);
  590. gmxx_rxx_frm_max.u64 = 0;
  591. gmxx_rxx_frm_max.s.len = 64 * 1024 - 4;
  592. cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface),
  593. gmxx_rxx_frm_max.u64);
  594. gmxx_rxx_jabber.u64 = 0;
  595. gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4;
  596. cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64);
  597. return 0;
  598. }