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 <string.h>
41 #include "libbdk-hal/if/bdk-if.h"
42 #include "libbdk-hal/bdk-qlm.h"
43 #include "libbdk-hal/bdk-utils.h"
44 #include "libbdk-boot/bdk-boot-qlm.h"
45 #include "libbdk-hal/bdk-config.h"
46 #include "libbdk-hal/bdk-twsi.h"
47
boot_init_qlm_clk(void)48 static void boot_init_qlm_clk(void)
49 {
50 /* Setup reference clocks */
51 for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
52 {
53 if (!bdk_numa_exists(n))
54 continue;
55
56 int num_qlms = bdk_qlm_get_num(n);
57
58 BDK_TRACE(INIT, "Initializing QLM clocks on Node %d\n", n);
59 for (int qlm = 0; qlm < num_qlms; qlm++)
60 {
61 bdk_qlm_clock_t clk = bdk_config_get_int(BDK_CONFIG_QLM_CLK, n, qlm);
62 if (BDK_QLM_CLK_LAST == clk) /* no entry */
63 continue;
64
65 if (clk > BDK_QLM_CLK_LAST)
66 {
67 bdk_warn("Invalid clock source %d for QLM%d on node %d. Not configuring.\n",
68 clk, qlm, n);
69 continue;
70 }
71
72 if (0 != bdk_qlm_set_clock(n, qlm, clk))
73 {
74 bdk_error("Error setting clock source %d for QLM%d on node %d. Ignoring.\n",
75 clk, qlm, n);
76 }
77 }
78 }
79 }
80
81 /**
82 * Given a node and DLM/QLM, return the possible BGX lanes connected to it. This
83 * is needed to determine which PHY address to use for SFP/SFP+ detection.
84 *
85 * @param node Node the DLM/QLM is on
86 * @param qlm DLM/QLM to find the BGX for
87 * @param bgx Output: The BGX instance number, or -1 on failure
88 * @param bgx_lane_mask
89 * Output: Which BGX indexes may be connected to this port
90 */
find_bgx(int node,int qlm,int * bgx,int * bgx_lane_mask)91 static void find_bgx(int node, int qlm, int *bgx, int *bgx_lane_mask)
92 {
93 *bgx = -1;
94 *bgx_lane_mask = 0;
95
96 if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
97 {
98 switch (qlm)
99 {
100 case 0: /* BGX0 -> QLM0 */
101 case 1: /* BGX1 -> QLM1 */
102 *bgx = qlm;
103 *bgx_lane_mask = 0xf;
104 return;
105 default:
106 BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
107 return;
108 }
109 }
110 else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
111 {
112 switch (qlm)
113 {
114 case 2: /* BGX0 -> QLM2 */
115 *bgx = 0;
116 *bgx_lane_mask = 0xf;
117 return;
118 case 3: /* BGX1 -> QLM3 */
119 *bgx = 1;
120 *bgx_lane_mask = 0xf;
121 return;
122 case 4: /* BGX3 -> DLM4 */
123 *bgx = 3;
124 *bgx_lane_mask = 0x3;
125 return;
126 case 5: /* BGX2 -> DLM5 */
127 *bgx = 2;
128 *bgx_lane_mask = 0x3;
129 return;
130 case 6: /* BGX2 -> DLM6 */
131 *bgx = 2;
132 *bgx_lane_mask = 0xc;
133 return;
134 default:
135 BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
136 return;
137 }
138 }
139 else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
140 {
141 switch (qlm)
142 {
143 case 0: /* BGX0 -> DLM0 */
144 *bgx = 0;
145 *bgx_lane_mask = 0x3;
146 return;
147 case 1: /* BGX0 -> DLM1 */
148 *bgx = 0;
149 *bgx_lane_mask = 0xc;
150 return;
151 case 2: /* BGX1 -> DLM2 */
152 *bgx = 1;
153 *bgx_lane_mask = 0x3;
154 return;
155 case 3: /* BGX1 -> DLM3 */
156 *bgx = 1;
157 *bgx_lane_mask = 0xc;
158 return;
159 default:
160 BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm);
161 return;
162 }
163 }
164 else
165 bdk_error("N%d.QLM%d: Unsupported chip, update %s()\n", node, qlm, __func__);
166 }
167
168 /**
169 * Determine the DLM/QLM mode based on a SFP/SFP+ connected to the port. Note that
170 * the CN8XXX parts can't control mode per lane, so all SFP/SFP+ on a DLM/QLM must
171 * be the same mode. This code is sloppy about finding the BGX PHY for the DLM/QLM
172 * because not all lanes may be used.
173 *
174 * @param node Node to determine mode for
175 * @param qlm DLM/QLM the SFP/SFP+ is connected to
176 *
177 * @return QLM mode or -1 on failure
178 */
init_sfp(int node,int qlm)179 static int init_sfp(int node, int qlm)
180 {
181 int mode = BDK_QLM_MODE_XFI_4X1; /* Default to XFI if detection fails */
182 int bgx = -1;
183 int bgx_lane_mask = 0;
184
185 find_bgx(node, qlm, &bgx, &bgx_lane_mask);
186 if (bgx == -1)
187 return mode;
188
189 BDK_TRACE(INIT, "N%d.QLM%d: Checking for SFP/SFP+\n", node, qlm);
190
191 for (int index = 0; index < 4; index++)
192 {
193 /* Skip BGX indexes that aren't applicable */
194 if ((bgx_lane_mask & (1 << index)) == 0)
195 continue;
196 /* Lookup the PHY address for this BGX port */
197 int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index);
198 /* SFP/SFP+ are connected with TWSI, so only check ports with
199 PHYs connected with TWSI */
200 if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI)
201 continue;
202
203 /* For TWSI:
204 Bits[31:24]: Node ID, 0xff for device node
205 Bits[23:16]: TWSI internal address width in bytes (0-2)
206 Bits[15:12]: 2=TWSI
207 Bits[11:8]: TWSI bus number
208 Bits[7:0]: TWSI address */
209 int n = (phy_addr >> 24) & 0xff;
210 int twsi_ia_width = (phy_addr >> 16) & 0xff;
211 int twsi_bus = (phy_addr >> 8) & 0xf;
212 int twsi_addr = 0x50; /* From SFP spec */
213 if (n == 0xff)
214 n = node;
215
216 /* Read bytes 0-3 from eeprom. Note read is big endian, so byte 0 is
217 bits 31:24 in the result */
218 int64_t eeprom_00_03 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 4, twsi_ia_width);
219 if (eeprom_00_03 == -1)
220 {
221 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
222 continue;
223 }
224 int64_t eeprom_04_07 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 4, 4, twsi_ia_width);
225 if (eeprom_04_07 == -1)
226 {
227 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
228 continue;
229 }
230 int64_t eeprom_08_11 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 8, 4, twsi_ia_width);
231 if (eeprom_08_11 == -1)
232 {
233 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
234 continue;
235 }
236 int64_t eeprom_12 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 12, 1, twsi_ia_width);
237 if (eeprom_12 == -1)
238 {
239 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index);
240 continue;
241 }
242
243 /* Byte 0: Identifier, should be 0x03 for SFP/SFP+
244 0x03 = SFP of SFP+
245 0x0c = QSFP
246 0x0d = QSFP+ */
247 if (bdk_extract(eeprom_00_03, 24, 8) != 0x03)
248 {
249 /* Byte 0 of eeprom should be 0x03 for SFP/SFP+ */
250 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ not detected\n", node, qlm, bgx, index);
251 continue;
252 }
253 /* Byte 1: Extended Identifier, should be 0x04 */
254 if (bdk_extract(eeprom_00_03, 16, 8) != 0x04)
255 {
256 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ incorrect extended identifier\n", node, qlm, bgx, index);
257 continue;
258 }
259 /* Byte 2: Connector
260 Value Description of connector
261 00h Unknown or unspecified
262 01h SC
263 02h Fibre Channel Style 1 copper connector
264 03h Fibre Channel Style 2 copper connector
265 04h BNC/TNC
266 05h Fibre Channel coaxial headers
267 06h FiberJack
268 07h LC
269 08h MT-RJ
270 09h MU
271 0Ah SG
272 0Bh Optical pigtail
273 0Ch MPO Parallel Optic
274 0Dh-1Fh Reserved, Unallocated
275 20h HSSDC II
276 21h Copper Pigtail
277 22h RJ45
278 23h-7Fh Reserved, Unallocated
279 80-FFh Vendor specific */
280 bool isOptical = false;
281 switch (bdk_extract(eeprom_00_03, 8, 8))
282 {
283 case 0x01: /* SC - Short channel */
284 case 0x07: /* LC - Long channel */
285 case 0x0B: /* Optical pigtail */
286 isOptical = true;
287 break;
288 }
289 BDK_TRACE(INIT, "N%d.QLM%d: SFP/SFP+ eeprom Bytes[0:3] 0x%0llx, Bytes[4:7] 0x%08llx, [8:11] 0x%08llx [12] 0x%02llx\n",
290 node, qlm, eeprom_00_03, eeprom_04_07, eeprom_08_11, eeprom_12);
291 /* Byte 3: Transceiver info first byte. See comments below */
292 /* Byte 3, bits 4-7 correspond to 10G Ethernet speeds */
293 /* 10G Ethernet Compliance Codes
294 Byte 3[7] 10G BASE-ER (Fiber - Extended Reach)
295 Byte 3[6] 10G BASE-LRM (Fiber - Long reach multi-mode)
296 Byte 3[5] 10G BASE-LR (Fiber - Long reach)
297 Byte 3[4] 10G BASE-SR (Fiber - Short reach) */
298 bool isXFI = bdk_extract(eeprom_00_03, 0, 8) != 0;
299 /* Byte 6, bits 0-7 correspond to Gigabit Ethernet speeds */
300 /* Gigabit Ethernet Compliance Codes
301 Byte 6[7] BASE-PX
302 Byte 6[6] BASE-BX10
303 Byte 6[5] 100BASE-FX
304 Byte 6[4] 100BASE-LX/LX10 (Fiber)
305 Byte 6[3] 1000BASE-T (Twisted pair)
306 Byte 6[2] 1000BASE-CX (Shielded balanced copper)
307 Byte 6[1] 1000BASE-LX (Fiber)
308 Byte 6[0] 1000BASE-SX (Fiber) */
309 bool isSGMII = bdk_extract(eeprom_04_07, 8, 8) != 0;
310 /* Byte 12 is the nominal bit rate, units of 100 MBits/sec. */
311 int bit_rate = eeprom_12 * 100;
312 if (bit_rate)
313 {
314 BDK_TRACE(INIT, "N%d.QLM%d: Nominal bit rate %d MBits/sec\n",
315 node, qlm, bit_rate);
316 isXFI = (bit_rate >= 10000);
317 isSGMII = (bit_rate <= 2500);
318 }
319
320 if (isXFI)
321 {
322 mode = BDK_QLM_MODE_XFI_4X1;
323 if (isOptical)
324 BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Optical\n", node, qlm);
325 else
326 BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Copper\n", node, qlm);
327 }
328 else if (isSGMII)
329 {
330 mode = BDK_QLM_MODE_SGMII_4X1;
331 if (isOptical)
332 {
333 /* This should be 1000BASE-X, gigabit over fiber */
334 BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Optical\n", node, qlm);
335 }
336 else /* This should be SGMII, gigabit over copper */
337 BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Copper\n", node, qlm);
338 }
339 }
340 return mode;
341 }
342
343 /**
344 * Determine the DLM/QLM mode based on a QSFP/QSFP+ connected to
345 * the port. This code is sloppy about finding the BGX PHY for
346 * the DLM/QLM because not all lanes may be used.
347 *
348 * @param node Node to determine mode for
349 * @param qlm DLM/QLM the SFP/SFP+ is connected to
350 *
351 * @return QLM mode or -1 on failure
352 */
init_qsfp(int node,int qlm)353 static int init_qsfp(int node, int qlm)
354 {
355 int mode = BDK_QLM_MODE_XLAUI_1X4; /* Default to XLAUI if detection fails */
356 int bgx = -1;
357 int bgx_lane_mask = 0;
358
359 find_bgx(node, qlm, &bgx, &bgx_lane_mask);
360 if (bgx == -1)
361 return mode;
362
363 BDK_TRACE(INIT, "N%d.QLM%d: Checking for QSFP/QSFP+\n", node, qlm);
364 int index = 0;
365
366 /* Lookup the PHY address for this BGX port */
367 int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index);
368 /* QSFP/QSFP+ are connected with TWSI, so only check ports with
369 PHYs connected with TWSI */
370 if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI)
371 return mode;
372
373 /* For TWSI:
374 Bits[31:24]: Node ID, 0xff for device node
375 Bits[23:16]: TWSI internal address width in bytes (0-2)
376 Bits[15:12]: 2=TWSI
377 Bits[11:8]: TWSI bus number
378 Bits[7:0]: TWSI address */
379 int n = (phy_addr >> 24) & 0xff;
380 int twsi_ia_width = (phy_addr >> 16) & 0xff;
381 int twsi_bus = (phy_addr >> 8) & 0xf;
382 int twsi_addr = 0x50; /* From SFP spec */
383 if (n == 0xff)
384 n = node;
385
386 /* Byte 0: Identifier, should be 0x03 for SFP/SFP+
387 0x03 = SFP of SFP+
388 0x0c = QSFP
389 0x0d = QSFP+ */
390 int64_t eeprom_00 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 1, twsi_ia_width);
391 switch (eeprom_00)
392 {
393 case 0x03:
394 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ contains a SFP+\n", node, qlm, bgx);
395 mode = init_sfp(node, qlm);
396 break;
397 case 0x0c:
398 case 0x0d:
399 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d Found a QSFP/QSFP+, assuming 40G\n", node, qlm, bgx);
400 mode = BDK_QLM_MODE_XLAUI_1X4;
401 break;
402 default:
403 BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ not detected\n", node, qlm, bgx);
404 break;
405 }
406 return mode;
407 }
408
boot_init_qlm_mode(void)409 static void boot_init_qlm_mode(void)
410 {
411 /* Check if QLM autoconfig is requested */
412 int qlm_auto = bdk_config_get_int(BDK_CONFIG_QLM_AUTO_CONFIG);
413 if (qlm_auto)
414 {
415 /* Auto configuration of QLMs
416 */
417 for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
418 {
419 if (bdk_numa_exists(n))
420 {
421 BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
422 bdk_qlm_auto_config(n);
423 }
424 }
425 }
426 /*
427 * Check if QLM autoconfig from DIP switch settings is requested
428 */
429 else if (bdk_config_get_int(BDK_CONFIG_QLM_DIP_AUTO_CONFIG))
430 {
431 BDK_TRACE(INIT, "Reading DIP Switch settings for QLM Auto configuration\n");
432
433 /* Auto configuration of QLMs
434 */
435 for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
436 {
437 if (bdk_numa_exists(n))
438 {
439 BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
440 if (bdk_qlm_dip_auto_config(n))
441 bdk_error("QLM Auto configuration failed!\n");
442 }
443 }
444
445 }
446 else
447 {
448 /* Initialize the QLMs based on configuration file settings
449 */
450
451 boot_init_qlm_clk();
452
453 for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++)
454 {
455 if (!bdk_numa_exists(n))
456 continue;
457
458 int num_qlms = bdk_qlm_get_num(n);
459
460 BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n);
461 for (int qlm = 0; qlm < num_qlms; qlm++)
462 {
463 const char *cfg_val;
464
465 cfg_val = bdk_config_get_str(BDK_CONFIG_QLM_MODE, n, qlm);
466 if (!cfg_val)
467 continue;
468
469 int mode;
470 int freq;
471 /* Check for special token telling us to configure the QLM
472 based on the SFP/SFP+/QSFP/QSFP+ plugged into the system. */
473 if ((strcmp(cfg_val, "SFP+") == 0) || (strcmp(cfg_val, "QSFP+") == 0))
474 {
475 if (strcmp(cfg_val, "SFP+") == 0)
476 mode = init_sfp(n, qlm);
477 else
478 mode = init_qsfp(n, qlm);
479
480 if (mode == BDK_QLM_MODE_SGMII_4X1)
481 freq = 1250;
482 else
483 freq = 10321;
484 }
485 else
486 {
487 mode = bdk_qlm_cfg_string_to_mode(cfg_val);
488 freq = bdk_config_get_int(BDK_CONFIG_QLM_FREQ, n, qlm);
489 }
490 if (-1 == mode)
491 {
492 bdk_error("Invalid QLM mode string '%s' for QLM%d on node %d. "
493 "Not configuring.\n", cfg_val, qlm, n);
494 continue;
495 }
496 if (-1 == freq)
497 {
498 bdk_error("No frequency setting for QLM%d on node %d. "
499 "Not configuring.\n", qlm, n);
500 continue;
501 }
502
503 bdk_qlm_set_mode(n, qlm, mode, freq, 0);
504 }
505 }
506 }
507 }
508
509 /**
510 * Configure QLM on all nodes as part of booting
511 */
bdk_boot_qlm(void)512 void bdk_boot_qlm(void)
513 {
514 boot_init_qlm_mode();
515 }
516