blob: 2eb3b7d9c462057d8ed23d993838c3daf6af54b7 [file] [log] [blame]
Aaron Williams6fd320c2022-04-07 09:11:25 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018-2022 Marvell International Ltd.
4 *
5 * Functions for XAUI initialization, configuration,
6 * and monitoring.
7 */
8
9#include <time.h>
10#include <log.h>
11#include <linux/delay.h>
12
13#include <mach/cvmx-regs.h>
14#include <mach/cvmx-csr.h>
15#include <mach/cvmx-bootmem.h>
16#include <mach/octeon-model.h>
17#include <mach/cvmx-fuse.h>
18#include <mach/octeon-feature.h>
19#include <mach/cvmx-qlm.h>
20#include <mach/octeon_qlm.h>
21#include <mach/cvmx-pcie.h>
22#include <mach/cvmx-coremask.h>
23
24#include <mach/cvmx-agl-defs.h>
25#include <mach/cvmx-bgxx-defs.h>
26#include <mach/cvmx-ciu-defs.h>
27#include <mach/cvmx-gmxx-defs.h>
28#include <mach/cvmx-ipd-defs.h>
29#include <mach/cvmx-pcsx-defs.h>
30#include <mach/cvmx-pcsxx-defs.h>
31#include <mach/cvmx-pki-defs.h>
32#include <mach/cvmx-pko-defs.h>
33#include <mach/cvmx-xcv-defs.h>
34
35#include <mach/cvmx-helper.h>
36#include <mach/cvmx-helper-board.h>
37#include <mach/cvmx-helper-cfg.h>
38
39int __cvmx_helper_xaui_enumerate(int xiface)
40{
41 struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
42 int interface = xi.interface;
43 union cvmx_gmxx_hg2_control gmx_hg2_control;
44
45 if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
46 enum cvmx_qlm_mode qlm_mode =
47 cvmx_qlm_get_dlm_mode(0, interface);
48
49 if (qlm_mode == CVMX_QLM_MODE_RXAUI)
50 return 1;
51 return 0;
52 }
53 /* If HiGig2 is enabled return 16 ports, otherwise return 1 port */
54 gmx_hg2_control.u64 = csr_rd(CVMX_GMXX_HG2_CONTROL(interface));
55 if (gmx_hg2_control.s.hg2tx_en)
56 return 16;
57 else
58 return 1;
59}
60
61/**
62 * @INTERNAL
63 * Probe a XAUI interface and determine the number of ports
64 * connected to it. The XAUI interface should still be down
65 * after this call.
66 *
67 * @param xiface Interface to probe
68 *
69 * @return Number of ports on the interface. Zero to disable.
70 */
71int __cvmx_helper_xaui_probe(int xiface)
72{
73 int i, ports;
74 struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
75 int interface = xi.interface;
76 union cvmx_gmxx_inf_mode mode;
77
78 /*
79 * CN63XX Pass 1.0 errata G-14395 requires the QLM De-emphasis
80 * be programmed.
81 */
82 if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_0)) {
83 union cvmx_ciu_qlm2 ciu_qlm;
84
85 ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2);
86 ciu_qlm.s.txbypass = 1;
87 ciu_qlm.s.txdeemph = 0x5;
88 ciu_qlm.s.txmargin = 0x1a;
89 csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64);
90 }
91
92 /*
93 * CN63XX Pass 2.x errata G-15273 requires the QLM De-emphasis
94 * be programmed when using a 156.25Mhz ref clock.
95 */
96 if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_X)) {
97 /* Read the QLM speed pins */
98 union cvmx_mio_rst_boot mio_rst_boot;
99
100 mio_rst_boot.u64 = csr_rd(CVMX_MIO_RST_BOOT);
101
102 if (mio_rst_boot.cn63xx.qlm2_spd == 0xb) {
103 union cvmx_ciu_qlm2 ciu_qlm;
104
105 ciu_qlm.u64 = csr_rd(CVMX_CIU_QLM2);
106 ciu_qlm.s.txbypass = 1;
107 ciu_qlm.s.txdeemph = 0xa;
108 ciu_qlm.s.txmargin = 0x1f;
109 csr_wr(CVMX_CIU_QLM2, ciu_qlm.u64);
110 }
111 }
112
113 /*
114 * Check if QLM is configured correct for XAUI/RXAUI, verify
115 * the speed as well as mode.
116 */
117 if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
118 int qlm = cvmx_qlm_interface(xiface);
119 enum cvmx_qlm_mode mode = cvmx_qlm_get_mode(qlm);
120
121 if (mode != CVMX_QLM_MODE_XAUI && mode != CVMX_QLM_MODE_RXAUI)
122 return 0;
123 }
124
125 ports = __cvmx_helper_xaui_enumerate(xiface);
126
127 if (ports <= 0)
128 return 0;
129
130 /*
131 * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the
132 * interface needs to be enabled before IPD otherwise per port
133 * backpressure may not work properly.
134 */
135 mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface));
136 mode.s.en = 1;
137 csr_wr(CVMX_GMXX_INF_MODE(interface), mode.u64);
138
139 if (!OCTEON_IS_MODEL(OCTEON_CN68XX) &&
140 !OCTEON_IS_MODEL(OCTEON_CN70XX)) {
141 /*
142 * Setup PKO to support 16 ports for HiGig2 virtual
143 * ports. We're pointing all of the PKO packet ports
144 * for this interface to the XAUI. This allows us to
145 * use HiGig2 backpressure per port.
146 */
147 for (i = 0; i < 16; i++) {
148 union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs;
149
150 pko_mem_port_ptrs.u64 = 0;
151 /*
152 * We set each PKO port to have equal priority
153 * in a round robin fashion.
154 */
155 pko_mem_port_ptrs.s.static_p = 0;
156 pko_mem_port_ptrs.s.qos_mask = 0xff;
157 /* All PKO ports map to the same XAUI hardware port */
158 pko_mem_port_ptrs.s.eid = interface * 4;
159 pko_mem_port_ptrs.s.pid = interface * 16 + i;
160 pko_mem_port_ptrs.s.bp_port = interface * 16 + i;
161 csr_wr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64);
162 }
163 }
164
165 return ports;
166}
167
168/**
169 * @INTERNAL
170 * Bringup XAUI interface. After this call packet I/O should be
171 * fully functional.
172 *
173 * @param interface to bring up
174 *
175 * @return Zero on success, negative on failure
176 */
177int __cvmx_helper_xaui_link_init(int interface)
178{
179 union cvmx_gmxx_prtx_cfg gmx_cfg;
180 union cvmx_pcsxx_control1_reg xaui_ctl;
181 union cvmx_pcsxx_misc_ctl_reg misc_ctl;
182 union cvmx_gmxx_tx_xaui_ctl tx_ctl;
183
184 /* (1) Interface has already been enabled. */
185
186 /* (2) Disable GMX. */
187 misc_ctl.u64 = csr_rd(CVMX_PCSXX_MISC_CTL_REG(interface));
188 misc_ctl.s.gmxeno = 1;
189 csr_wr(CVMX_PCSXX_MISC_CTL_REG(interface), misc_ctl.u64);
190
191 /* (3) Disable GMX and PCSX interrupts. */
192 csr_wr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0);
193 csr_wr(CVMX_GMXX_TX_INT_EN(interface), 0x0);
194 csr_wr(CVMX_PCSXX_INT_EN_REG(interface), 0x0);
195
196 /* (4) Bring up the PCSX and GMX reconciliation layer. */
197 /* (4)a Set polarity and lane swapping. */
198 /* (4)b */
199 tx_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface));
200 /* Enable better IFG packing and improves performance */
201 tx_ctl.s.dic_en = 1;
202 tx_ctl.s.uni_en = 0;
203 csr_wr(CVMX_GMXX_TX_XAUI_CTL(interface), tx_ctl.u64);
204
205 /* (4)c Aply reset sequence */
206 xaui_ctl.u64 = csr_rd(CVMX_PCSXX_CONTROL1_REG(interface));
207 xaui_ctl.s.lo_pwr = 0;
208
209 /*
210 * Errata G-15618 requires disabling PCS soft reset in some
211 * OCTEON II models.
212 */
213 if (!OCTEON_IS_MODEL(OCTEON_CN63XX) &&
214 !OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X) &&
215 !OCTEON_IS_MODEL(OCTEON_CN68XX))
216 xaui_ctl.s.reset = 1;
217 csr_wr(CVMX_PCSXX_CONTROL1_REG(interface), xaui_ctl.u64);
218
219 if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X) && interface != 1) {
220 /*
221 * Note that GMX 1 was skipped as GMX0 is on the same
222 * QLM and will always be done first
223 *
224 * Workaround for Errata (G-16467).
225 */
226 int qlm = interface;
227#ifdef CVMX_QLM_DUMP_STATE
228 debug("%s:%d: XAUI%d: Applying workaround for Errata G-16467\n",
229 __func__, __LINE__, qlm);
230 cvmx_qlm_display_registers(qlm);
231 debug("\n");
232#endif
233 /*
234 * This workaround only applies to QLMs running XAUI
235 * at 6.25Ghz
236 */
237 if ((cvmx_qlm_get_gbaud_mhz(qlm) == 6250) &&
238 (cvmx_qlm_jtag_get(qlm, 0, "clkf_byp") != 20)) {
239 /* Wait 100us for links to stabalize */
240 udelay(100);
241 cvmx_qlm_jtag_set(qlm, -1, "clkf_byp", 20);
242 /* Allow the QLM to exit reset */
243 cvmx_qlm_jtag_set(qlm, -1, "cfg_rst_n_clr", 0);
244 /* Wait 100us for links to stabalize */
245 udelay(100);
246 /* Allow TX on QLM */
247 cvmx_qlm_jtag_set(qlm, -1, "cfg_tx_idle_set", 0);
248 }
249#ifdef CVMX_QLM_DUMP_STATE
250 debug("%s:%d: XAUI%d: Done applying workaround for Errata G-16467\n",
251 __func__, __LINE__, qlm);
252 cvmx_qlm_display_registers(qlm);
253 debug("\n\n");
254#endif
255 }
256
257 /* Wait for PCS to come out of reset */
258 if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_CONTROL1_REG(interface),
259 cvmx_pcsxx_control1_reg_t, reset, ==, 0,
260 10000))
261 return -1;
262 /* Wait for PCS to be aligned */
263 if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_10GBX_STATUS_REG(interface),
264 cvmx_pcsxx_10gbx_status_reg_t, alignd, ==, 1,
265 10000))
266 return -1;
267 /* Wait for RX to be ready */
268 if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_RX_XAUI_CTL(interface),
269 cvmx_gmxx_rx_xaui_ctl_t, status, ==, 0,
270 10000))
271 return -1;
272
273 /* (6) Configure GMX */
274
275 /* Wait for GMX RX to be idle */
276 if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(0, interface),
277 cvmx_gmxx_prtx_cfg_t, rx_idle, ==, 1, 10000))
278 return -1;
279 /* Wait for GMX TX to be idle */
280 if (CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(0, interface),
281 cvmx_gmxx_prtx_cfg_t, tx_idle, ==, 1, 10000))
282 return -1;
283
284 /* GMX configure */
285 gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface));
286 gmx_cfg.s.speed = 1;
287 gmx_cfg.s.speed_msb = 0;
288 gmx_cfg.s.slottime = 1;
289 csr_wr(CVMX_GMXX_TX_PRTS(interface), 1);
290 csr_wr(CVMX_GMXX_TXX_SLOT(0, interface), 512);
291 csr_wr(CVMX_GMXX_TXX_BURST(0, interface), 8192);
292 csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
293
294 /* Wait for receive link */
295 if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS1_REG(interface),
296 cvmx_pcsxx_status1_reg_t, rcv_lnk, ==, 1,
297 10000))
298 return -1;
299 if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS2_REG(interface),
300 cvmx_pcsxx_status2_reg_t, xmtflt, ==, 0,
301 10000))
302 return -1;
303 if (CVMX_WAIT_FOR_FIELD64(CVMX_PCSXX_STATUS2_REG(interface),
304 cvmx_pcsxx_status2_reg_t, rcvflt, ==, 0,
305 10000))
306 return -1;
307
308 /* (8) Enable packet reception */
309 misc_ctl.s.gmxeno = 0;
310 csr_wr(CVMX_PCSXX_MISC_CTL_REG(interface), misc_ctl.u64);
311
312 /* Clear all error interrupts before enabling the interface. */
313 csr_wr(CVMX_GMXX_RXX_INT_REG(0, interface), ~0x0ull);
314 csr_wr(CVMX_GMXX_TX_INT_REG(interface), ~0x0ull);
315 csr_wr(CVMX_PCSXX_INT_REG(interface), ~0x0ull);
316
317 /* Enable GMX */
318 gmx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface));
319 gmx_cfg.s.en = 1;
320 csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
321
322 return 0;
323}
324
325/**
326 * @INTERNAL
327 * Bringup and enable a XAUI interface. After this call packet
328 * I/O should be fully functional. This is called with IPD
329 * enabled but PKO disabled.
330 *
331 * @param xiface Interface to bring up
332 *
333 * @return Zero on success, negative on failure
334 */
335int __cvmx_helper_xaui_enable(int xiface)
336{
337 struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
338 int interface = xi.interface;
339
340 __cvmx_helper_setup_gmx(interface, 1);
341
342 /* Setup PKND and BPID */
343 if (octeon_has_feature(OCTEON_FEATURE_PKND)) {
344 union cvmx_gmxx_bpid_msk bpid_msk;
345 union cvmx_gmxx_bpid_mapx bpid_map;
346 union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg;
347 union cvmx_gmxx_txx_append gmxx_txx_append_cfg;
348
349 /* Setup PKIND */
350 gmxx_prtx_cfg.u64 = csr_rd(CVMX_GMXX_PRTX_CFG(0, interface));
351 gmxx_prtx_cfg.s.pknd = cvmx_helper_get_pknd(interface, 0);
352 csr_wr(CVMX_GMXX_PRTX_CFG(0, interface), gmxx_prtx_cfg.u64);
353
354 /* Setup BPID */
355 bpid_map.u64 = csr_rd(CVMX_GMXX_BPID_MAPX(0, interface));
356 bpid_map.s.val = 1;
357 bpid_map.s.bpid = cvmx_helper_get_bpid(interface, 0);
358 csr_wr(CVMX_GMXX_BPID_MAPX(0, interface), bpid_map.u64);
359
360 bpid_msk.u64 = csr_rd(CVMX_GMXX_BPID_MSK(interface));
361 bpid_msk.s.msk_or |= 1;
362 bpid_msk.s.msk_and &= ~1;
363 csr_wr(CVMX_GMXX_BPID_MSK(interface), bpid_msk.u64);
364
365 /* CN68XX adds the padding and FCS in PKO, not GMX */
366 gmxx_txx_append_cfg.u64 =
367 csr_rd(CVMX_GMXX_TXX_APPEND(0, interface));
368 gmxx_txx_append_cfg.s.fcs = 0;
369 gmxx_txx_append_cfg.s.pad = 0;
370 csr_wr(CVMX_GMXX_TXX_APPEND(0, interface),
371 gmxx_txx_append_cfg.u64);
372 }
373
374 /* 70XX eval boards use Marvel phy, set disparity accordingly. */
375 if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
376 union cvmx_gmxx_rxaui_ctl rxaui_ctl;
377
378 rxaui_ctl.u64 = csr_rd(CVMX_GMXX_RXAUI_CTL(interface));
379 rxaui_ctl.s.disparity = 1;
380 csr_wr(CVMX_GMXX_RXAUI_CTL(interface), rxaui_ctl.u64);
381 }
382
383 __cvmx_helper_xaui_link_init(interface);
384
385 return 0;
386}
387
388/**
389 * @INTERNAL
390 * Return the link state of an IPD/PKO port as returned by
391 * auto negotiation. The result of this function may not match
392 * Octeon's link config if auto negotiation has changed since
393 * the last call to cvmx_helper_link_set().
394 *
395 * @param ipd_port IPD/PKO port to query
396 *
397 * @return Link state
398 */
399cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port)
400{
401 int interface = cvmx_helper_get_interface_num(ipd_port);
402 union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl;
403 union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl;
404 union cvmx_pcsxx_status1_reg pcsxx_status1_reg;
405 cvmx_helper_link_info_t result;
406
407 gmxx_tx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface));
408 gmxx_rx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_RX_XAUI_CTL(interface));
409 pcsxx_status1_reg.u64 = csr_rd(CVMX_PCSXX_STATUS1_REG(interface));
410 result.u64 = 0;
411
412 /* Only return a link if both RX and TX are happy */
413 if (gmxx_tx_xaui_ctl.s.ls == 0 && gmxx_rx_xaui_ctl.s.status == 0 &&
414 pcsxx_status1_reg.s.rcv_lnk == 1) {
415 union cvmx_pcsxx_misc_ctl_reg misc_ctl;
416
417 result.s.link_up = 1;
418 result.s.full_duplex = 1;
419 if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
420 union cvmx_mio_qlmx_cfg qlm_cfg;
421 int lanes;
422 int qlm = (interface == 1) ? 0 : interface;
423
424 qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm));
425 result.s.speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10;
426 lanes = (qlm_cfg.s.qlm_cfg == 7) ? 2 : 4;
427 result.s.speed *= lanes;
428 } else if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
429 int qlm = cvmx_qlm_interface(interface);
430
431 result.s.speed = cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10;
432 result.s.speed *= 4;
433 } else {
434 result.s.speed = 10000;
435 }
436 misc_ctl.u64 = csr_rd(CVMX_PCSXX_MISC_CTL_REG(interface));
437 if (misc_ctl.s.gmxeno)
438 __cvmx_helper_xaui_link_init(interface);
439 } else {
440 /* Disable GMX and PCSX interrupts. */
441 csr_wr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0);
442 csr_wr(CVMX_GMXX_TX_INT_EN(interface), 0x0);
443 csr_wr(CVMX_PCSXX_INT_EN_REG(interface), 0x0);
444 }
445 return result;
446}
447
448/**
449 * @INTERNAL
450 * Configure an IPD/PKO port for the specified link state. This
451 * function does not influence auto negotiation at the PHY level.
452 * The passed link state must always match the link state returned
453 * by cvmx_helper_link_get(). It is normally best to use
454 * cvmx_helper_link_autoconf() instead.
455 *
456 * @param ipd_port IPD/PKO port to configure
457 * @param link_info The new link state
458 *
459 * @return Zero on success, negative on failure
460 */
461int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info)
462{
463 int interface = cvmx_helper_get_interface_num(ipd_port);
464 union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl;
465 union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl;
466
467 gmxx_tx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_TX_XAUI_CTL(interface));
468 gmxx_rx_xaui_ctl.u64 = csr_rd(CVMX_GMXX_RX_XAUI_CTL(interface));
469
470 /* If the link shouldn't be up, then just return */
471 if (!link_info.s.link_up)
472 return 0;
473
474 /* Do nothing if both RX and TX are happy */
475 if (gmxx_tx_xaui_ctl.s.ls == 0 && gmxx_rx_xaui_ctl.s.status == 0)
476 return 0;
477
478 /* Bring the link up */
479 return __cvmx_helper_xaui_link_init(interface);
480}
481
482/**
483 * @INTERNAL
484 * Configure a port for internal and/or external loopback. Internal loopback
485 * causes packets sent by the port to be received by Octeon. External loopback
486 * causes packets received from the wire to sent out again.
487 *
488 * @param ipd_port IPD/PKO port to loopback.
489 * @param enable_internal
490 * Non zero if you want internal loopback
491 * @param enable_external
492 * Non zero if you want external loopback
493 *
494 * @return Zero on success, negative on failure.
495 */
496extern int __cvmx_helper_xaui_configure_loopback(int ipd_port,
497 int enable_internal,
498 int enable_external)
499{
500 int interface = cvmx_helper_get_interface_num(ipd_port);
501 union cvmx_pcsxx_control1_reg pcsxx_control1_reg;
502 union cvmx_gmxx_xaui_ext_loopback gmxx_xaui_ext_loopback;
503
504 /* Set the internal loop */
505 pcsxx_control1_reg.u64 = csr_rd(CVMX_PCSXX_CONTROL1_REG(interface));
506 pcsxx_control1_reg.s.loopbck1 = enable_internal;
507 csr_wr(CVMX_PCSXX_CONTROL1_REG(interface), pcsxx_control1_reg.u64);
508
509 /* Set the external loop */
510 gmxx_xaui_ext_loopback.u64 =
511 csr_rd(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface));
512 gmxx_xaui_ext_loopback.s.en = enable_external;
513 csr_wr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface),
514 gmxx_xaui_ext_loopback.u64);
515
516 /* Take the link through a reset */
517 return __cvmx_helper_xaui_link_init(interface);
518}