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