1 /***********************license start***********************************
2 * Copyright (c) 2003-2017 Cavium Inc. ([email protected]). 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 Inc. nor the names of
19 * its contributors may be used to endorse or promote products
20 * derived from this software without specific prior written
21 * permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include "libbdk-hal/if/bdk-if.h"
41 #include "libbdk-hal/bdk-qlm.h"
42 #include "libbdk-hal/qlm/bdk-qlm-common.h"
43 #include "libbdk-arch/bdk-csrs-gser.h"
44 #include "libbdk-arch/bdk-csrs-pem.h"
45 #include "libbdk-hal/bdk-config.h"
46 #include "libbdk-hal/bdk-utils.h"
47 #include "libbdk-hal/bdk-twsi.h"
48
49 /* Indexed by QLM number and lane */
50 static uint64_t prbs_errors[14][4];
51
52 /**
53 * Figure out which lane mode to use for a given reference clock and GBaud
54 *
55 * @param mode_name String name for error messages
56 * @param qlm QlM being configured
57 * @param ref_clk Reference clock in hertz
58 * @param baud_mhz Baud rate in Mhz
59 *
60 * @return Lane mode or -1 on failure
61 */
__bdk_qlm_get_lane_mode_for_speed_and_ref_clk(const char * mode_name,int qlm,int ref_clk,int baud_mhz)62 int __bdk_qlm_get_lane_mode_for_speed_and_ref_clk(const char *mode_name, int qlm, int ref_clk, int baud_mhz)
63 {
64 if (baud_mhz <= 1250)
65 {
66 if ((ref_clk == REF_156MHZ) || (ref_clk == REF_100MHZ))
67 return BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII;
68 else
69 {
70 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
71 return -1;
72 }
73 }
74 else if (baud_mhz <= 2500)
75 {
76 if (ref_clk == REF_100MHZ)
77 return BDK_GSER_LMODE_E_R_25G_REFCLK100;
78 else if (ref_clk == REF_125MHZ)
79 return BDK_GSER_LMODE_E_R_25G_REFCLK125;
80 else
81 {
82 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
83 return -1;
84 }
85 }
86 else if (baud_mhz <= 3125)
87 {
88 if (ref_clk == REF_156MHZ)
89 return BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI;
90 else
91 {
92 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
93 return -1;
94 }
95 }
96 else if (baud_mhz <= 5000)
97 {
98 if (ref_clk == REF_100MHZ)
99 return BDK_GSER_LMODE_E_R_5G_REFCLK100;
100 else if (ref_clk == REF_125MHZ)
101 return BDK_GSER_LMODE_E_R_5G_REFCLK125;
102 else
103 return BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII;
104 }
105 else if (baud_mhz <= 6250)
106 {
107 if (ref_clk == REF_156MHZ)
108 return BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI;
109 else
110 {
111 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
112 return -1;
113 }
114 }
115 else if (baud_mhz <= 8000)
116 {
117 if (ref_clk == REF_100MHZ)
118 return BDK_GSER_LMODE_E_R_8G_REFCLK100;
119 else if (ref_clk == REF_125MHZ)
120 return BDK_GSER_LMODE_E_R_8G_REFCLK125;
121 else
122 {
123 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
124 return -1;
125 }
126 }
127 else /* Baud 10312.5 */
128 {
129 if (ref_clk == REF_156MHZ)
130 return BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR;
131 else
132 {
133 bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
134 return -1;
135 }
136 }
137 bdk_error("Invalid speed for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
138 return -1;
139 }
140
141 /**
142 * Setup the PEM to either driver or receive reset from PRST based on RC or EP
143 *
144 * @param node Node to use in a Numa setup
145 * @param pem Which PEM to setuo
146 * @param is_endpoint
147 * Non zero if PEM is a EP
148 */
__bdk_qlm_setup_pem_reset(bdk_node_t node,int pem,int is_endpoint)149 void __bdk_qlm_setup_pem_reset(bdk_node_t node, int pem, int is_endpoint)
150 {
151 /* Make sure is_endpoint is either 0 or 1 */
152 is_endpoint = (is_endpoint != 0);
153 BDK_CSR_MODIFY(c, node, BDK_RST_CTLX(pem),
154 c.s.prst_link = 0; /* Link down doesn't automatically assert PERST */
155 c.s.rst_link = is_endpoint; /* Link down automatically assert soft reset for EP */
156 c.s.rst_drv = !is_endpoint; /* PERST is output for RC, input for EP */
157 c.s.rst_rcv = is_endpoint; /* Only read PERST in EP mode */
158 c.s.rst_chip = 0); /* PERST doesn't pull CHIP_RESET */
159
160 if (is_endpoint)
161 {
162 /* If we're configuring an endpoint manually the PEM will not
163 be turned on by default by the hardware. Turn it on now */
164 BDK_CSR_INIT(pemx_on, node, BDK_PEMX_ON(pem));
165 if (!pemx_on.s.pemon)
166 {
167 BDK_CSR_MODIFY(c, node, BDK_PEMX_CLK_EN(pem),
168 c.cn83xx.pceclk_gate = 0;
169 c.cn83xx.csclk_gate = 0);
170 BDK_CSR_MODIFY(c, node, BDK_PEMX_ON(pem),
171 c.s.pemon = 1);
172 }
173 }
174 }
175
176 /**
177 * Measure the reference clock of a QLM
178 *
179 * @param qlm QLM to measure
180 *
181 * @return Clock rate in Hz
182 */
__bdk_qlm_measure_refclock(bdk_node_t node,int qlm)183 int __bdk_qlm_measure_refclock(bdk_node_t node, int qlm)
184 {
185 /* Clear the counter */
186 BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
187 c.s.enb = 0;
188 c.s.clr = 1);
189 bdk_wait_usec(1); /* Give counter a chance to clear */
190 if (BDK_CSR_READ(node, BDK_GSERX_REFCLK_EVT_CNTR(qlm)))
191 bdk_error("GSER%d: Ref clock counter not zero\n", qlm);
192 /* Start counting */
193 uint64_t start = bdk_clock_get_count(BDK_CLOCK_TIME);
194 BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
195 c.s.enb = 1;
196 c.s.clr = 0);
197 /* Wait for a short time to get a number of counts */
198 bdk_wait_usec(20000); /* 20ms */
199 /* Stop counting */
200 BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
201 c.s.enb = 0);
202 uint64_t stop = bdk_clock_get_count(BDK_CLOCK_TIME);
203 bdk_wait_usec(1); /* Give counter a chance to stabalize */
204
205 /* Calculate the rate */
206 uint64_t count = BDK_CSR_READ(node, BDK_GSERX_REFCLK_EVT_CNTR(qlm));
207 count *= bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME);
208 count /= stop - start;
209 return count;
210 }
211
212 /**
213 * Put a QLM into hardware reset
214 *
215 * @param node Node to use in a numa setup
216 * @param qlm QLM to use
217 *
218 * @return Zero on success, negative on failure
219 */
__bdk_qlm_reset(bdk_node_t node,int qlm)220 int __bdk_qlm_reset(bdk_node_t node, int qlm)
221 {
222 BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
223 c.s.phy_reset = 1);
224 return 0;
225 }
226
227 /**
228 * Enable PRBS on a QLM
229 *
230 * @param node Node to use in a numa setup
231 * @param qlm QLM to use
232 * @param prbs PRBS mode (31, etc)
233 * @param dir Directions to enable. This is so you can enable TX and later
234 * enable RX after TX has run for a time
235 *
236 * @return Zero on success, negative on failure
237 */
__bdk_qlm_enable_prbs(bdk_node_t node,int qlm,int prbs,bdk_qlm_direction_t dir)238 int __bdk_qlm_enable_prbs(bdk_node_t node, int qlm, int prbs, bdk_qlm_direction_t dir)
239 {
240 const int NUM_LANES = bdk_qlm_get_lanes(node, qlm);
241 int mode;
242 switch (prbs)
243 {
244 case 31:
245 mode = 1;
246 break;
247 case 23:
248 mode = 2; /* Or 3? */
249 break;
250 case 16:
251 mode = 4;
252 break;
253 case 15:
254 mode = 5;
255 break;
256 case 11:
257 mode = 6;
258 break;
259 case 7:
260 mode = 7;
261 break;
262 default:
263 mode = prbs & 0xff;
264 for (int lane = 0; lane < NUM_LANES; lane++)
265 BDK_CSR_WRITE(node, BDK_GSERX_LANEX_LBERT_PAT_CFG(qlm, lane), prbs >> 8);
266 BDK_TRACE(QLM, "Using mode 0x%x with custom pattern 0x%x\n", mode, prbs >> 8);
267 break;
268 }
269
270 /* For some reason PRBS doesn't work if GSER is configured for PCIe.
271 Disconnect PCIe when we start PRBS */
272 BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
273 if (gserx_cfg.s.pcie)
274 {
275 gserx_cfg.s.pcie = 0;
276 BDK_CSR_WRITE(node, BDK_GSERX_CFG(qlm), gserx_cfg.u);
277 bdk_warn("N%d.QLM%d: Disabling PCIe for PRBS/pattern generation\n", node, qlm);
278 }
279 /* For some reason PRBS doesn't work if GSER is configured for SATA.
280 Disconnect SATA when we start PRBS */
281 if (gserx_cfg.s.sata)
282 {
283 gserx_cfg.s.sata = 0;
284 BDK_CSR_WRITE(node, BDK_GSERX_CFG(qlm), gserx_cfg.u);
285 bdk_warn("N%d.QLM%d: Disabling SATA for PRBS/pattern generation\n", node, qlm);
286 bdk_warn("N%d.QLM%d: SATA PRBS/patterns always run at 6G\n", node, qlm);
287 }
288
289 BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
290 c.s.phy_reset = 0);
291
292 if (dir & BDK_QLM_DIRECTION_TX)
293 {
294 /* Disable first in case already running */
295 for (int lane = 0; lane < NUM_LANES; lane++)
296 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
297 c.s.lbert_pg_en = 0);
298 for (int lane = 0; lane < NUM_LANES; lane++)
299 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
300 c.s.lbert_pg_en = 1; /* Enable generator */
301 c.s.lbert_pg_width = 3; /* 20 bit */
302 c.s.lbert_pg_mode = mode);
303 }
304
305 if (dir & BDK_QLM_DIRECTION_RX)
306 {
307 /* Clear the error counter and Disable the matcher */
308 for (int lane = 0; lane < NUM_LANES; lane++)
309 {
310 prbs_errors[qlm][lane] = 0;
311 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
312 c.s.lbert_pm_en = 0);
313 }
314 for (int lane = 0; lane < NUM_LANES; lane++)
315 {
316 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
317 c.s.lbert_pm_en = 1; /* Enable matcher */
318 c.s.lbert_pm_width = 3; /* 20 bit */
319 c.s.lbert_pm_mode = mode);
320 }
321 /* Tell the matcher to start sync */
322 for (int retry=0; retry < 4; retry++)
323 {
324 for (int lane = 0; lane < NUM_LANES; lane++)
325 {
326 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
327 c.s.lbert_pm_sync_start = 1);
328 }
329 /* Wait 10ms */
330 bdk_wait_usec(10000);
331 }
332 }
333 return 0;
334 }
335
336 /**
337 * Disable PRBS on a QLM
338 *
339 * @param node Node to use in a numa setup
340 * @param qlm QLM to use
341 *
342 * @return Zero on success, negative on failure
343 */
__bdk_qlm_disable_prbs(bdk_node_t node,int qlm)344 int __bdk_qlm_disable_prbs(bdk_node_t node, int qlm)
345 {
346 const int NUM_LANES = bdk_qlm_get_lanes(node, qlm);
347 BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
348 if (phy_ctl.s.phy_reset)
349 return -1;
350
351 for (int lane = 0; lane < NUM_LANES; lane++)
352 {
353 prbs_errors[qlm][lane] = 0;
354 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
355 c.s.lbert_pg_en = 0;
356 c.s.lbert_pm_en = 0);
357 }
358 return 0;
359 }
360
361 /**
362 * Return the number of PRBS errors since PRBS started running
363 *
364 * @param node Node to use in numa setup
365 * @param qlm QLM to use
366 * @param lane Which lane
367 * @param clear Clear counter after return the current value
368 *
369 * @return Number of errors
370 */
__bdk_qlm_get_prbs_errors(bdk_node_t node,int qlm,int lane,int clear)371 uint64_t __bdk_qlm_get_prbs_errors(bdk_node_t node, int qlm, int lane, int clear)
372 {
373 /* Restart synchronization */
374 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
375 c.s.lbert_pm_sync_start = 1);
376 /* This CSR is self clearing per the CSR description, but it doesn't
377 seem to do that. Instead it clears when we trigger sync again */
378 BDK_CSR_INIT(rx, node, BDK_GSERX_LANEX_LBERT_ECNT(qlm, lane));
379 uint64_t errors = rx.s.lbert_err_cnt;
380 if (rx.s.lbert_err_ovbit14)
381 errors <<= 7;
382 prbs_errors[qlm][lane] += errors;
383 uint64_t result = prbs_errors[qlm][lane];
384 if (clear)
385 prbs_errors[qlm][lane] = 0;
386 return result;
387 }
388
389 /**
390 * Inject an error into PRBS
391 *
392 * @param node Node to use in numa setup
393 * @param qlm QLM to use
394 * @param lane Which lane
395 */
__bdk_qlm_inject_prbs_error(bdk_node_t node,int qlm,int lane)396 void __bdk_qlm_inject_prbs_error(bdk_node_t node, int qlm, int lane)
397 {
398 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
399 c.s.lbert_pg_err_insert = 1);
400 }
401
402 /**
403 * Enable shallow loopback on a QLM
404 *
405 * @param node Node to use in a numa setup
406 * @param qlm QLM to use
407 * @param loop Type of loopback. Not all QLMs support all modes
408 *
409 * @return Zero on success, negative on failure
410 */
__bdk_qlm_enable_loop(bdk_node_t node,int qlm,bdk_qlm_loop_t loop)411 int __bdk_qlm_enable_loop(bdk_node_t node, int qlm, bdk_qlm_loop_t loop)
412 {
413 bdk_error("Chip doesn't support shallow QLM loopback\n");
414 return -1;
415 }
416
417 /**
418 * Initialize the QLM mode table
419 *
420 * @param node Node to initialize
421 * @param qlm Which QLM
422 * @param ref_clk Reference clock of the QLM in Hz
423 */
__bdk_qlm_init_mode_table(bdk_node_t node,int qlm,int ref_clk)424 void __bdk_qlm_init_mode_table(bdk_node_t node, int qlm, int ref_clk)
425 {
426 /* The QLM PLLs are controlled by an array of parameters indexed
427 by the QLM mode for each QLM. We need to fill in these tables.
428 Also each lane has some mode parameters, again in a array index
429 by the lane_mode */
430 for (int lane_mode = 0; lane_mode < 12; lane_mode++)
431 {
432 /* The values used below are all from
433 http://mawiki.caveonetworks.com/wiki/78xx/GSER_WEST */
434 BDK_CSR_INIT(pll_mode_0 , node, BDK_GSERX_PLL_PX_MODE_0(qlm, lane_mode));
435 BDK_CSR_INIT(pll_mode_1 , node, BDK_GSERX_PLL_PX_MODE_1(qlm, lane_mode));
436 BDK_CSR_INIT(lane_mode_0, node, BDK_GSERX_LANE_PX_MODE_0(qlm, lane_mode));
437 BDK_CSR_INIT(lane_mode_1, node, BDK_GSERX_LANE_PX_MODE_1(qlm, lane_mode));
438 switch (lane_mode)
439 {
440 case BDK_GSER_LMODE_E_R_25G_REFCLK100:
441 case BDK_GSER_LMODE_E_R_5G_REFCLK100:
442 case BDK_GSER_LMODE_E_R_8G_REFCLK100:
443 /* These modes are used for PCIe where the defaults are
444 correct. Skip programming these */
445 continue;
446 case BDK_GSER_LMODE_E_R_125G_REFCLK15625_KX:
447 pll_mode_0.s.pll_icp = 0x1;
448 pll_mode_0.s.pll_rloop = 0x3;
449 pll_mode_0.s.pll_pcs_div = 0x28;
450
451 pll_mode_1.s.pll_16p5en = 0x1;
452 pll_mode_1.s.pll_cpadj = 0x3;
453 pll_mode_1.s.pll_pcie3en = 0x0;
454 pll_mode_1.s.pll_opr = 0x0;
455 pll_mode_1.s.pll_div = 0x10;
456
457 lane_mode_0.s.ctle = 0x0;
458 lane_mode_0.s.pcie = 0x0;
459 lane_mode_0.s.tx_ldiv = 0x2;
460 lane_mode_0.s.rx_ldiv = 0x2;
461 lane_mode_0.s.srate = 0x0;
462 lane_mode_0.s.tx_mode = 0x3;
463 lane_mode_0.s.rx_mode = 0x3;
464
465 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
466 lane_mode_1.s.vma_mm = 0x1;
467 lane_mode_1.s.cdr_fgain = 0xc;
468 lane_mode_1.s.ph_acc_adj = 0x1e;
469 break;
470 case BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI:
471 pll_mode_0.s.pll_icp = 0x1;
472 pll_mode_0.s.pll_rloop = 0x3;
473 pll_mode_0.s.pll_pcs_div = 0x14;
474
475 pll_mode_1.s.pll_16p5en = 0x1;
476 pll_mode_1.s.pll_cpadj = 0x2;
477 pll_mode_1.s.pll_pcie3en = 0x0;
478 pll_mode_1.s.pll_opr = 0x0;
479 pll_mode_1.s.pll_div = 0x14;
480
481 lane_mode_0.s.ctle = 0x0;
482 lane_mode_0.s.pcie = 0x0;
483 lane_mode_0.s.tx_ldiv = 0x1;
484 lane_mode_0.s.rx_ldiv = 0x1;
485 lane_mode_0.s.srate = 0x0;
486 lane_mode_0.s.tx_mode = 0x3;
487 lane_mode_0.s.rx_mode = 0x3;
488
489 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
490 lane_mode_1.s.vma_mm = 0x1;
491 lane_mode_1.s.cdr_fgain = 0xc;
492 lane_mode_1.s.ph_acc_adj = 0x1e;
493 break;
494 case BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR:
495 pll_mode_0.s.pll_icp = 0x1;
496 pll_mode_0.s.pll_rloop = 0x5;
497 pll_mode_0.s.pll_pcs_div = 0xa;
498
499 pll_mode_1.s.pll_16p5en = 0x1;
500 pll_mode_1.s.pll_cpadj = 0x2;
501 pll_mode_1.s.pll_pcie3en = 0x0;
502 pll_mode_1.s.pll_opr = 0x1;
503 pll_mode_1.s.pll_div = 0x21;
504
505 lane_mode_0.s.ctle = 0x3;
506 lane_mode_0.s.pcie = 0x0;
507 lane_mode_0.s.tx_ldiv = 0x0;
508 lane_mode_0.s.rx_ldiv = 0x0;
509 lane_mode_0.s.srate = 0x0;
510 lane_mode_0.s.tx_mode = 0x3;
511 lane_mode_0.s.rx_mode = 0x3;
512
513 lane_mode_1.s.vma_fine_cfg_sel = 0x1;
514 lane_mode_1.s.vma_mm = 0x0;
515 lane_mode_1.s.cdr_fgain = 0xa;
516 lane_mode_1.s.ph_acc_adj = 0xf;
517 break;
518 case BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII:
519 pll_mode_0.s.pll_icp = 0x1;
520 pll_mode_0.s.pll_rloop = 0x3;
521 pll_mode_0.s.pll_pcs_div = 0x28;
522
523 pll_mode_1.s.pll_16p5en = 0x1;
524 pll_mode_1.s.pll_cpadj = 0x3;
525 pll_mode_1.s.pll_pcie3en = 0x0;
526 pll_mode_1.s.pll_opr = 0x0;
527 pll_mode_1.s.pll_div = 0x10;
528
529 lane_mode_0.s.ctle = 0x0;
530 lane_mode_0.s.pcie = 0x0;
531 lane_mode_0.s.tx_ldiv = 0x2;
532 lane_mode_0.s.rx_ldiv = 0x2;
533 lane_mode_0.s.srate = 0x0;
534 lane_mode_0.s.tx_mode = 0x3;
535 lane_mode_0.s.rx_mode = 0x3;
536
537 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
538 lane_mode_1.s.vma_mm = 0x1;
539 lane_mode_1.s.cdr_fgain = 0xc;
540 lane_mode_1.s.ph_acc_adj = 0x1e;
541 if(ref_clk == REF_100MHZ)
542 {
543 pll_mode_0.s.pll_pcs_div = 0x28;
544 pll_mode_1.s.pll_div = 0x19;
545 pll_mode_1.s.pll_cpadj = 0x2;
546 }
547 break;
548 case BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII:
549 pll_mode_0.s.pll_icp = 0x1; /* Per Scott McIlhenny 5/17/2016 (t81) */
550 pll_mode_0.s.pll_rloop = 0x3;
551 pll_mode_0.s.pll_pcs_div = 0xa;
552
553 pll_mode_1.s.pll_16p5en = 0x0;
554 pll_mode_1.s.pll_cpadj = 0x2;
555 pll_mode_1.s.pll_pcie3en = 0x0;
556 pll_mode_1.s.pll_opr = 0x0;
557 /* QSGMII is a special case. We use the same table entry for
558 100Mhz and 125Mhz clocks as the normal 156Mhz */
559 switch (ref_clk)
560 {
561 case REF_100MHZ:
562 pll_mode_1.s.pll_div = 0x19;
563 break;
564 case REF_125MHZ:
565 pll_mode_1.s.pll_div = 0x14;
566 break;
567 default: /* REF_156MHZ */
568 pll_mode_1.s.pll_div = 0x10;
569 break;
570 }
571
572 lane_mode_0.s.ctle = 0x0;
573 lane_mode_0.s.pcie = 0x0;
574 lane_mode_0.s.tx_ldiv = 0x0;
575 lane_mode_0.s.rx_ldiv = 0x0;
576 lane_mode_0.s.srate = 0x0;
577 lane_mode_0.s.tx_mode = 0x3;
578 lane_mode_0.s.rx_mode = 0x3;
579
580 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
581 lane_mode_1.s.vma_mm = 0x1; /* Per Scott McIlhenny 5/17/2016 (t81) */
582 lane_mode_1.s.cdr_fgain = 0xc;
583 lane_mode_1.s.ph_acc_adj = 0x1e;
584 break;
585 case BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI:
586 pll_mode_0.s.pll_icp = 0x1;
587 pll_mode_0.s.pll_rloop = 0x3;
588 pll_mode_0.s.pll_pcs_div = 0xa;
589
590 pll_mode_1.s.pll_16p5en = 0x0;
591 pll_mode_1.s.pll_cpadj = 0x2;
592 pll_mode_1.s.pll_pcie3en = 0x0;
593 pll_mode_1.s.pll_opr = 0x0;
594 pll_mode_1.s.pll_div = 0x14;
595
596 lane_mode_0.s.ctle = 0x0;
597 lane_mode_0.s.pcie = 0x0;
598 lane_mode_0.s.tx_ldiv = 0x0;
599 lane_mode_0.s.rx_ldiv = 0x0;
600 lane_mode_0.s.srate = 0x0;
601 lane_mode_0.s.tx_mode = 0x3;
602 lane_mode_0.s.rx_mode = 0x3;
603
604 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
605 lane_mode_1.s.vma_mm = 0x0;
606 lane_mode_1.s.cdr_fgain = 0xa;
607 lane_mode_1.s.ph_acc_adj = 0x14;
608 break;
609 case BDK_GSER_LMODE_E_R_25G_REFCLK125:
610 pll_mode_0.s.pll_icp = 0x3;
611 pll_mode_0.s.pll_rloop = 0x3;
612 pll_mode_0.s.pll_pcs_div = 0x5;
613
614 pll_mode_1.s.pll_16p5en = 0x0;
615 pll_mode_1.s.pll_cpadj = 0x1;
616 pll_mode_1.s.pll_pcie3en = 0x0;
617 pll_mode_1.s.pll_opr = 0x0;
618 pll_mode_1.s.pll_div = 0x14;
619
620 lane_mode_0.s.ctle = 0x0;
621 lane_mode_0.s.pcie = 0x1;
622 lane_mode_0.s.tx_ldiv = 0x1;
623 lane_mode_0.s.rx_ldiv = 0x1;
624 lane_mode_0.s.srate = 0x0;
625 lane_mode_0.s.tx_mode = 0x3;
626 lane_mode_0.s.rx_mode = 0x3;
627
628 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
629 lane_mode_1.s.vma_mm = 0x1;
630 lane_mode_1.s.cdr_fgain = 0xa;
631 lane_mode_1.s.ph_acc_adj = 0x14;
632 break;
633 case BDK_GSER_LMODE_E_R_5G_REFCLK125:
634 pll_mode_0.s.pll_icp = 0x3;
635 pll_mode_0.s.pll_rloop = 0x3;
636 pll_mode_0.s.pll_pcs_div = 0xa;
637
638 pll_mode_1.s.pll_16p5en = 0x0;
639 pll_mode_1.s.pll_cpadj = 0x1;
640 pll_mode_1.s.pll_pcie3en = 0x0;
641 pll_mode_1.s.pll_opr = 0x0;
642 pll_mode_1.s.pll_div = 0x14;
643
644 lane_mode_0.s.ctle = 0x0;
645 lane_mode_0.s.pcie = 0x1;
646 lane_mode_0.s.tx_ldiv = 0x0;
647 lane_mode_0.s.rx_ldiv = 0x0;
648 lane_mode_0.s.srate = 0x0;
649 lane_mode_0.s.tx_mode = 0x3;
650 lane_mode_0.s.rx_mode = 0x3;
651
652 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
653 lane_mode_1.s.vma_mm = 0x0;
654 lane_mode_1.s.cdr_fgain = 0xa;
655 lane_mode_1.s.ph_acc_adj = 0x14;
656 break;
657 case BDK_GSER_LMODE_E_R_8G_REFCLK125:
658 pll_mode_0.s.pll_icp = 0x2;
659 pll_mode_0.s.pll_rloop = 0x5;
660 pll_mode_0.s.pll_pcs_div = 0xa;
661
662 pll_mode_1.s.pll_16p5en = 0x0;
663 pll_mode_1.s.pll_cpadj = 0x1;
664 pll_mode_1.s.pll_pcie3en = 0x1;
665 pll_mode_1.s.pll_opr = 0x1;
666 pll_mode_1.s.pll_div = 0x20;
667
668 lane_mode_0.s.ctle = 0x3;
669 lane_mode_0.s.pcie = 0x0;
670 lane_mode_0.s.tx_ldiv = 0x0;
671 lane_mode_0.s.rx_ldiv = 0x0;
672 lane_mode_0.s.srate = 0x0;
673 lane_mode_0.s.tx_mode = 0x2;
674 lane_mode_0.s.rx_mode = 0x2;
675
676 lane_mode_1.s.vma_fine_cfg_sel = 0x0;
677 lane_mode_1.s.vma_mm = 0x0;
678 lane_mode_1.s.cdr_fgain = 0xb;
679 lane_mode_1.s.ph_acc_adj = 0x23;
680 break;
681 }
682 BDK_CSR_WRITE(node, BDK_GSERX_PLL_PX_MODE_0(qlm, lane_mode), pll_mode_0.u);
683 BDK_CSR_WRITE(node, BDK_GSERX_PLL_PX_MODE_1(qlm, lane_mode), pll_mode_1.u);
684 BDK_CSR_WRITE(node, BDK_GSERX_LANE_PX_MODE_0(qlm, lane_mode), lane_mode_0.u);
685 BDK_CSR_WRITE(node, BDK_GSERX_LANE_PX_MODE_1(qlm, lane_mode), lane_mode_1.u);
686 }
687 }
688
689 /**
690 * Given a valid PEM number, return its speed in Gbaud
691 *
692 * @param node Node to use in numa setup
693 * @param pem PEM to get speed of
694 *
695 * @return Speed in Gbaud. Zero if disabled
696 */
__bdk_qlm_get_gbaud_mhz_pem(bdk_node_t node,int pem)697 int __bdk_qlm_get_gbaud_mhz_pem(bdk_node_t node, int pem)
698 {
699 BDK_CSR_INIT(pem_cfg, node, BDK_PEMX_CFG(pem));
700 switch (pem_cfg.cn83xx.md)
701 {
702 case 0: /* Gen 1 */
703 return 2500;
704 case 1: /* Gen 2 */
705 return 5000;
706 case 2: /* Gen 3 */
707 return 8000;
708 default:
709 return 0;
710 }
711 }
712
713 /**
714 * Get the speed of a QLM using its LMODE. This can't be used on PCIe QLMs.
715 *
716 * @param node Node to use in numa setup
717 * @param qlm Which QLM
718 *
719 * @return QLM speed on Gbaud
720 */
__bdk_qlm_get_gbaud_mhz_lmode(bdk_node_t node,int qlm)721 int __bdk_qlm_get_gbaud_mhz_lmode(bdk_node_t node, int qlm)
722 {
723 /* QLM is not in PCIe, assume LMODE is good enough for determining
724 the speed */
725 BDK_CSR_INIT(lane_mode, node, BDK_GSERX_LANE_MODE(qlm));
726 switch (lane_mode.s.lmode)
727 {
728 case BDK_GSER_LMODE_E_R_25G_REFCLK100:
729 return 2500;
730 case BDK_GSER_LMODE_E_R_5G_REFCLK100:
731 return 5000;
732 case BDK_GSER_LMODE_E_R_8G_REFCLK100:
733 return 8000;
734 case BDK_GSER_LMODE_E_R_125G_REFCLK15625_KX:
735 return 1250;
736 case BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI:
737 return 3125;
738 case BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR:
739 return 10312;
740 case BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII:
741 return 1250;
742 case BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII:
743 return 5000;
744 case BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI:
745 return 6250;
746 case BDK_GSER_LMODE_E_R_25G_REFCLK125:
747 return 2500;
748 case BDK_GSER_LMODE_E_R_5G_REFCLK125:
749 return 5000;
750 case BDK_GSER_LMODE_E_R_8G_REFCLK125:
751 return 8000;
752 default:
753 return 0;
754 }
755 }
756
757 /**
758 * Converts a measured reference clock to a likely ideal value. Rounds
759 * clock speed to the nearest REF_*Mhz define.
760 *
761 * @param node Node to use in numa setup
762 * @param qlm Which QLM
763 * @param measured_hz
764 * Measured value
765 *
766 * @return Value exactly matching a define
767 */
__bdk_qlm_round_refclock(bdk_node_t node,int qlm,int measured_hz)768 int __bdk_qlm_round_refclock(bdk_node_t node, int qlm, int measured_hz)
769 {
770 int ref_clk;
771 if ((measured_hz > REF_100MHZ - REF_100MHZ / 10) && (measured_hz < REF_100MHZ + REF_100MHZ / 10))
772 {
773 ref_clk = REF_100MHZ;
774 }
775 else if ((measured_hz > REF_125MHZ - REF_125MHZ / 10) && (measured_hz < REF_125MHZ + REF_125MHZ / 10))
776 {
777 ref_clk = REF_125MHZ;
778 }
779 else if ((measured_hz > REF_156MHZ - REF_156MHZ / 10) && (measured_hz < REF_156MHZ + REF_156MHZ / 10))
780 {
781 ref_clk = REF_156MHZ;
782 }
783 else if (measured_hz < 1000000)
784 {
785 ref_clk = 0; /* Used for disabled QLMs */
786 }
787 else
788 {
789 ref_clk = measured_hz;
790 bdk_error("N%d.QLM%d: Unexpected reference clock speed of %d Mhz\n", node, qlm, measured_hz / 1000000);
791 }
792 return ref_clk;
793 }
794
795 /**
796 * TWSI reads from the MCU randomly timeout. Retry a few times on
797 * failure to try and recover
798 *
799 * @param node Node to use in a Numa setup. Can be an exact ID or a special
800 * value.
801 * @param twsi_id which TWSI bus to use
802 * @param dev_addr Device address (7 bit)
803 * @param internal_addr
804 * Internal address. Can be 0, 1 or 2 bytes in width
805 * @param num_bytes Number of data bytes to read (1-4)
806 * @param ia_width_bytes
807 * Internal address size in bytes (0, 1, or 2)
808 *
809 * @return Read data, or -1 on failure
810 */
mcu_read(bdk_node_t node,int twsi_id,uint8_t dev_addr,uint16_t internal_addr,int num_bytes,int ia_width_bytes)811 static int64_t mcu_read(bdk_node_t node, int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes)
812 {
813 int read_tries = 0;
814 int64_t result;
815 do
816 {
817 result = bdk_twsix_read_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes);
818 read_tries++;
819 if (result < 0)
820 {
821 BDK_TRACE(QLM, "Timeout %d reading from MCU\n", read_tries);
822 bdk_wait_usec(100000);
823 }
824 } while ((result < 0) && (read_tries < 3));
825 return result;
826 }
827
__bdk_qlm_set_reference(bdk_node_t node,int qlm,int ref_clk)828 static void __bdk_qlm_set_reference(bdk_node_t node, int qlm, int ref_clk)
829 {
830 int use_clock;
831 if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) || CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
832 {
833 switch (ref_clk)
834 {
835 case REF_156MHZ:
836 use_clock = 0; /* Common clock 0 */
837 BDK_TRACE(QLM, "Setting N%d.QLM%d to use common clock 0\n", node, qlm);
838 break;
839 case REF_100MHZ:
840 use_clock = 1; /* Common clock 1 */
841 BDK_TRACE(QLM, "Setting N%d.QLM%d to use common clock 1\n", node, qlm);
842 break;
843 default:
844 use_clock = 2; /* External clock */
845 BDK_TRACE(QLM, "Setting N%d.QLM%d to use external clock\n", node, qlm);
846 break;
847 }
848 }
849 else
850 {
851 bdk_error("Update __bdk_qlm_set_reference() for qlm auto config of this chip\n");
852 return;
853 }
854 BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
855 c.s.com_clk_sel = (use_clock != 2);
856 c.s.use_com1 = (use_clock == 1));
857 }
858
859 /**
860 * For Cavium EVB and EBB board, query the MCU to determine the QLM setup. Applying
861 * any configuration found.
862 *
863 * @param node Node to configure
864 *
865 * @return Zero on success, negative on failure
866 */
bdk_qlm_mcu_auto_config(bdk_node_t node)867 int bdk_qlm_mcu_auto_config(bdk_node_t node)
868 {
869 const int MCU_TWSI_BUS = 0;
870 const int MCU_TWSI_ADDRESS = 0x60;
871 int64_t data;
872
873 /* Check the two magic number bytes the MCU should return */
874 data = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x00, 1, 1);
875 if (data != 0xa5)
876 {
877 printf("QLM Config: MCU not found, skipping auto configuration\n");
878 return -1;
879 }
880 data = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x01, 1, 1);
881 if (data != 0x5a)
882 {
883 bdk_error("QLM Config: MCU magic number incorrect\n");
884 return -1;
885 }
886
887 /* Read the MCU version */
888 int mcu_major = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x02, 1, 1);
889 int mcu_minor = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x03, 1, 1);
890 BDK_TRACE(QLM, "MCU version %d.%d\n", mcu_major, mcu_minor);
891 if ((mcu_major < 2) || ((mcu_major == 2) && (mcu_minor < 30)))
892 {
893 bdk_error("QLM Config: Unexpected MCU version %d.%d\n", mcu_major, mcu_minor);
894 return -1;
895 }
896
897 /* Find out how many lanes the MCU thinks are available */
898 int lanes = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x16, 1, 1);
899 BDK_TRACE(QLM, "MCU says board has %d lanes\n", lanes);
900 int correct_lanes = 0;
901 if (cavium_is_altpkg(CAVIUM_CN88XX))
902 correct_lanes = 22;
903 else if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
904 correct_lanes = 32;
905 else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
906 correct_lanes = 22;
907 else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
908 correct_lanes = 8;
909 if (lanes != correct_lanes)
910 {
911 bdk_error("QLM Config: Unexpected number of lanes (%d) from MCU\n", lanes);
912 return -1;
913 }
914
915 int lane = 0;
916 int qlm = 0;
917 while (lane < lanes)
918 {
919 int write_status;
920 int width;
921 int mode;
922 int speed;
923 int refclk;
924 /* TWSI reads from the MCU randomly timeout. Retry a few times on
925 failure to try and recover */
926 int read_tries = 0;
927 do
928 {
929 read_tries++;
930 if (read_tries > 3)
931 {
932 bdk_error("QLM Config: Timeouts reading from MCU\n");
933 return -1;
934 }
935 /* Space request out 20ms */
936 bdk_wait_usec(20000);
937 /* Select the lane we are interested in */
938 write_status = bdk_twsix_write_ia(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x16, 1, 1, lane);
939 /* Space request out 20ms */
940 bdk_wait_usec(20000);
941 /* Get the mode */
942 width = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x17, 1, 1);
943 mode = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x18, 2, 1);
944 speed = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x19, 2, 1);
945 refclk = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x1a, 1, 1);
946 } while ((write_status < 0) || (width < 0) || (mode < 0) || (speed < 0) || (refclk < 0));
947
948 BDK_TRACE(QLM, "MCU lane %d, width %d, mode 0x%x, speed 0x%x, ref 0x%x\n",
949 lane, width, mode, speed, refclk);
950 if ((width != 0) && (width != 1) && (width != 2) && (width != 4) && (width != 8))
951 {
952 bdk_error("QLM Config: Unexpected interface width (%d) from MCU\n", width);
953 return -1;
954 }
955 /* MCU reports a width of 0 for unconfigured QLMs. It reports a width
956 of 1 for some combinations on CN80XX, and two on others. Convert
957 either 0 or 1 to the actual width, or 2 for CN80XX. Yuck */
958 if ((width == 0) || (width == 1))
959 {
960 if (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2))
961 width = 2;
962 else
963 width = bdk_qlm_get_lanes(node, qlm);
964 }
965 bdk_qlm_modes_t qlm_mode;
966 int qlm_speed = (speed >> 8) * 1000 + (speed & 0xff) * 1000 / 256;
967 int use_ref = 0;
968 bdk_qlm_mode_flags_t qlm_flags = 0;
969 if (mode < 0x4000)
970 {
971 switch (mode)
972 {
973 case 0x0000: /* No Configuration */
974 qlm_mode = BDK_QLM_MODE_DISABLED;
975 break;
976 case 0x0101: /* PCIe Host */
977 qlm_mode = (width == 8) ? BDK_QLM_MODE_PCIE_1X8 :
978 (width == 4) ? BDK_QLM_MODE_PCIE_1X4 :
979 BDK_QLM_MODE_PCIE_1X2;
980 use_ref = REF_100MHZ;
981 break;
982 case 0x0102: /* PCIe Endpoint */
983 qlm_mode = (width == 8) ? BDK_QLM_MODE_PCIE_1X8 :
984 (width == 4) ? BDK_QLM_MODE_PCIE_1X4 :
985 BDK_QLM_MODE_PCIE_1X2;
986 qlm_flags = BDK_QLM_MODE_FLAG_ENDPOINT;
987 use_ref = 0; /* Use the external reference for EP mode */
988 break;
989 case 0x1000: /* SGMII */
990 qlm_mode = (width == 4) ? BDK_QLM_MODE_SGMII_4X1 :
991 (width == 2) ? BDK_QLM_MODE_SGMII_2X1 :
992 BDK_QLM_MODE_SGMII_1X1;
993 use_ref = REF_156MHZ;
994 /* CN80XX parts on EBBs use phy port 2 for SGMII, while QSGMII
995 uses the correct port. Fix this for DLM1 and DLM3 */
996 if (cavium_is_altpkg(CAVIUM_CN81XX))
997 {
998 int bgx = (qlm == 3) ? 1 : 0;
999 uint64_t phy = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, 0, bgx, 2);
1000 bdk_config_set_int(phy, BDK_CONFIG_PHY_ADDRESS, 0, bgx, 1);
1001 }
1002 break;
1003 case 0x1100: /* QSGMII */
1004 qlm_mode = BDK_QLM_MODE_QSGMII_4X1;
1005 use_ref = REF_100MHZ;
1006 break;
1007 case 0x2000: /* XAUI */
1008 qlm_mode = BDK_QLM_MODE_XAUI_1X4;
1009 use_ref = REF_156MHZ;
1010 break;
1011 case 0x2100: /* RXAUI */
1012 qlm_mode = (width == 2) ? BDK_QLM_MODE_RXAUI_1X2 : BDK_QLM_MODE_RXAUI_2X2;
1013 use_ref = REF_156MHZ;
1014 break;
1015 case 0x2200: /* DXAUI */
1016 qlm_mode = BDK_QLM_MODE_XAUI_1X4;
1017 use_ref = REF_156MHZ;
1018 break;
1019 case 0x3001: /* Interlaken */
1020 qlm_mode = BDK_QLM_MODE_ILK;
1021 use_ref = REF_156MHZ;
1022 break;
1023 default:
1024 bdk_error("QLM Config: Unexpected interface mode (0x%x) from MCU\n", mode);
1025 qlm_mode = BDK_QLM_MODE_DISABLED;
1026 break;
1027 }
1028 }
1029 else
1030 {
1031 switch (mode)
1032 {
1033 case 0x4000: /* SATA */
1034 qlm_mode = (width == 2) ? BDK_QLM_MODE_SATA_2X1 : BDK_QLM_MODE_SATA_4X1;
1035 use_ref = REF_100MHZ;
1036 break;
1037 case 0x5001: /* XFI */
1038 qlm_mode = (width == 4) ? BDK_QLM_MODE_XFI_4X1 :
1039 (width == 2) ? BDK_QLM_MODE_XFI_2X1 :
1040 BDK_QLM_MODE_XFI_1X1;
1041 use_ref = REF_156MHZ;
1042 break;
1043 case 0x5002: /* 10G-KR */
1044 qlm_mode = (width == 4) ? BDK_QLM_MODE_10G_KR_4X1 :
1045 (width == 2) ? BDK_QLM_MODE_10G_KR_2X1 :
1046 BDK_QLM_MODE_10G_KR_1X1;
1047 use_ref = REF_156MHZ;
1048 break;
1049 case 0x6001: /* XLAUI */
1050 qlm_mode = BDK_QLM_MODE_XLAUI_1X4;
1051 use_ref = REF_156MHZ;
1052 break;
1053 case 0x6002: /* 40G-KR4 */
1054 qlm_mode = BDK_QLM_MODE_40G_KR4_1X4;
1055 use_ref = REF_156MHZ;
1056 break;
1057 default:
1058 bdk_error("QLM Config: Unexpected interface mode (0x%x) from MCU\n", mode);
1059 qlm_mode = BDK_QLM_MODE_DISABLED;
1060 break;
1061 }
1062 }
1063 lane += width;
1064 do
1065 {
1066 int internal_qlm = qlm;
1067 /* Alternate package parts have different QLM numbers for internal
1068 versus external. The MCU uses the external numbers */
1069 if (cavium_is_altpkg(CAVIUM_CN88XX))
1070 {
1071 switch (qlm)
1072 {
1073 case 0: /* QLM0 -> QLM4 */
1074 internal_qlm = 4;
1075 break;
1076 case 1: /* QLM1 -> QLM5 */
1077 internal_qlm = 5;
1078 break;
1079 case 2: /* QLM2 -> QLM0 */
1080 internal_qlm = 0;
1081 break;
1082 case 3: /* QLM3 -> QLM1 */
1083 internal_qlm = 1;
1084 break;
1085 case 4: /* DLM4 -> QLM2 */
1086 internal_qlm = 2;
1087 break;
1088 case 5: /* DLM5 -> QLM6 */
1089 internal_qlm = 6;
1090 break;
1091 case 6: /* DLM6 -> QLM7 */
1092 internal_qlm = 7;
1093 break;
1094 default:
1095 bdk_error("Invalid external QLM%d from MCU\n", qlm);
1096 return -1;
1097 }
1098 }
1099 if (qlm_flags & BDK_QLM_MODE_FLAG_ENDPOINT)
1100 {
1101 BDK_TRACE(QLM, "Skipping N%d.QLM%d mode %s(%d), speed %d, flags 0x%x (EP should already be setup)\n",
1102 node, internal_qlm, bdk_qlm_mode_tostring(qlm_mode), qlm_mode, qlm_speed, qlm_flags);
1103 }
1104 else
1105 {
1106 BDK_TRACE(QLM, "Setting N%d.QLM%d mode %s(%d), speed %d, flags 0x%x\n",
1107 node, internal_qlm, bdk_qlm_mode_tostring(qlm_mode), qlm_mode, qlm_speed, qlm_flags);
1108 /* Set the reference clock for this QLM */
1109 __bdk_qlm_set_reference(node, internal_qlm, use_ref);
1110 if (bdk_qlm_set_mode(node, internal_qlm, qlm_mode, qlm_speed, qlm_flags))
1111 return -1;
1112 }
1113 int num_lanes = bdk_qlm_get_lanes(node, internal_qlm);
1114 /* CN86XX looks like two lanes each for DLM4-7 */
1115 if (cavium_is_altpkg(CAVIUM_CN88XX) && (qlm >= 4))
1116 num_lanes = 2;
1117 if (qlm_mode == BDK_QLM_MODE_PCIE_1X8)
1118 {
1119 /* PCIe x8 is a special case as the QLM config function
1120 actually configures both QLMs in one go */
1121 qlm++;
1122 width -= 8;
1123 }
1124 else if ((qlm_mode == BDK_QLM_MODE_PCIE_1X4) && (width > num_lanes))
1125 {
1126 /* PCIe x4 is a special case as the QLM config function
1127 actually configures both QLMs in one go */
1128 qlm++;
1129 width -= 4;
1130 }
1131 else if (width >= num_lanes)
1132 {
1133 if (num_lanes == 1)
1134 width -= 2; /* Special case for CN80XX */
1135 else
1136 width -= num_lanes;
1137 }
1138 else
1139 width = 0;
1140 qlm++;
1141 } while (width > 0);
1142 }
1143 return 0;
1144 }
1145
1146 /**
1147 * Display the current settings of a QLM lane
1148 *
1149 * @param node Node the QLM is on
1150 * @param qlm QLM to display
1151 * @param qlm_lane Lane to use
1152 * @param show_tx Display TX parameters
1153 * @param show_rx Display RX parameters
1154 */
bdk_qlm_display_settings(bdk_node_t node,int qlm,int qlm_lane,bool show_tx,bool show_rx)1155 void bdk_qlm_display_settings(bdk_node_t node, int qlm, int qlm_lane, bool show_tx, bool show_rx)
1156 {
1157 const char *dir_label[] = {"Hold", "Inc", "Dec", "Hold"};
1158
1159 uint64_t rx_aeq_out_0 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_0(qlm, qlm_lane));
1160 uint64_t rx_aeq_out_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_1(qlm, qlm_lane));
1161 uint64_t rx_aeq_out_2 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_2(qlm, qlm_lane));
1162 uint64_t rx_vma_status_0 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_VMA_STATUS_0(qlm, qlm_lane));
1163 uint64_t rx_vma_status_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_VMA_STATUS_1(qlm, qlm_lane));
1164 uint64_t sds_pin_mon_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_SDS_PIN_MON_1(qlm, qlm_lane));
1165 uint64_t sds_pin_mon_2 = BDK_CSR_READ(node, BDK_GSERX_LANEX_SDS_PIN_MON_2(qlm, qlm_lane));
1166 uint64_t br_rxx_eer = BDK_CSR_READ(node, BDK_GSERX_BR_RXX_EER(qlm, qlm_lane));
1167
1168 printf("N%d.QLM%d Lane %d:\n", node, qlm, qlm_lane);
1169 if (show_rx)
1170 {
1171 printf(" DFE Tap 1: %llu, Tap 2: %lld, Tap 3: %lld, Tap 4: %lld, Tap 5: %lld\n",
1172 bdk_extract(rx_aeq_out_1, 0, 5),
1173 bdk_extract_smag(rx_aeq_out_1, 5, 9),
1174 bdk_extract_smag(rx_aeq_out_1, 10, 14),
1175 bdk_extract_smag(rx_aeq_out_0, 0, 4),
1176 bdk_extract_smag(rx_aeq_out_0, 5, 9));
1177 printf(" Pre-CTLE Gain: %llu, Post-CTLE Gain: %llu, CTLE Peak: %llu, CTLE Pole: %llu\n",
1178 bdk_extract(rx_aeq_out_2, 4, 4),
1179 bdk_extract(rx_aeq_out_2, 0, 4),
1180 bdk_extract(rx_vma_status_0, 2, 4),
1181 bdk_extract(rx_vma_status_0, 0, 2));
1182 printf(" RX Equalization Tx Directions Hints TXPRE: %s, TXMAIN: %s, TXPOST: %s, Figure of Merit: %llu\n",
1183 dir_label[bdk_extract(br_rxx_eer, 0, 2)],
1184 dir_label[bdk_extract(br_rxx_eer, 2, 2)],
1185 dir_label[bdk_extract(br_rxx_eer, 4, 2)],
1186 bdk_extract(br_rxx_eer, 6, 8));
1187 }
1188 if (show_tx)
1189 {
1190 printf(" TX Swing: %llu, Pre-emphasis Pre-cursor: %llu, Post-cursor: %llu\n",
1191 bdk_extract(sds_pin_mon_1, 1, 5),
1192 bdk_extract(sds_pin_mon_2, 0, 4),
1193 bdk_extract(sds_pin_mon_2, 4, 5));
1194 printf(" TX Boost Enable: %llu, TX Turbo Mode: %llu\n",
1195 bdk_extract(sds_pin_mon_2, 10, 1),
1196 bdk_extract(sds_pin_mon_2, 9, 1));
1197 }
1198 printf(" Training-done: %llu\n",
1199 bdk_extract(rx_vma_status_1, 7, 1));
1200 }
1201
1202 /**
1203 * Perform RX equalization on a QLM
1204 *
1205 * @param node Node the QLM is on
1206 * @param qlm QLM to perform RX equalization on
1207 * @param qlm_lane Lane to use, or -1 for all lanes
1208 *
1209 * @return Zero on success, negative if any lane failed RX equalization
1210 */
__bdk_qlm_rx_equalization(bdk_node_t node,int qlm,int qlm_lane)1211 int __bdk_qlm_rx_equalization(bdk_node_t node, int qlm, int qlm_lane)
1212 {
1213 /* Don't touch QLMs is reset or powered down */
1214 BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
1215 if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset)
1216 return -1;
1217 /* Don't run on PCIe links */
1218 if (bdk_qlm_get_mode(node, qlm) <= BDK_QLM_MODE_PCIE_1X8)
1219 return -1;
1220
1221 int fail = 0; /* Bitmask of lanes that failed CDR Lock or Eltrical Idle check */
1222 int pending = 0; /* Bitmask of lanes that we're waiting for */
1223 int MAX_LANES = bdk_qlm_get_lanes(node, qlm);
1224
1225 BDK_TRACE(QLM, "N%d.QLM%d: Starting RX equalization on lane %d\n", node, qlm, qlm_lane);
1226 for (int lane = 0; lane < MAX_LANES; lane++)
1227 {
1228 /* Skip lanes we don't care about */
1229 if ((qlm_lane != -1) && (qlm_lane != lane))
1230 continue;
1231 /* Check that the lane has completed CDR lock */
1232 BDK_CSR_INIT(eie_detsts, node, BDK_GSERX_RX_EIE_DETSTS(qlm));
1233 if (((1 << lane) & eie_detsts.s.cdrlock) == 0)
1234 {
1235 /* Mark bad so we skip this lane below */
1236 fail |= 1 << lane;
1237 continue;
1238 }
1239 /* Enable software control */
1240 BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_CTL(qlm, lane),
1241 c.s.rxt_swm = 1);
1242 /* Clear the completion flag and initiate a new request */
1243 BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_EER(qlm, lane),
1244 c.s.rxt_esv = 0;
1245 c.s.rxt_eer = 1);
1246 /* Remember that we have to wait for this lane */
1247 pending |= 1 << lane;
1248 }
1249
1250 /* Timing a few of these over XFI on CN73XX, each takes 21-23ms. XLAUI
1251 was about the same time. DXAUI and RXAUI both took 2-3ms. Put the
1252 timeout at 250ms, which is roughly 10x my measurements. */
1253 uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + bdk_clock_get_rate(node, BDK_CLOCK_TIME) / 4;
1254 while (pending)
1255 {
1256 for (int lane = 0; lane < MAX_LANES; lane++)
1257 {
1258 int lane_mask = 1 << lane;
1259 /* Only check lanes that are pending */
1260 if (!(pending & lane_mask))
1261 continue;
1262 /* Read the registers for checking Electrical Idle / CDR lock and
1263 the status of the RX equalization */
1264 BDK_CSR_INIT(eie_detsts, node, BDK_GSERX_RX_EIE_DETSTS(qlm));
1265 BDK_CSR_INIT(gserx_br_rxx_eer, node, BDK_GSERX_BR_RXX_EER(qlm, lane));
1266 /* Mark failure if lane entered Electrical Idle or lost CDR Lock. The
1267 bit for the lane will have cleared in either EIESTS or CDRLOCK */
1268 if (!(eie_detsts.s.eiests & eie_detsts.s.cdrlock & lane_mask))
1269 {
1270 fail |= lane_mask;
1271 pending &= ~lane_mask;
1272 }
1273 else if (gserx_br_rxx_eer.s.rxt_esv)
1274 {
1275 /* Clear pending if RX equalization finished */
1276 pending &= ~lane_mask;
1277 }
1278 }
1279 /* Break out of the loop on timeout */
1280 if (bdk_clock_get_count(BDK_CLOCK_TIME) > timeout)
1281 break;
1282 }
1283
1284 /* Cleanup and report status */
1285 for (int lane = 0; lane < MAX_LANES; lane++)
1286 {
1287 /* Skip lanes we don't care about */
1288 if ((qlm_lane != -1) && (qlm_lane != lane))
1289 continue;
1290 int lane_mask = 1 << lane;
1291 /* Get the final RX equalization status */
1292 BDK_CSR_INIT(gserx_br_rxx_eer, node, BDK_GSERX_BR_RXX_EER(qlm, lane));
1293 /* Disable software control */
1294 BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_CTL(qlm, lane),
1295 c.s.rxt_swm = 0);
1296 /* Report status */
1297 if (fail & lane_mask)
1298 {
1299 BDK_TRACE(QLM, "N%d.QLM%d: Lane %d RX equalization lost CDR Lock or entered Electrical Idle\n", node, qlm, lane);
1300 }
1301 else if ((pending & lane_mask) || !gserx_br_rxx_eer.s.rxt_esv)
1302 {
1303 BDK_TRACE(QLM, "N%d.QLM%d: Lane %d RX equalization timeout\n", node, qlm, lane);
1304 fail |= 1 << lane;
1305 }
1306 else
1307 {
1308 bdk_qlm_display_settings(node, qlm, lane, false, true);
1309 }
1310 }
1311
1312 return (fail) ? -1 : 0;
1313 }
1314
1315 /**
1316 * Configure the TX tuning parameters for a QLM lane. The tuning parameters can
1317 * be specified as -1 to maintain their current value
1318 *
1319 * @param node Node to configure
1320 * @param qlm QLM to configure
1321 * @param lane Lane to configure
1322 * @param tx_swing Transmit swing (coef 0) Range 0-31
1323 * @param tx_pre Pre cursor emphasis (Coef -1). Range 0-15
1324 * @param tx_post Post cursor emphasis (Coef +1). Range 0-31
1325 * @param tx_gain Transmit gain. Range 0-7
1326 * @param tx_vboost Transmit voltage boost. Range 0-1
1327 *
1328 * @return Zero on success, negative on failure
1329 */
__bdk_qlm_tune_lane_tx(bdk_node_t node,int qlm,int lane,int tx_swing,int tx_pre,int tx_post,int tx_gain,int tx_vboost)1330 int __bdk_qlm_tune_lane_tx(bdk_node_t node, int qlm, int lane, int tx_swing, int tx_pre, int tx_post, int tx_gain, int tx_vboost)
1331 {
1332 /* Check tuning constraints */
1333 if ((tx_swing < -1) || (tx_swing > 25))
1334 {
1335 bdk_error("N%d.QLM%d: Lane %d: Invalid TX_SWING(%d)\n", node, qlm, lane, tx_swing);
1336 return -1;
1337 }
1338 if ((tx_pre < -1) || (tx_pre > 10))
1339 {
1340 bdk_error("N%d.QLM%d: Lane %d: Invalid TX_PRE(%d)\n", node, qlm, lane, tx_pre);
1341 return -1;
1342 }
1343 if ((tx_post < -1) || (tx_post > 15))
1344 {
1345 bdk_error("N%d.QLM%d: Lane %d: Invalid TX_POST(%d)\n", node, qlm, lane, tx_post);
1346 return -1;
1347 }
1348 if ((tx_pre >= 0) && (tx_post >= 0) && (tx_swing >= 0) && (tx_pre + tx_post - tx_swing > 2))
1349 {
1350 bdk_error("N%d.QLM%d: Lane %d: TX_PRE(%d) + TX_POST(%d) - TX_SWING(%d) must be less than or equal to 2\n", node, qlm, lane, tx_pre, tx_post, tx_swing);
1351 return -1;
1352 }
1353 if ((tx_pre >= 0) && (tx_post >= 0) && (tx_swing >= 0) && (tx_pre + tx_post + tx_swing > 35))
1354 {
1355 bdk_error("N%d.QLM%d: Lane %d: TX_PRE(%d) + TX_POST(%d) + TX_SWING(%d) must be less than or equal to 35\n", node, qlm, lane, tx_pre, tx_post, tx_swing);
1356 return -1;
1357 }
1358
1359 if ((tx_gain < -1) || (tx_gain > 7))
1360 {
1361 bdk_error("N%d.QLM%d: Lane %d: Invalid TX_GAIN(%d). TX_GAIN must be between 0 and 7\n", node, qlm, lane, tx_gain);
1362 return -1;
1363 }
1364
1365 if ((tx_vboost < -1) || (tx_vboost > 1))
1366 {
1367 bdk_error("N%d.QLM%d: Lane %d: Invalid TX_VBOOST(%d). TX_VBOOST must be 0 or 1.\n", node, qlm, lane, tx_vboost);
1368 return -1;
1369 }
1370
1371 if ((tx_pre != -1) && (tx_post == -1))
1372 {
1373 BDK_CSR_INIT(emphasis, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane));
1374 tx_post = emphasis.s.cfg_tx_premptap >> 4;
1375 }
1376
1377 if ((tx_post != -1) && (tx_pre == -1))
1378 {
1379 BDK_CSR_INIT(emphasis, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane));
1380 tx_pre = emphasis.s.cfg_tx_premptap & 0xf;
1381 }
1382
1383 BDK_TRACE(QLM, "N%d.QLM%d: Lane %d: TX_SWING=%d, TX_PRE=%d, TX_POST=%d, TX_GAIN=%d, TX_VBOOST=%d\n",
1384 node, qlm, lane, tx_swing, tx_pre, tx_post, tx_gain, tx_vboost);
1385
1386 /* Manual Tx Swing and Tx Equalization Programming Steps */
1387
1388 /* 1) Enable Tx swing and Tx emphasis overrides */
1389 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_1(qlm, lane),
1390 c.s.tx_swing_ovrrd_en = (tx_swing != -1);
1391 c.s.tx_premptap_ovrrd_val = (tx_pre != -1) && (tx_post != -1);
1392 c.s.tx_vboost_en_ovrrd_en = (tx_vboost != -1)); /* Vboost override */
1393 /* 2) Program the Tx swing and Tx emphasis Pre-cursor and Post-cursor values */
1394 if (tx_swing != -1)
1395 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_0(qlm, lane),
1396 c.s.cfg_tx_swing = tx_swing);
1397 if ((tx_pre != -1) && (tx_post != -1))
1398 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane),
1399 c.s.cfg_tx_premptap = (tx_post << 4) | tx_pre);
1400 /* Apply TX gain settings */
1401 if (tx_gain != -1)
1402 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_3(qlm, lane),
1403 c.s.pcs_sds_tx_gain = tx_gain);
1404 /* Apply TX vboost settings */
1405 if (tx_vboost != -1)
1406 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_3(qlm, lane),
1407 c.s.cfg_tx_vboost_en = tx_vboost);
1408 /* 3) Program override for the Tx coefficient request */
1409 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_0(qlm, lane),
1410 if (((tx_pre != -1) && (tx_post != -1)) || (tx_swing != -1))
1411 c.s.cfg_tx_coeff_req_ovrrd_val = 1;
1412 if (tx_vboost != -1)
1413 c.s.cfg_tx_vboost_en_ovrrd_val = 1;
1414 );
1415 /* 4) Enable the Tx coefficient request override enable */
1416 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
1417 if (((tx_pre != -1) && (tx_post != -1)) || (tx_swing != -1))
1418 c.s.cfg_tx_coeff_req_ovrrd_en = 1;
1419 if (tx_vboost != -1)
1420 c.s.cfg_tx_vboost_en_ovrrd_en = 1
1421 );
1422 /* 5) Issue a Control Interface Configuration Override request to start
1423 the Tx equalizer Optimization cycle which applies the new Tx swing
1424 and equalization settings */
1425 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
1426 c.s.ctlifc_ovrrd_req = 1);
1427
1428 /* 6) Prepare for a subsequent Tx swing and Tx equalization adjustment:
1429 a) Disable the Tx coefficient request override enable */
1430 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
1431 c.s.cfg_tx_coeff_req_ovrrd_en = 0);
1432 /* b) Issue a Control Interface Configuration Override request */
1433 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
1434 c.s.ctlifc_ovrrd_req = 1);
1435 /* The new Tx swing and Pre-cursor and Post-cursor settings will now take
1436 effect. */
1437 return 0;
1438 }
1439
1440 /**
1441 * Some QLM speeds need to override the default tuning parameters
1442 *
1443 * @param node Node to use in a Numa setup
1444 * @param qlm QLM to configure
1445 * @param mode Desired mode
1446 * @param baud_mhz Desired speed
1447 */
__bdk_qlm_tune(bdk_node_t node,int qlm,bdk_qlm_modes_t mode,int baud_mhz)1448 void __bdk_qlm_tune(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz)
1449 {
1450 /* Note: This function is not called for CCPI. For CCPI tuning, see
1451 bdk-init-nz-node.c */
1452 /* Tuning parameters override the KR training. Don't apply them for KR links */
1453 switch (mode)
1454 {
1455 case BDK_QLM_MODE_10G_KR_1X1:
1456 case BDK_QLM_MODE_10G_KR_2X1:
1457 case BDK_QLM_MODE_10G_KR_4X1:
1458 case BDK_QLM_MODE_40G_KR4_1X4:
1459 return;
1460 case BDK_QLM_MODE_PCIE_1X1:
1461 case BDK_QLM_MODE_PCIE_2X1:
1462 case BDK_QLM_MODE_PCIE_1X2:
1463 case BDK_QLM_MODE_PCIE_1X4:
1464 case BDK_QLM_MODE_PCIE_1X8:
1465 /* Don't tune PCIe Gen3 as it has its own builtin, similar to KR */
1466 if (baud_mhz > 5000)
1467 return;
1468 break;
1469 default:
1470 break;
1471 }
1472
1473 /* We're apply tuning for all lanes on this QLM */
1474 int num_lanes = bdk_qlm_get_lanes(node, qlm);
1475 for (int lane = 0; lane < num_lanes; lane++)
1476 {
1477 /* TX Swing: First read any board specific setting from the environment */
1478 int swing = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_SWING, node, qlm, lane);
1479 /* If no setting, use hard coded generic defaults */
1480 if (swing == -1)
1481 {
1482 if (baud_mhz == 6250)
1483 {
1484 /* Email from Brendan Metzner about RXAUI around 2/7/2016 */
1485 swing = 0x12;
1486 }
1487 else if (baud_mhz == 10312)
1488 {
1489 /* From lab measurements of EBB8800 at 10.3125G */
1490 swing = 0xd;
1491 }
1492 }
1493
1494 /* TX Premptap: First read any board specific setting from the environment */
1495 int premptap = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_PREMPTAP, node, qlm, lane);
1496 /* If no setting, use hard coded generic defaults */
1497 if (premptap == -1)
1498 {
1499 if (baud_mhz == 6250)
1500 {
1501 /* From lab measurements of EBB8800 at 6.25G */
1502 premptap = 0xa0;
1503 }
1504 else if (baud_mhz == 10312)
1505 {
1506 /* From lab measurements of EBB8800 at 10.3125G */
1507 premptap = 0xd0;
1508 }
1509 }
1510
1511 int tx_pre = (premptap == -1) ? -1 : premptap & 0xf;
1512 int tx_post = (premptap == -1) ? -1 : premptap >> 4;
1513 int gain = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_GAIN, node, qlm, lane);
1514 int vboost = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_VBOOST, node, qlm, lane);
1515
1516 __bdk_qlm_tune_lane_tx(node, qlm, lane, swing, tx_pre, tx_post, gain, vboost);
1517
1518 /* Email from Brendan Metzner about RXAUI around 2/7/2016 suggested the
1519 following setting for RXAUI at 6.25G with both PHY or cable. I'm
1520 applying it to all lanes running at 6.25G */
1521 if (baud_mhz == 6250)
1522 {
1523 /* This is changing the Q/QB error sampler 0 threshold from 0xD
1524 to 0xF */
1525 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CFG_4(qlm, lane),
1526 c.s.cfg_rx_errdet_ctrl = 0xcf6f);
1527 }
1528 }
1529 }
1530
1531 /**
1532 * Disables DFE for the specified QLM lane(s).
1533 * This function should only be called for low-loss channels.
1534 *
1535 * @param node Node to configure
1536 * @param qlm QLM to configure
1537 * @param lane Lane to configure, or -1 for all lanes
1538 */
__bdk_qlm_dfe_disable(int node,int qlm,int lane)1539 void __bdk_qlm_dfe_disable(int node, int qlm, int lane)
1540 {
1541 int num_lanes = bdk_qlm_get_lanes(node, qlm);
1542 int l;
1543
1544 for (l = 0; l < num_lanes; l++) {
1545 if ((lane != -1) && (lane != l))
1546 continue;
1547 /* 1. Write GSERX_LANEx_RX_LOOP_CTRL = 0x0270 (var "loop_ctrl" with bits 8 & 1 cleared).
1548 * bit<1> dfe_en_byp = 1'b0 */
1549 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_LOOP_CTRL(qlm, l),
1550 c.s.cfg_rx_lctrl = c.s.cfg_rx_lctrl & 0x3fd);
1551
1552 /* 2. Write GSERX_LANEx_RX_VALBBD_CTRL_1 = 0x0000 (var "ctrl1" with all bits cleared)
1553 * bits<14:11> CFG_RX_DFE_C3_MVAL = 4'b0000
1554 * bit<10> CFG_RX_DFE_C3_MSGN = 1'b0
1555 * bits<9:6> CFG_RX_DFE_C2_MVAL = 4'b0000
1556 * bit<5> CFG_RX_DFE_C2_MSGN = 1'b0
1557 * bits<4:1> CFG_RX_DFE_C1_MVAL = 5'b0000
1558 * bits<0> CFG_RX_DFE_C1_MSGN = 1'b0 */
1559 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_1(qlm, l),
1560 c.s.dfe_c3_mval = 0;
1561 c.s.dfe_c3_msgn = 0;
1562 c.s.dfe_c2_mval = 0;
1563 c.s.dfe_c2_msgn = 0;
1564 c.s.dfe_c1_mval = 0;
1565 c.s.dfe_c1_msgn = 0);
1566
1567 /* 3. Write GSERX_LANEx_RX_VALBBD_CTRL_0 = 0x2400 (var "ctrl0" with following bits set/cleared)
1568 * bits<11:10> CFG_RX_DFE_GAIN = 0x1
1569 * bits<9:6> CFG_RX_DFE_C5_MVAL = 4'b0000
1570 * bit<5> CFG_RX_DFE_C5_MSGN = 1'b0
1571 * bits<4:1> CFG_RX_DFE_C4_MVAL = 4'b0000
1572 * bit<0> CFG_RX_DFE_C4_MSGN = 1'b0 */
1573 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_0(qlm, l),
1574 c.s.dfe_gain = 0x1;
1575 c.s.dfe_c5_mval = 0;
1576 c.s.dfe_c5_msgn = 0;
1577 c.s.dfe_c4_mval = 0;
1578 c.s.dfe_c4_msgn = 0);
1579
1580 /* 4. Write GSER(0..13)_LANE(0..3)_RX_VALBBD_CTRL_2 = 0x003F //enable DFE tap overrides
1581 * bit<5> dfe_ovrd_en = 1
1582 * bit<4> dfe_c5_ovrd_val = 1
1583 * bit<3> dfe_c4_ovrd_val = 1
1584 * bit<2> dfe_c3_ovrd_val = 1
1585 * bit<1> dfe_c2_ovrd_val = 1
1586 * bit<0> dfe_c1_ovrd_val = 1
1587 */
1588 BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_2(qlm, l),
1589 c.s.dfe_ovrd_en = 0x1;
1590 c.s.dfe_c5_ovrd_val = 0x1;
1591 c.s.dfe_c4_ovrd_val = 0x1;
1592 c.s.dfe_c3_ovrd_val = 0x1;
1593 c.s.dfe_c2_ovrd_val = 0x1;
1594 c.s.dfe_c1_ovrd_val = 0x1);
1595
1596 }
1597 }
1598
1599 /**
1600 * Check if a specific lane is using KR training. This is used by low level GSER
1601 * code to remember which QLMs and lanes need to support KR training for BGX. The
1602 * hardware doesn't have a bit set aside to record this, so we repurpose the
1603 * register GSERX_SCRATCH.
1604 *
1605 * @param node Node to check
1606 * @param qlm QLM to check
1607 * @param lane Lane to check
1608 *
1609 * @return True if this lane uses KR with BGX, false otherwise
1610 */
__bdk_qlm_is_lane_kr(bdk_node_t node,int qlm,int lane)1611 bool __bdk_qlm_is_lane_kr(bdk_node_t node, int qlm, int lane)
1612 {
1613 uint64_t mask = BDK_CSR_READ(node, BDK_GSERX_SCRATCH(qlm));
1614 return 1 & (mask >> lane);
1615 }
1616
1617 /**
1618 * Set if a specific lane is using KR training. This is used by low level GSER
1619 * code to remember which QLMs and lanes need to support KR training for BGX. The
1620 * hardware doesn't have a bit set aside to record this, so we repurpose the
1621 * register GSERX_SCRATCH.
1622 *
1623 * @param node Node to set
1624 * @param qlm QLM to set
1625 * @param lane Lane to set
1626 * @param is_kr KR (true) or XFI/XLAUI (false)
1627 */
__bdk_qlm_set_lane_kr(bdk_node_t node,int qlm,int lane,bool is_kr)1628 void __bdk_qlm_set_lane_kr(bdk_node_t node, int qlm, int lane, bool is_kr)
1629 {
1630 uint64_t mask = BDK_CSR_READ(node, BDK_GSERX_SCRATCH(qlm));
1631 if (is_kr)
1632 mask |= 1 << lane;
1633 else
1634 mask &= ~(1 << lane);
1635 BDK_CSR_WRITE(node, BDK_GSERX_SCRATCH(qlm), mask);
1636 }
1637