1 /*
2 * qsfp.c: Implements SFF-8636 based QSFP+/QSFP28 Diagnostics Memory map.
3 *
4 * Copyright 2010 Solarflare Communications Inc.
5 * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
6 * Copyright (C) 2014 Cumulus networks Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Freeoftware Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Vidya Ravipati <vidya@cumulusnetworks.com>
14 * This implementation is loosely based on current SFP parser
15 * and SFF-8636 spec Rev 2.7 (ftp://ftp.seagate.com/pub/sff/SFF-8636.PDF)
16 * by SFF Committee.
17 */
18
19 /*
20 * Description:
21 * a) The register/memory layout is up to 5 128 byte pages defined by
22 * a "pages valid" register and switched via a "page select"
23 * register. Memory of 256 bytes can be memory mapped at a time
24 * according to SFF 8636.
25 * b) SFF 8636 based 640 bytes memory layout is presented for parser
26 *
27 * SFF 8636 based QSFP Memory Map
28 *
29 * 2-Wire Serial Address: 1010000x
30 *
31 * Lower Page 00h (128 bytes)
32 * ======================
33 * | |
34 * |Page Select Byte(127)|
35 * ======================
36 * |
37 * V
38 * ----------------------------------------
39 * | | | |
40 * V V V V
41 * ---------- ---------- --------- ------------
42 * | Upper | | Upper | | Upper | | Upper |
43 * | Page 00h | | Page 01h | | Page 02h | | Page 03h |
44 * | | |(Optional)| |(Optional)| | (Optional) |
45 * | | | | | | | |
46 * | | | | | | | |
47 * | ID | | AST | | User | | For |
48 * | Fields | | Table | | EEPROM | | Cable |
49 * | | | | | Data | | Assemblies |
50 * | | | | | | | |
51 * | | | | | | | |
52 * ----------- ----------- ---------- --------------
53 *
54 *
55 **/
56 #include <stdio.h>
57 #include <math.h>
58 #include "internal.h"
59 #include "sff-common.h"
60 #include "qsfp.h"
61
62 #define MAX_DESC_SIZE 42
63
64 static struct sff8636_aw_flags {
65 const char *str; /* Human-readable string, null at the end */
66 int offset; /* A2-relative address offset */
67 __u8 value; /* Alarm is on if (offset & value) != 0. */
68 } sff8636_aw_flags[] = {
69 { "Laser bias current high alarm (Chan 1)",
70 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HALARM) },
71 { "Laser bias current low alarm (Chan 1)",
72 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LALARM) },
73 { "Laser bias current high warning (Chan 1)",
74 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HWARN) },
75 { "Laser bias current low warning (Chan 1)",
76 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LWARN) },
77
78 { "Laser bias current high alarm (Chan 2)",
79 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HALARM) },
80 { "Laser bias current low alarm (Chan 2)",
81 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LALARM) },
82 { "Laser bias current high warning (Chan 2)",
83 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HWARN) },
84 { "Laser bias current low warning (Chan 2)",
85 SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LWARN) },
86
87 { "Laser bias current high alarm (Chan 3)",
88 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HALARM) },
89 { "Laser bias current low alarm (Chan 3)",
90 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LALARM) },
91 { "Laser bias current high warning (Chan 3)",
92 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HWARN) },
93 { "Laser bias current low warning (Chan 3)",
94 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LWARN) },
95
96 { "Laser bias current high alarm (Chan 4)",
97 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HALARM) },
98 { "Laser bias current low alarm (Chan 4)",
99 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LALARM) },
100 { "Laser bias current high warning (Chan 4)",
101 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HWARN) },
102 { "Laser bias current low warning (Chan 4)",
103 SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LWARN) },
104
105 { "Module temperature high alarm",
106 SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HALARM_STATUS) },
107 { "Module temperature low alarm",
108 SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LALARM_STATUS) },
109 { "Module temperature high warning",
110 SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HWARN_STATUS) },
111 { "Module temperature low warning",
112 SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LWARN_STATUS) },
113
114 { "Module voltage high alarm",
115 SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HALARM_STATUS) },
116 { "Module voltage low alarm",
117 SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LALARM_STATUS) },
118 { "Module voltage high warning",
119 SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HWARN_STATUS) },
120 { "Module voltage low warning",
121 SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LWARN_STATUS) },
122
123 { "Laser tx power high alarm (Channel 1)",
124 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HALARM) },
125 { "Laser tx power low alarm (Channel 1)",
126 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LALARM) },
127 { "Laser tx power high warning (Channel 1)",
128 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HWARN) },
129 { "Laser tx power low warning (Channel 1)",
130 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LWARN) },
131
132 { "Laser tx power high alarm (Channel 2)",
133 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HALARM) },
134 { "Laser tx power low alarm (Channel 2)",
135 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LALARM) },
136 { "Laser tx power high warning (Channel 2)",
137 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HWARN) },
138 { "Laser tx power low warning (Channel 2)",
139 SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LWARN) },
140
141 { "Laser tx power high alarm (Channel 3)",
142 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HALARM) },
143 { "Laser tx power low alarm (Channel 3)",
144 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LALARM) },
145 { "Laser tx power high warning (Channel 3)",
146 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HWARN) },
147 { "Laser tx power low warning (Channel 3)",
148 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LWARN) },
149
150 { "Laser tx power high alarm (Channel 4)",
151 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HALARM) },
152 { "Laser tx power low alarm (Channel 4)",
153 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LALARM) },
154 { "Laser tx power high warning (Channel 4)",
155 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HWARN) },
156 { "Laser tx power low warning (Channel 4)",
157 SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LWARN) },
158
159 { "Laser rx power high alarm (Channel 1)",
160 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HALARM) },
161 { "Laser rx power low alarm (Channel 1)",
162 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LALARM) },
163 { "Laser rx power high warning (Channel 1)",
164 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HWARN) },
165 { "Laser rx power low warning (Channel 1)",
166 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LWARN) },
167
168 { "Laser rx power high alarm (Channel 2)",
169 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HALARM) },
170 { "Laser rx power low alarm (Channel 2)",
171 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LALARM) },
172 { "Laser rx power high warning (Channel 2)",
173 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HWARN) },
174 { "Laser rx power low warning (Channel 2)",
175 SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LWARN) },
176
177 { "Laser rx power high alarm (Channel 3)",
178 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HALARM) },
179 { "Laser rx power low alarm (Channel 3)",
180 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LALARM) },
181 { "Laser rx power high warning (Channel 3)",
182 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HWARN) },
183 { "Laser rx power low warning (Channel 3)",
184 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LWARN) },
185
186 { "Laser rx power high alarm (Channel 4)",
187 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HALARM) },
188 { "Laser rx power low alarm (Channel 4)",
189 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LALARM) },
190 { "Laser rx power high warning (Channel 4)",
191 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HWARN) },
192 { "Laser rx power low warning (Channel 4)",
193 SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LWARN) },
194
195 { NULL, 0, 0 },
196 };
197
sff8636_show_identifier(const __u8 * id)198 static void sff8636_show_identifier(const __u8 *id)
199 {
200 sff8024_show_identifier(id, SFF8636_ID_OFFSET);
201 }
202
sff8636_show_ext_identifier(const __u8 * id)203 static void sff8636_show_ext_identifier(const __u8 *id)
204 {
205 printf("\t%-41s : 0x%02x\n", "Extended identifier",
206 id[SFF8636_EXT_ID_OFFSET]);
207
208 static const char *pfx =
209 "\tExtended identifier description :";
210
211 switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_PWR_CLASS_MASK) {
212 case SFF8636_EXT_ID_PWR_CLASS_1:
213 printf("%s 1.5W max. Power consumption\n", pfx);
214 break;
215 case SFF8636_EXT_ID_PWR_CLASS_2:
216 printf("%s 2.0W max. Power consumption\n", pfx);
217 break;
218 case SFF8636_EXT_ID_PWR_CLASS_3:
219 printf("%s 2.5W max. Power consumption\n", pfx);
220 break;
221 case SFF8636_EXT_ID_PWR_CLASS_4:
222 printf("%s 3.5W max. Power consumption\n", pfx);
223 break;
224 }
225
226 if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK)
227 printf("%s CDR present in TX,", pfx);
228 else
229 printf("%s No CDR in TX,", pfx);
230
231 if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK)
232 printf(" CDR present in RX\n");
233 else
234 printf(" No CDR in RX\n");
235
236 switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_EPWR_CLASS_MASK) {
237 case SFF8636_EXT_ID_PWR_CLASS_LEGACY:
238 printf("%s", pfx);
239 break;
240 case SFF8636_EXT_ID_PWR_CLASS_5:
241 printf("%s 4.0W max. Power consumption,", pfx);
242 break;
243 case SFF8636_EXT_ID_PWR_CLASS_6:
244 printf("%s 4.5W max. Power consumption, ", pfx);
245 break;
246 case SFF8636_EXT_ID_PWR_CLASS_7:
247 printf("%s 5.0W max. Power consumption, ", pfx);
248 break;
249 }
250 if (id[SFF8636_PWR_MODE_OFFSET] & SFF8636_HIGH_PWR_ENABLE)
251 printf(" High Power Class (> 3.5 W) enabled\n");
252 else
253 printf(" High Power Class (> 3.5 W) not enabled\n");
254 }
255
sff8636_show_connector(const __u8 * id)256 static void sff8636_show_connector(const __u8 *id)
257 {
258 sff8024_show_connector(id, SFF8636_CTOR_OFFSET);
259 }
260
sff8636_show_transceiver(const __u8 * id)261 static void sff8636_show_transceiver(const __u8 *id)
262 {
263 static const char *pfx =
264 "\tTransceiver type :";
265
266 printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
267 "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
268 "Transceiver codes",
269 id[SFF8636_ETHERNET_COMP_OFFSET],
270 id[SFF8636_SONET_COMP_OFFSET],
271 id[SFF8636_SAS_COMP_OFFSET],
272 id[SFF8636_GIGE_COMP_OFFSET],
273 id[SFF8636_FC_LEN_OFFSET],
274 id[SFF8636_FC_TECH_OFFSET],
275 id[SFF8636_FC_TRANS_MEDIA_OFFSET],
276 id[SFF8636_FC_SPEED_OFFSET]);
277
278 /* 10G/40G Ethernet Compliance Codes */
279 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LRM)
280 printf("%s 10G Ethernet: 10G Base-LRM\n", pfx);
281 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LR)
282 printf("%s 10G Ethernet: 10G Base-LR\n", pfx);
283 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_SR)
284 printf("%s 10G Ethernet: 10G Base-SR\n", pfx);
285 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_CR4)
286 printf("%s 40G Ethernet: 40G Base-CR4\n", pfx);
287 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_SR4)
288 printf("%s 40G Ethernet: 40G Base-SR4\n", pfx);
289 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_LR4)
290 printf("%s 40G Ethernet: 40G Base-LR4\n", pfx);
291 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_ACTIVE)
292 printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx);
293 /* Extended Specification Compliance Codes from SFF-8024 */
294 if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_RSRVD) {
295 switch (id[SFF8636_OPTION_1_OFFSET]) {
296 case SFF8636_ETHERNET_UNSPECIFIED:
297 printf("%s (reserved or unknown)\n", pfx);
298 break;
299 case SFF8636_ETHERNET_100G_AOC:
300 printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)\n",
301 pfx);
302 break;
303 case SFF8636_ETHERNET_100G_SR4:
304 printf("%s 100G Ethernet: 100G Base-SR4 or 25GBase-SR\n",
305 pfx);
306 break;
307 case SFF8636_ETHERNET_100G_LR4:
308 printf("%s 100G Ethernet: 100G Base-LR4\n", pfx);
309 break;
310 case SFF8636_ETHERNET_100G_ER4:
311 printf("%s 100G Ethernet: 100G Base-ER4\n", pfx);
312 break;
313 case SFF8636_ETHERNET_100G_SR10:
314 printf("%s 100G Ethernet: 100G Base-SR10\n", pfx);
315 break;
316 case SFF8636_ETHERNET_100G_CWDM4_FEC:
317 printf("%s 100G Ethernet: 100G CWDM4 MSA with FEC\n", pfx);
318 break;
319 case SFF8636_ETHERNET_100G_PSM4:
320 printf("%s 100G Ethernet: 100G PSM4 Parallel SMF\n", pfx);
321 break;
322 case SFF8636_ETHERNET_100G_ACC:
323 printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)\n",
324 pfx);
325 break;
326 case SFF8636_ETHERNET_100G_CWDM4_NO_FEC:
327 printf("%s 100G Ethernet: 100G CWDM4 MSA without FEC\n", pfx);
328 break;
329 case SFF8636_ETHERNET_100G_RSVD1:
330 printf("%s (reserved or unknown)\n", pfx);
331 break;
332 case SFF8636_ETHERNET_100G_CR4:
333 printf("%s 100G Ethernet: 100G Base-CR4 or 25G Base-CR CA-L\n",
334 pfx);
335 break;
336 case SFF8636_ETHERNET_25G_CR_CA_S:
337 printf("%s 25G Ethernet: 25G Base-CR CA-S\n", pfx);
338 break;
339 case SFF8636_ETHERNET_25G_CR_CA_N:
340 printf("%s 25G Ethernet: 25G Base-CR CA-N\n", pfx);
341 break;
342 case SFF8636_ETHERNET_40G_ER4:
343 printf("%s 40G Ethernet: 40G Base-ER4\n", pfx);
344 break;
345 case SFF8636_ETHERNET_4X10_SR:
346 printf("%s 4x10G Ethernet: 10G Base-SR\n", pfx);
347 break;
348 case SFF8636_ETHERNET_40G_PSM4:
349 printf("%s 40G Ethernet: 40G PSM4 Parallel SMF\n", pfx);
350 break;
351 case SFF8636_ETHERNET_G959_P1I1_2D1:
352 printf("%s Ethernet: G959.1 profile P1I1-2D1 (10709 MBd, 2km, 1310nm SM)\n",
353 pfx);
354 break;
355 case SFF8636_ETHERNET_G959_P1S1_2D2:
356 printf("%s Ethernet: G959.1 profile P1S1-2D2 (10709 MBd, 40km, 1550nm SM)\n",
357 pfx);
358 break;
359 case SFF8636_ETHERNET_G959_P1L1_2D2:
360 printf("%s Ethernet: G959.1 profile P1L1-2D2 (10709 MBd, 80km, 1550nm SM)\n",
361 pfx);
362 break;
363 case SFF8636_ETHERNET_10GT_SFI:
364 printf("%s 10G Ethernet: 10G Base-T with SFI electrical interface\n",
365 pfx);
366 break;
367 case SFF8636_ETHERNET_100G_CLR4:
368 printf("%s 100G Ethernet: 100G CLR4\n", pfx);
369 break;
370 case SFF8636_ETHERNET_100G_AOC2:
371 printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n",
372 pfx);
373 break;
374 case SFF8636_ETHERNET_100G_ACC2:
375 printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n",
376 pfx);
377 break;
378 default:
379 printf("%s (reserved or unknown)\n", pfx);
380 break;
381 }
382 }
383
384 /* SONET Compliance Codes */
385 if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_40G_OTN))
386 printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx);
387 if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR))
388 printf("%s SONET: OC-48, long reach\n", pfx);
389 if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR))
390 printf("%s SONET: OC-48, intermediate reach\n", pfx);
391 if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR))
392 printf("%s SONET: OC-48, short reach\n", pfx);
393
394 /* SAS/SATA Compliance Codes */
395 if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G))
396 printf("%s SAS 6.0G\n", pfx);
397 if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G))
398 printf("%s SAS 3.0G\n", pfx);
399
400 /* Ethernet Compliance Codes */
401 if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T)
402 printf("%s Ethernet: 1000BASE-T\n", pfx);
403 if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX)
404 printf("%s Ethernet: 1000BASE-CX\n", pfx);
405 if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX)
406 printf("%s Ethernet: 1000BASE-LX\n", pfx);
407 if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX)
408 printf("%s Ethernet: 1000BASE-SX\n", pfx);
409
410 /* Fibre Channel link length */
411 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG)
412 printf("%s FC: very long distance (V)\n", pfx);
413 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT)
414 printf("%s FC: short distance (S)\n", pfx);
415 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT)
416 printf("%s FC: intermediate distance (I)\n", pfx);
417 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG)
418 printf("%s FC: long distance (L)\n", pfx);
419 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED)
420 printf("%s FC: medium distance (M)\n", pfx);
421
422 /* Fibre Channel transmitter technology */
423 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC)
424 printf("%s FC: Longwave laser (LC)\n", pfx);
425 if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER)
426 printf("%s FC: Electrical inter-enclosure (EL)\n", pfx);
427 if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA)
428 printf("%s FC: Electrical intra-enclosure (EL)\n", pfx);
429 if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_WO_OFC)
430 printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx);
431 if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC)
432 printf("%s FC: Shortwave laser with OFC (SL)\n", pfx);
433 if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL)
434 printf("%s FC: Longwave laser (LL)\n", pfx);
435
436 /* Fibre Channel transmission media */
437 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TW)
438 printf("%s FC: Twin Axial Pair (TW)\n", pfx);
439 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TP)
440 printf("%s FC: Twisted Pair (TP)\n", pfx);
441 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_MI)
442 printf("%s FC: Miniature Coax (MI)\n", pfx);
443 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TV)
444 printf("%s FC: Video Coax (TV)\n", pfx);
445 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M6)
446 printf("%s FC: Multimode, 62.5m (M6)\n", pfx);
447 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M5)
448 printf("%s FC: Multimode, 50m (M5)\n", pfx);
449 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_OM3)
450 printf("%s FC: Multimode, 50um (OM3)\n", pfx);
451 if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_SM)
452 printf("%s FC: Single Mode (SM)\n", pfx);
453
454 /* Fibre Channel speed */
455 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS)
456 printf("%s FC: 1200 MBytes/sec\n", pfx);
457 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS)
458 printf("%s FC: 800 MBytes/sec\n", pfx);
459 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS)
460 printf("%s FC: 1600 MBytes/sec\n", pfx);
461 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS)
462 printf("%s FC: 400 MBytes/sec\n", pfx);
463 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS)
464 printf("%s FC: 200 MBytes/sec\n", pfx);
465 if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS)
466 printf("%s FC: 100 MBytes/sec\n", pfx);
467 }
468
sff8636_show_encoding(const __u8 * id)469 static void sff8636_show_encoding(const __u8 *id)
470 {
471 sff8024_show_encoding(id, SFF8636_ENCODING_OFFSET, ETH_MODULE_SFF_8636);
472 }
473
sff8636_show_rate_identifier(const __u8 * id)474 static void sff8636_show_rate_identifier(const __u8 *id)
475 {
476 /* TODO: Need to fix rate select logic */
477 printf("\t%-41s : 0x%02x\n", "Rate identifier",
478 id[SFF8636_EXT_RS_OFFSET]);
479 }
480
sff8636_show_oui(const __u8 * id)481 static void sff8636_show_oui(const __u8 *id)
482 {
483 sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
484 }
485
sff8636_show_wavelength_or_copper_compliance(const __u8 * id)486 static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
487 {
488 printf("\t%-41s : 0x%02x", "Transmitter technology",
489 (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK));
490
491 switch (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) {
492 case SFF8636_TRANS_850_VCSEL:
493 printf(" (850 nm VCSEL)\n");
494 break;
495 case SFF8636_TRANS_1310_VCSEL:
496 printf(" (1310 nm VCSEL)\n");
497 break;
498 case SFF8636_TRANS_1550_VCSEL:
499 printf(" (1550 nm VCSEL)\n");
500 break;
501 case SFF8636_TRANS_1310_FP:
502 printf(" (1310 nm FP)\n");
503 break;
504 case SFF8636_TRANS_1310_DFB:
505 printf(" (1310 nm DFB)\n");
506 break;
507 case SFF8636_TRANS_1550_DFB:
508 printf(" (1550 nm DFB)\n");
509 break;
510 case SFF8636_TRANS_1310_EML:
511 printf(" (1310 nm EML)\n");
512 break;
513 case SFF8636_TRANS_1550_EML:
514 printf(" (1550 nm EML)\n");
515 break;
516 case SFF8636_TRANS_OTHERS:
517 printf(" (Others/Undefined)\n");
518 break;
519 case SFF8636_TRANS_1490_DFB:
520 printf(" (1490 nm DFB)\n");
521 break;
522 case SFF8636_TRANS_COPPER_PAS_UNEQUAL:
523 printf(" (Copper cable unequalized)\n");
524 break;
525 case SFF8636_TRANS_COPPER_PAS_EQUAL:
526 printf(" (Copper cable passive equalized)\n");
527 break;
528 case SFF8636_TRANS_COPPER_LNR_FAR_EQUAL:
529 printf(" (Copper cable, near and far end limiting active equalizers)\n");
530 break;
531 case SFF8636_TRANS_COPPER_FAR_EQUAL:
532 printf(" (Copper cable, far end limiting active equalizers)\n");
533 break;
534 case SFF8636_TRANS_COPPER_NEAR_EQUAL:
535 printf(" (Copper cable, near end limiting active equalizers)\n");
536 break;
537 case SFF8636_TRANS_COPPER_LNR_EQUAL:
538 printf(" (Copper cable, linear active equalizers)\n");
539 break;
540 }
541
542 if ((id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK)
543 >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
544 printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz",
545 id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]);
546 printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz",
547 id[SFF8636_WAVELEN_LOW_BYTE_OFFSET]);
548 printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz",
549 id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]);
550 printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
551 id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]);
552 } else {
553 printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
554 (((id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) |
555 id[SFF8636_WAVELEN_LOW_BYTE_OFFSET])*0.05));
556 printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
557 (((id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) |
558 id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET])*0.005));
559 }
560 }
561
sff8636_show_revision_compliance(const __u8 * id)562 static void sff8636_show_revision_compliance(const __u8 *id)
563 {
564 static const char *pfx =
565 "\tRevision Compliance :";
566
567 switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) {
568 case SFF8636_REV_UNSPECIFIED:
569 printf("%s Revision not specified\n", pfx);
570 break;
571 case SFF8636_REV_8436_48:
572 printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
573 break;
574 case SFF8636_REV_8436_8636:
575 printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
576 break;
577 case SFF8636_REV_8636_13:
578 printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
579 break;
580 case SFF8636_REV_8636_14:
581 printf("%s SFF-8636 Rev 1.4\n", pfx);
582 break;
583 case SFF8636_REV_8636_15:
584 printf("%s SFF-8636 Rev 1.5\n", pfx);
585 break;
586 case SFF8636_REV_8636_20:
587 printf("%s SFF-8636 Rev 2.0\n", pfx);
588 break;
589 case SFF8636_REV_8636_27:
590 printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
591 break;
592 default:
593 printf("%s Unallocated\n", pfx);
594 break;
595 }
596 }
597
598 /*
599 * 2-byte internal temperature conversions:
600 * First byte is a signed 8-bit integer, which is the temp decimal part
601 * Second byte are 1/256th of degree, which are added to the dec part.
602 */
603 #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
604
sff8636_dom_parse(const __u8 * id,struct sff_diags * sd)605 static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd)
606 {
607 int i = 0;
608
609 /* Monitoring Thresholds for Alarms and Warnings */
610 sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF8636_VCC_CURR);
611 sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF8636_VCC_HALRM);
612 sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF8636_VCC_LALRM);
613 sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF8636_VCC_HWARN);
614 sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF8636_VCC_LWARN);
615
616 sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR);
617 sd->sfp_temp[HALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HALRM);
618 sd->sfp_temp[LALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LALRM);
619 sd->sfp_temp[HWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HWARN);
620 sd->sfp_temp[LWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LWARN);
621
622 sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_HALRM);
623 sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_LALRM);
624 sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_HWARN);
625 sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_LWARN);
626
627 sd->tx_power[HALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_HALRM);
628 sd->tx_power[LALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_LALRM);
629 sd->tx_power[HWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_HWARN);
630 sd->tx_power[LWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_LWARN);
631
632 sd->rx_power[HALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_HALRM);
633 sd->rx_power[LALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_LALRM);
634 sd->rx_power[HWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_HWARN);
635 sd->rx_power[LWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_LWARN);
636
637
638 /* Channel Specific Data */
639 for (i = 0; i < MAX_CHANNEL_NUM; i++) {
640 u8 rx_power_offset, tx_bias_offset;
641 u8 tx_power_offset;
642
643 switch (i) {
644 case 0:
645 rx_power_offset = SFF8636_RX_PWR_1_OFFSET;
646 tx_power_offset = SFF8636_TX_PWR_1_OFFSET;
647 tx_bias_offset = SFF8636_TX_BIAS_1_OFFSET;
648 break;
649 case 1:
650 rx_power_offset = SFF8636_RX_PWR_2_OFFSET;
651 tx_power_offset = SFF8636_TX_PWR_2_OFFSET;
652 tx_bias_offset = SFF8636_TX_BIAS_2_OFFSET;
653 break;
654 case 2:
655 rx_power_offset = SFF8636_RX_PWR_3_OFFSET;
656 tx_power_offset = SFF8636_TX_PWR_3_OFFSET;
657 tx_bias_offset = SFF8636_TX_BIAS_3_OFFSET;
658 break;
659 case 3:
660 rx_power_offset = SFF8636_RX_PWR_4_OFFSET;
661 tx_power_offset = SFF8636_TX_PWR_4_OFFSET;
662 tx_bias_offset = SFF8636_TX_BIAS_4_OFFSET;
663 break;
664 }
665 sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset);
666 sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
667 sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
668 }
669
670 }
671
sff8636_show_dom(const __u8 * id,__u32 eeprom_len)672 static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
673 {
674 struct sff_diags sd = {0};
675 char *rx_power_string = NULL;
676 char power_string[MAX_DESC_SIZE];
677 int i;
678
679 /*
680 * There is no clear identifier to signify the existence of
681 * optical diagnostics similar to SFF-8472. So checking existence
682 * of page 3, will provide the gurantee for existence of alarms
683 * and thresholds
684 * If pagging support exists, then supports_alarms is marked as 1
685 */
686
687 if (eeprom_len == ETH_MODULE_SFF_8636_MAX_LEN) {
688 if (!(id[SFF8636_STATUS_2_OFFSET] &
689 SFF8636_STATUS_PAGE_3_PRESENT)) {
690 sd.supports_alarms = 1;
691 }
692 }
693
694 sd.rx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] &
695 SFF8636_RX_PWR_TYPE_MASK;
696 sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] &
697 SFF8636_RX_PWR_TYPE_MASK;
698
699 sff8636_dom_parse(id, &sd);
700
701 PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
702 PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
703
704 /*
705 * SFF-8636/8436 spec is not clear whether RX power/ TX bias
706 * current fields are supported or not. A valid temperature
707 * reading is used as existence for TX/RX power.
708 */
709 if ((sd.sfp_temp[MCURR] == 0x0) ||
710 (sd.sfp_temp[MCURR] == (__s16)0xFFFF))
711 return;
712
713 printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
714 (sd.supports_alarms ? "Yes" : "No"));
715
716 for (i = 0; i < MAX_CHANNEL_NUM; i++) {
717 snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
718 "Laser tx bias current", i+1);
719 PRINT_BIAS(power_string, sd.scd[i].bias_cur);
720 }
721
722 for (i = 0; i < MAX_CHANNEL_NUM; i++) {
723 snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
724 "Transmit avg optical power", i+1);
725 PRINT_xX_PWR(power_string, sd.scd[i].tx_power);
726 }
727
728 if (!sd.rx_power_type)
729 rx_power_string = "Receiver signal OMA";
730 else
731 rx_power_string = "Rcvr signal avg optical power";
732
733 for (i = 0; i < MAX_CHANNEL_NUM; i++) {
734 snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)",
735 rx_power_string, i+1);
736 PRINT_xX_PWR(power_string, sd.scd[i].rx_power);
737 }
738
739 if (sd.supports_alarms) {
740 for (i = 0; sff8636_aw_flags[i].str; ++i) {
741 printf("\t%-41s : %s\n", sff8636_aw_flags[i].str,
742 id[sff8636_aw_flags[i].offset]
743 & sff8636_aw_flags[i].value ? "On" : "Off");
744 }
745
746 sff_show_thresholds(sd);
747 }
748
749 }
sff8636_show_all(const __u8 * id,__u32 eeprom_len)750 void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
751 {
752 sff8636_show_identifier(id);
753 if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
754 (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
755 (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) {
756 sff8636_show_ext_identifier(id);
757 sff8636_show_connector(id);
758 sff8636_show_transceiver(id);
759 sff8636_show_encoding(id);
760 sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET,
761 "BR, Nominal", 100, "Mbps");
762 sff8636_show_rate_identifier(id);
763 sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET,
764 "Length (SMF,km)", 1, "km");
765 sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET,
766 "Length (OM3 50um)", 2, "m");
767 sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET,
768 "Length (OM2 50um)", 1, "m");
769 sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET,
770 "Length (OM1 62.5um)", 1, "m");
771 sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET,
772 "Length (Copper or Active cable)", 1, "m");
773 sff8636_show_wavelength_or_copper_compliance(id);
774 sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET,
775 SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
776 sff8636_show_oui(id);
777 sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET,
778 SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
779 sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
780 SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
781 sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
782 SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
783 sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
784 SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
785 sff8636_show_revision_compliance(id);
786 sff8636_show_dom(id, eeprom_len);
787 }
788 }
789