xref: /aosp_15_r20/external/ethtool/qsfp.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * qsfp.c: Implements SFF-8636 based QSFP+/QSFP28 Diagnostics Memory map.
3  *
4  * Copyright 2010 Solarflare Communications Inc.
5  * Aurelien Guillaume <[email protected]> (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 <[email protected]>
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 <errno.h>
59 #include "internal.h"
60 #include "sff-common.h"
61 #include "qsfp.h"
62 #include "cmis.h"
63 #include "netlink/extapi.h"
64 
65 struct sff8636_memory_map {
66 	const __u8 *lower_memory;
67 	const __u8 *upper_memory[4];
68 #define page_00h upper_memory[0x0]
69 #define page_03h upper_memory[0x3]
70 };
71 
72 #define SFF8636_PAGE_SIZE	0x80
73 #define SFF8636_I2C_ADDRESS	0x50
74 #define SFF8636_MAX_CHANNEL_NUM	4
75 
76 #define MAX_DESC_SIZE	42
77 
78 static struct sff8636_aw_flags {
79 	const char *str;        /* Human-readable string, null at the end */
80 	int offset;
81 	__u8 value;             /* Alarm is on if (offset & value) != 0. */
82 } sff8636_aw_flags[] = {
83 	{ "Laser bias current high alarm   (Chan 1)",
84 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HALARM) },
85 	{ "Laser bias current low alarm    (Chan 1)",
86 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LALARM) },
87 	{ "Laser bias current high warning (Chan 1)",
88 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HWARN) },
89 	{ "Laser bias current low warning  (Chan 1)",
90 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LWARN) },
91 
92 	{ "Laser bias current high alarm   (Chan 2)",
93 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HALARM) },
94 	{ "Laser bias current low alarm    (Chan 2)",
95 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LALARM) },
96 	{ "Laser bias current high warning (Chan 2)",
97 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HWARN) },
98 	{ "Laser bias current low warning  (Chan 2)",
99 		SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LWARN) },
100 
101 	{ "Laser bias current high alarm   (Chan 3)",
102 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HALARM) },
103 	{ "Laser bias current low alarm    (Chan 3)",
104 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LALARM) },
105 	{ "Laser bias current high warning (Chan 3)",
106 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HWARN) },
107 	{ "Laser bias current low warning  (Chan 3)",
108 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LWARN) },
109 
110 	{ "Laser bias current high alarm   (Chan 4)",
111 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HALARM) },
112 	{ "Laser bias current low alarm    (Chan 4)",
113 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LALARM) },
114 	{ "Laser bias current high warning (Chan 4)",
115 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HWARN) },
116 	{ "Laser bias current low warning  (Chan 4)",
117 		SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LWARN) },
118 
119 	{ "Module temperature high alarm",
120 		SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HALARM_STATUS) },
121 	{ "Module temperature low alarm",
122 		SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LALARM_STATUS) },
123 	{ "Module temperature high warning",
124 		SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HWARN_STATUS) },
125 	{ "Module temperature low warning",
126 		SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LWARN_STATUS) },
127 
128 	{ "Module voltage high alarm",
129 		SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HALARM_STATUS) },
130 	{ "Module voltage low alarm",
131 		SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LALARM_STATUS) },
132 	{ "Module voltage high warning",
133 		SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HWARN_STATUS) },
134 	{ "Module voltage low warning",
135 		SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LWARN_STATUS) },
136 
137 	{ "Laser tx power high alarm   (Channel 1)",
138 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HALARM) },
139 	{ "Laser tx power low alarm    (Channel 1)",
140 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LALARM) },
141 	{ "Laser tx power high warning (Channel 1)",
142 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HWARN) },
143 	{ "Laser tx power low warning  (Channel 1)",
144 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LWARN) },
145 
146 	{ "Laser tx power high alarm   (Channel 2)",
147 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HALARM) },
148 	{ "Laser tx power low alarm    (Channel 2)",
149 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LALARM) },
150 	{ "Laser tx power high warning (Channel 2)",
151 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HWARN) },
152 	{ "Laser tx power low warning  (Channel 2)",
153 		SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LWARN) },
154 
155 	{ "Laser tx power high alarm   (Channel 3)",
156 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HALARM) },
157 	{ "Laser tx power low alarm    (Channel 3)",
158 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LALARM) },
159 	{ "Laser tx power high warning (Channel 3)",
160 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HWARN) },
161 	{ "Laser tx power low warning  (Channel 3)",
162 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LWARN) },
163 
164 	{ "Laser tx power high alarm   (Channel 4)",
165 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HALARM) },
166 	{ "Laser tx power low alarm    (Channel 4)",
167 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LALARM) },
168 	{ "Laser tx power high warning (Channel 4)",
169 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HWARN) },
170 	{ "Laser tx power low warning  (Channel 4)",
171 		SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LWARN) },
172 
173 	{ "Laser rx power high alarm   (Channel 1)",
174 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HALARM) },
175 	{ "Laser rx power low alarm    (Channel 1)",
176 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LALARM) },
177 	{ "Laser rx power high warning (Channel 1)",
178 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HWARN) },
179 	{ "Laser rx power low warning  (Channel 1)",
180 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LWARN) },
181 
182 	{ "Laser rx power high alarm   (Channel 2)",
183 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HALARM) },
184 	{ "Laser rx power low alarm    (Channel 2)",
185 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LALARM) },
186 	{ "Laser rx power high warning (Channel 2)",
187 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HWARN) },
188 	{ "Laser rx power low warning  (Channel 2)",
189 		SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LWARN) },
190 
191 	{ "Laser rx power high alarm   (Channel 3)",
192 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HALARM) },
193 	{ "Laser rx power low alarm    (Channel 3)",
194 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LALARM) },
195 	{ "Laser rx power high warning (Channel 3)",
196 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HWARN) },
197 	{ "Laser rx power low warning  (Channel 3)",
198 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LWARN) },
199 
200 	{ "Laser rx power high alarm   (Channel 4)",
201 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HALARM) },
202 	{ "Laser rx power low alarm    (Channel 4)",
203 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LALARM) },
204 	{ "Laser rx power high warning (Channel 4)",
205 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HWARN) },
206 	{ "Laser rx power low warning  (Channel 4)",
207 		SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LWARN) },
208 
209 	{ NULL, 0, 0 },
210 };
211 
sff8636_show_identifier(const struct sff8636_memory_map * map)212 static void sff8636_show_identifier(const struct sff8636_memory_map *map)
213 {
214 	sff8024_show_identifier(map->lower_memory, SFF8636_ID_OFFSET);
215 }
216 
sff8636_show_ext_identifier(const struct sff8636_memory_map * map)217 static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map)
218 {
219 	printf("\t%-41s : 0x%02x\n", "Extended identifier",
220 	       map->page_00h[SFF8636_EXT_ID_OFFSET]);
221 
222 	static const char *pfx =
223 		"\tExtended identifier description           :";
224 
225 	switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
226 		SFF8636_EXT_ID_PWR_CLASS_MASK) {
227 	case SFF8636_EXT_ID_PWR_CLASS_1:
228 		printf("%s 1.5W max. Power consumption\n", pfx);
229 		break;
230 	case SFF8636_EXT_ID_PWR_CLASS_2:
231 		printf("%s 2.0W max. Power consumption\n", pfx);
232 		break;
233 	case SFF8636_EXT_ID_PWR_CLASS_3:
234 		printf("%s 2.5W max. Power consumption\n", pfx);
235 		break;
236 	case SFF8636_EXT_ID_PWR_CLASS_4:
237 		printf("%s 3.5W max. Power consumption\n", pfx);
238 		break;
239 	}
240 
241 	if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK)
242 		printf("%s CDR present in TX,", pfx);
243 	else
244 		printf("%s No CDR in TX,", pfx);
245 
246 	if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK)
247 		printf(" CDR present in RX\n");
248 	else
249 		printf(" No CDR in RX\n");
250 
251 	switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
252 		SFF8636_EXT_ID_EPWR_CLASS_MASK) {
253 	case SFF8636_EXT_ID_PWR_CLASS_LEGACY:
254 		printf("%s", pfx);
255 		break;
256 	case SFF8636_EXT_ID_PWR_CLASS_5:
257 		printf("%s 4.0W max. Power consumption,", pfx);
258 		break;
259 	case SFF8636_EXT_ID_PWR_CLASS_6:
260 		printf("%s 4.5W max. Power consumption, ", pfx);
261 		break;
262 	case SFF8636_EXT_ID_PWR_CLASS_7:
263 		printf("%s 5.0W max. Power consumption, ", pfx);
264 		break;
265 	}
266 	if (map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
267 	    SFF8636_HIGH_PWR_ENABLE)
268 		printf(" High Power Class (> 3.5 W) enabled\n");
269 	else
270 		printf(" High Power Class (> 3.5 W) not enabled\n");
271 	printf("\t%-41s : ", "Power set");
272 	printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
273 			     SFF8636_LOW_PWR_SET));
274 	printf("\t%-41s : ", "Power override");
275 	printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
276 			     SFF8636_PWR_OVERRIDE));
277 }
278 
sff8636_show_connector(const struct sff8636_memory_map * map)279 static void sff8636_show_connector(const struct sff8636_memory_map *map)
280 {
281 	sff8024_show_connector(map->page_00h, SFF8636_CTOR_OFFSET);
282 }
283 
sff8636_show_transceiver(const struct sff8636_memory_map * map)284 static void sff8636_show_transceiver(const struct sff8636_memory_map *map)
285 {
286 	static const char *pfx =
287 		"\tTransceiver type                          :";
288 
289 	printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
290 			"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
291 			"Transceiver codes",
292 			map->page_00h[SFF8636_ETHERNET_COMP_OFFSET],
293 			map->page_00h[SFF8636_SONET_COMP_OFFSET],
294 			map->page_00h[SFF8636_SAS_COMP_OFFSET],
295 			map->page_00h[SFF8636_GIGE_COMP_OFFSET],
296 			map->page_00h[SFF8636_FC_LEN_OFFSET],
297 			map->page_00h[SFF8636_FC_TECH_OFFSET],
298 			map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET],
299 			map->page_00h[SFF8636_FC_SPEED_OFFSET]);
300 
301 	/* 10G/40G Ethernet Compliance Codes */
302 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
303 	    SFF8636_ETHERNET_10G_LRM)
304 		printf("%s 10G Ethernet: 10G Base-LRM\n", pfx);
305 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
306 	    SFF8636_ETHERNET_10G_LR)
307 		printf("%s 10G Ethernet: 10G Base-LR\n", pfx);
308 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
309 	    SFF8636_ETHERNET_10G_SR)
310 		printf("%s 10G Ethernet: 10G Base-SR\n", pfx);
311 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
312 	    SFF8636_ETHERNET_40G_CR4)
313 		printf("%s 40G Ethernet: 40G Base-CR4\n", pfx);
314 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
315 	    SFF8636_ETHERNET_40G_SR4)
316 		printf("%s 40G Ethernet: 40G Base-SR4\n", pfx);
317 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
318 	    SFF8636_ETHERNET_40G_LR4)
319 		printf("%s 40G Ethernet: 40G Base-LR4\n", pfx);
320 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
321 	    SFF8636_ETHERNET_40G_ACTIVE)
322 		printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx);
323 	/* Extended Specification Compliance Codes from SFF-8024 */
324 	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
325 	    SFF8636_ETHERNET_RSRVD) {
326 		switch (map->page_00h[SFF8636_OPTION_1_OFFSET]) {
327 		case SFF8636_ETHERNET_UNSPECIFIED:
328 			printf("%s (reserved or unknown)\n", pfx);
329 			break;
330 		case SFF8636_ETHERNET_100G_AOC:
331 			printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)\n",
332 					pfx);
333 			break;
334 		case SFF8636_ETHERNET_100G_SR4:
335 			printf("%s 100G Ethernet: 100G Base-SR4 or 25GBase-SR\n",
336 					pfx);
337 			break;
338 		case SFF8636_ETHERNET_100G_LR4:
339 			printf("%s 100G Ethernet: 100G Base-LR4\n", pfx);
340 			break;
341 		case SFF8636_ETHERNET_100G_ER4:
342 			printf("%s 100G Ethernet: 100G Base-ER4\n", pfx);
343 			break;
344 		case SFF8636_ETHERNET_100G_SR10:
345 			printf("%s 100G Ethernet: 100G Base-SR10\n", pfx);
346 			break;
347 		case SFF8636_ETHERNET_100G_CWDM4_FEC:
348 			printf("%s 100G Ethernet: 100G CWDM4 MSA with FEC\n", pfx);
349 			break;
350 		case SFF8636_ETHERNET_100G_PSM4:
351 			printf("%s 100G Ethernet: 100G PSM4 Parallel SMF\n", pfx);
352 			break;
353 		case SFF8636_ETHERNET_100G_ACC:
354 			printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)\n",
355 				pfx);
356 			break;
357 		case SFF8636_ETHERNET_100G_CWDM4_NO_FEC:
358 			printf("%s 100G Ethernet: 100G CWDM4 MSA without FEC\n", pfx);
359 			break;
360 		case SFF8636_ETHERNET_100G_RSVD1:
361 			printf("%s (reserved or unknown)\n", pfx);
362 			break;
363 		case SFF8636_ETHERNET_100G_CR4:
364 			printf("%s 100G Ethernet: 100G Base-CR4 or 25G Base-CR CA-L\n",
365 				pfx);
366 			break;
367 		case SFF8636_ETHERNET_25G_CR_CA_S:
368 			printf("%s 25G Ethernet: 25G Base-CR CA-S\n", pfx);
369 			break;
370 		case SFF8636_ETHERNET_25G_CR_CA_N:
371 			printf("%s 25G Ethernet: 25G Base-CR CA-N\n", pfx);
372 			break;
373 		case SFF8636_ETHERNET_40G_ER4:
374 			printf("%s 40G Ethernet: 40G Base-ER4\n", pfx);
375 			break;
376 		case SFF8636_ETHERNET_4X10_SR:
377 			printf("%s 4x10G Ethernet: 10G Base-SR\n", pfx);
378 			break;
379 		case SFF8636_ETHERNET_40G_PSM4:
380 			printf("%s 40G Ethernet: 40G PSM4 Parallel SMF\n", pfx);
381 			break;
382 		case SFF8636_ETHERNET_G959_P1I1_2D1:
383 			printf("%s Ethernet: G959.1 profile P1I1-2D1 (10709 MBd, 2km, 1310nm SM)\n",
384 					pfx);
385 			break;
386 		case SFF8636_ETHERNET_G959_P1S1_2D2:
387 			printf("%s Ethernet: G959.1 profile P1S1-2D2 (10709 MBd, 40km, 1550nm SM)\n",
388 					pfx);
389 			break;
390 		case SFF8636_ETHERNET_G959_P1L1_2D2:
391 			printf("%s Ethernet: G959.1 profile P1L1-2D2 (10709 MBd, 80km, 1550nm SM)\n",
392 					pfx);
393 			break;
394 		case SFF8636_ETHERNET_10GT_SFI:
395 			printf("%s 10G Ethernet: 10G Base-T with SFI electrical interface\n",
396 					pfx);
397 			break;
398 		case SFF8636_ETHERNET_100G_CLR4:
399 			printf("%s 100G Ethernet: 100G CLR4\n", pfx);
400 			break;
401 		case SFF8636_ETHERNET_100G_AOC2:
402 			printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n",
403 					pfx);
404 			break;
405 		case SFF8636_ETHERNET_100G_ACC2:
406 			printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n",
407 					pfx);
408 			break;
409 		case SFF8636_ETHERNET_100GE_DWDM2:
410 			printf("%s 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n",
411 					pfx);
412 			break;
413 		case SFF8636_ETHERNET_100G_1550NM_WDM:
414 			printf("%s 100G 1550nm WDM (4 wavelengths)\n", pfx);
415 			break;
416 		case SFF8636_ETHERNET_10G_BASET_SR:
417 			printf("%s 10GBASE-T Short Reach (30 meters)\n", pfx);
418 			break;
419 		case SFF8636_ETHERNET_5G_BASET:
420 			printf("%s 5GBASE-T\n", pfx);
421 			break;
422 		case SFF8636_ETHERNET_2HALFG_BASET:
423 			printf("%s 2.5GBASE-T\n", pfx);
424 			break;
425 		case SFF8636_ETHERNET_40G_SWDM4:
426 			printf("%s 40G SWDM4\n", pfx);
427 			break;
428 		case SFF8636_ETHERNET_100G_SWDM4:
429 			printf("%s 100G SWDM4\n", pfx);
430 			break;
431 		case SFF8636_ETHERNET_100G_PAM4_BIDI:
432 			printf("%s 100G PAM4 BiDi\n", pfx);
433 			break;
434 		case SFF8636_ETHERNET_4WDM10_MSA:
435 			printf("%s 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n",
436 					pfx);
437 			break;
438 		case SFF8636_ETHERNET_4WDM20_MSA:
439 			printf("%s 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n",
440 					pfx);
441 			break;
442 		case SFF8636_ETHERNET_4WDM40_MSA:
443 			printf("%s 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n",
444 					pfx);
445 			break;
446 		case SFF8636_ETHERNET_100G_DR:
447 			printf("%s 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx);
448 			break;
449 		case SFF8636_ETHERNET_100G_FR_NOFEC:
450 			 printf("%s 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
451 			break;
452 		case SFF8636_ETHERNET_100G_LR_NOFEC:
453 			printf("%s 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
454 			break;
455 		case SFF8636_ETHERNET_200G_ACC1:
456 			printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
457 					pfx);
458 			break;
459 		case SFF8636_ETHERNET_200G_AOC1:
460 			printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
461 					pfx);
462 			break;
463 		case SFF8636_ETHERNET_200G_ACC2:
464 			printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
465 					pfx);
466 			break;
467 		case SFF8636_ETHERNET_200G_A0C2:
468 			printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
469 					pfx);
470 			break;
471 		case SFF8636_ETHERNET_200G_CR4:
472 			printf("%s 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx);
473 			break;
474 		case SFF8636_ETHERNET_200G_SR4:
475 			printf("%s 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx);
476 			break;
477 		case SFF8636_ETHERNET_200G_DR4:
478 			printf("%s 50GBASE-FR or 200GBASE-DR4\n", pfx);
479 			break;
480 		case SFF8636_ETHERNET_200G_FR4:
481 			printf("%s 200GBASE-FR4\n", pfx);
482 			break;
483 		case SFF8636_ETHERNET_200G_PSM4:
484 			 printf("%s 200G 1550 nm PSM4\n", pfx);
485 			break;
486 		case SFF8636_ETHERNET_50G_LR:
487 			printf("%s 50GBASE-LR\n", pfx);
488 			break;
489 		case SFF8636_ETHERNET_200G_LR4:
490 			printf("%s 200GBASE-LR4\n", pfx);
491 			break;
492 		case SFF8636_ETHERNET_64G_EA:
493 			printf("%s 64GFC EA\n", pfx);
494 			break;
495 		case SFF8636_ETHERNET_64G_SW:
496 			printf("%s 64GFC SW\n", pfx);
497 			break;
498 		case SFF8636_ETHERNET_64G_LW:
499 			printf("%s 64GFC LW\n", pfx);
500 			break;
501 		case SFF8636_ETHERNET_128FC_EA:
502 			printf("%s 128GFC EA\n", pfx);
503 			break;
504 		case SFF8636_ETHERNET_128FC_SW:
505 			printf("%s 128GFC SW\n", pfx);
506 			break;
507 		case SFF8636_ETHERNET_128FC_LW:
508 			printf("%s 128GFC LW\n", pfx);
509 			break;
510 		default:
511 			printf("%s (reserved or unknown)\n", pfx);
512 			break;
513 		}
514 	}
515 
516 	/* SONET Compliance Codes */
517 	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] &
518 	    (SFF8636_SONET_40G_OTN))
519 		printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx);
520 	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR))
521 		printf("%s SONET: OC-48, long reach\n", pfx);
522 	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR))
523 		printf("%s SONET: OC-48, intermediate reach\n", pfx);
524 	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR))
525 		printf("%s SONET: OC-48, short reach\n", pfx);
526 
527 	/* SAS/SATA Compliance Codes */
528 	if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G))
529 		printf("%s SAS 6.0G\n", pfx);
530 	if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G))
531 		printf("%s SAS 3.0G\n", pfx);
532 
533 	/* Ethernet Compliance Codes */
534 	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T)
535 		printf("%s Ethernet: 1000BASE-T\n", pfx);
536 	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX)
537 		printf("%s Ethernet: 1000BASE-CX\n", pfx);
538 	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX)
539 		printf("%s Ethernet: 1000BASE-LX\n", pfx);
540 	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX)
541 		printf("%s Ethernet: 1000BASE-SX\n", pfx);
542 
543 	/* Fibre Channel link length */
544 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG)
545 		printf("%s FC: very long distance (V)\n", pfx);
546 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT)
547 		printf("%s FC: short distance (S)\n", pfx);
548 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT)
549 		printf("%s FC: intermediate distance (I)\n", pfx);
550 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG)
551 		printf("%s FC: long distance (L)\n", pfx);
552 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED)
553 		printf("%s FC: medium distance (M)\n", pfx);
554 
555 	/* Fibre Channel transmitter technology */
556 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC)
557 		printf("%s FC: Longwave laser (LC)\n", pfx);
558 	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER)
559 		printf("%s FC: Electrical inter-enclosure (EL)\n", pfx);
560 	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA)
561 		printf("%s FC: Electrical intra-enclosure (EL)\n", pfx);
562 	if (map->page_00h[SFF8636_FC_TECH_OFFSET] &
563 	    SFF8636_FC_TECH_SHORT_WO_OFC)
564 		printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx);
565 	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC)
566 		printf("%s FC: Shortwave laser with OFC (SL)\n", pfx);
567 	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL)
568 		printf("%s FC: Longwave laser (LL)\n", pfx);
569 
570 	/* Fibre Channel transmission media */
571 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
572 	    SFF8636_FC_TRANS_MEDIA_TW)
573 		printf("%s FC: Twin Axial Pair (TW)\n", pfx);
574 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
575 	    SFF8636_FC_TRANS_MEDIA_TP)
576 		printf("%s FC: Twisted Pair (TP)\n", pfx);
577 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
578 	    SFF8636_FC_TRANS_MEDIA_MI)
579 		printf("%s FC: Miniature Coax (MI)\n", pfx);
580 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
581 	    SFF8636_FC_TRANS_MEDIA_TV)
582 		printf("%s FC: Video Coax (TV)\n", pfx);
583 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
584 	    SFF8636_FC_TRANS_MEDIA_M6)
585 		printf("%s FC: Multimode, 62.5m (M6)\n", pfx);
586 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
587 	    SFF8636_FC_TRANS_MEDIA_M5)
588 		printf("%s FC: Multimode, 50m (M5)\n", pfx);
589 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
590 	    SFF8636_FC_TRANS_MEDIA_OM3)
591 		printf("%s FC: Multimode, 50um (OM3)\n", pfx);
592 	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
593 	    SFF8636_FC_TRANS_MEDIA_SM)
594 		printf("%s FC: Single Mode (SM)\n", pfx);
595 
596 	/* Fibre Channel speed */
597 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS)
598 		printf("%s FC: 1200 MBytes/sec\n", pfx);
599 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS)
600 		printf("%s FC: 800 MBytes/sec\n", pfx);
601 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS)
602 		printf("%s FC: 1600 MBytes/sec\n", pfx);
603 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS)
604 		printf("%s FC: 400 MBytes/sec\n", pfx);
605 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS)
606 		printf("%s FC: 200 MBytes/sec\n", pfx);
607 	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS)
608 		printf("%s FC: 100 MBytes/sec\n", pfx);
609 }
610 
sff8636_show_encoding(const struct sff8636_memory_map * map)611 static void sff8636_show_encoding(const struct sff8636_memory_map *map)
612 {
613 	sff8024_show_encoding(map->page_00h, SFF8636_ENCODING_OFFSET,
614 			      ETH_MODULE_SFF_8636);
615 }
616 
sff8636_show_rate_identifier(const struct sff8636_memory_map * map)617 static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map)
618 {
619 	/* TODO: Need to fix rate select logic */
620 	printf("\t%-41s : 0x%02x\n", "Rate identifier",
621 	       map->page_00h[SFF8636_EXT_RS_OFFSET]);
622 }
623 
624 static void
sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map * map)625 sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map)
626 {
627 	printf("\t%-41s : 0x%02x", "Transmitter technology",
628 	       map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
629 	       SFF8636_TRANS_TECH_MASK);
630 
631 	switch (map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
632 		SFF8636_TRANS_TECH_MASK) {
633 	case SFF8636_TRANS_850_VCSEL:
634 		printf(" (850 nm VCSEL)\n");
635 		break;
636 	case SFF8636_TRANS_1310_VCSEL:
637 		printf(" (1310 nm VCSEL)\n");
638 		break;
639 	case SFF8636_TRANS_1550_VCSEL:
640 		printf(" (1550 nm VCSEL)\n");
641 		break;
642 	case SFF8636_TRANS_1310_FP:
643 		printf(" (1310 nm FP)\n");
644 		break;
645 	case SFF8636_TRANS_1310_DFB:
646 		printf(" (1310 nm DFB)\n");
647 		break;
648 	case SFF8636_TRANS_1550_DFB:
649 		printf(" (1550 nm DFB)\n");
650 		break;
651 	case SFF8636_TRANS_1310_EML:
652 		printf(" (1310 nm EML)\n");
653 		break;
654 	case SFF8636_TRANS_1550_EML:
655 		printf(" (1550 nm EML)\n");
656 		break;
657 	case SFF8636_TRANS_OTHERS:
658 		printf(" (Others/Undefined)\n");
659 		break;
660 	case SFF8636_TRANS_1490_DFB:
661 		printf(" (1490 nm DFB)\n");
662 		break;
663 	case SFF8636_TRANS_COPPER_PAS_UNEQUAL:
664 		printf(" (Copper cable unequalized)\n");
665 		break;
666 	case SFF8636_TRANS_COPPER_PAS_EQUAL:
667 		printf(" (Copper cable passive equalized)\n");
668 		break;
669 	case SFF8636_TRANS_COPPER_LNR_FAR_EQUAL:
670 		printf(" (Copper cable, near and far end limiting active equalizers)\n");
671 		break;
672 	case SFF8636_TRANS_COPPER_FAR_EQUAL:
673 		printf(" (Copper cable, far end limiting active equalizers)\n");
674 		break;
675 	case SFF8636_TRANS_COPPER_NEAR_EQUAL:
676 		printf(" (Copper cable, near end limiting active equalizers)\n");
677 		break;
678 	case SFF8636_TRANS_COPPER_LNR_EQUAL:
679 		printf(" (Copper cable, linear active equalizers)\n");
680 		break;
681 	}
682 
683 	if ((map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
684 	     SFF8636_TRANS_TECH_MASK) >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
685 		printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz",
686 			map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]);
687 		printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz",
688 			map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]);
689 		printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz",
690 			map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]);
691 		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
692 		       map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]);
693 	} else {
694 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
695 		       (((map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) |
696 			 map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]) * 0.05));
697 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
698 		       (((map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) |
699 			 map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]) * 0.005));
700 	}
701 }
702 
703 /*
704  * 2-byte internal temperature conversions:
705  * First byte is a signed 8-bit integer, which is the temp decimal part
706  * Second byte are 1/256th of degree, which are added to the dec part.
707  */
708 #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
709 
sff8636_dom_parse(const struct sff8636_memory_map * map,struct sff_diags * sd)710 static void sff8636_dom_parse(const struct sff8636_memory_map *map,
711 			      struct sff_diags *sd)
712 {
713 	const __u8 *id = map->lower_memory;
714 	int i = 0;
715 
716 	/* Monitoring Thresholds for Alarms and Warnings */
717 	sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR);
718 	sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR);
719 
720 	if (!map->page_03h)
721 		goto out;
722 
723 	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
724 						   SFF8636_VCC_HALRM);
725 	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
726 						   SFF8636_VCC_LALRM);
727 	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
728 						   SFF8636_VCC_HWARN);
729 	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
730 						   SFF8636_VCC_LWARN);
731 
732 	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
733 						       SFF8636_TEMP_HALRM);
734 	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
735 						       SFF8636_TEMP_LALRM);
736 	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
737 						       SFF8636_TEMP_HWARN);
738 	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
739 						       SFF8636_TEMP_LWARN);
740 
741 	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
742 						SFF8636_TX_BIAS_HALRM);
743 	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
744 						SFF8636_TX_BIAS_LALRM);
745 	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
746 						SFF8636_TX_BIAS_HWARN);
747 	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
748 						SFF8636_TX_BIAS_LWARN);
749 
750 	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
751 						SFF8636_TX_PWR_HALRM);
752 	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
753 						SFF8636_TX_PWR_LALRM);
754 	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
755 						SFF8636_TX_PWR_HWARN);
756 	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
757 						SFF8636_TX_PWR_LWARN);
758 
759 	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
760 						SFF8636_RX_PWR_HALRM);
761 	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
762 						SFF8636_RX_PWR_LALRM);
763 	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
764 						SFF8636_RX_PWR_HWARN);
765 	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
766 						SFF8636_RX_PWR_LWARN);
767 
768 out:
769 	/* Channel Specific Data */
770 	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
771 		u8 rx_power_offset, tx_bias_offset;
772 		u8 tx_power_offset;
773 
774 		switch (i) {
775 		case 0:
776 			rx_power_offset = SFF8636_RX_PWR_1_OFFSET;
777 			tx_power_offset = SFF8636_TX_PWR_1_OFFSET;
778 			tx_bias_offset = SFF8636_TX_BIAS_1_OFFSET;
779 			break;
780 		case 1:
781 			rx_power_offset = SFF8636_RX_PWR_2_OFFSET;
782 			tx_power_offset = SFF8636_TX_PWR_2_OFFSET;
783 			tx_bias_offset = SFF8636_TX_BIAS_2_OFFSET;
784 			break;
785 		case 2:
786 			rx_power_offset = SFF8636_RX_PWR_3_OFFSET;
787 			tx_power_offset = SFF8636_TX_PWR_3_OFFSET;
788 			tx_bias_offset = SFF8636_TX_BIAS_3_OFFSET;
789 			break;
790 		case 3:
791 			rx_power_offset = SFF8636_RX_PWR_4_OFFSET;
792 			tx_power_offset = SFF8636_TX_PWR_4_OFFSET;
793 			tx_bias_offset = SFF8636_TX_BIAS_4_OFFSET;
794 			break;
795 		}
796 		sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset);
797 		sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
798 		sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
799 	}
800 }
801 
sff8636_show_dom(const struct sff8636_memory_map * map)802 static void sff8636_show_dom(const struct sff8636_memory_map *map)
803 {
804 	struct sff_diags sd = {0};
805 	char *rx_power_string = NULL;
806 	char power_string[MAX_DESC_SIZE];
807 	int i;
808 
809 	/*
810 	 * There is no clear identifier to signify the existence of
811 	 * optical diagnostics similar to SFF-8472. So checking existence
812 	 * of page 3, will provide the gurantee for existence of alarms
813 	 * and thresholds
814 	 * If pagging support exists, then supports_alarms is marked as 1
815 	 */
816 	if (map->page_03h)
817 		sd.supports_alarms = 1;
818 
819 	sd.rx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
820 			   SFF8636_RX_PWR_TYPE_MASK;
821 	sd.tx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
822 			   SFF8636_RX_PWR_TYPE_MASK;
823 
824 	sff8636_dom_parse(map, &sd);
825 
826 	PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
827 	PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
828 
829 	/*
830 	 * SFF-8636/8436 spec is not clear whether RX power/ TX bias
831 	 * current fields are supported or not. A valid temperature
832 	 * reading is used as existence for TX/RX power.
833 	 */
834 	if ((sd.sfp_temp[MCURR] == 0x0) ||
835 	    (sd.sfp_temp[MCURR] == (__s16)0xFFFF))
836 		return;
837 
838 	printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
839 		(sd.supports_alarms ? "Yes" : "No"));
840 
841 	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
842 		snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
843 					"Laser tx bias current", i+1);
844 		PRINT_BIAS(power_string, sd.scd[i].bias_cur);
845 	}
846 
847 	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
848 		snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
849 					"Transmit avg optical power", i+1);
850 		PRINT_xX_PWR(power_string, sd.scd[i].tx_power);
851 	}
852 
853 	if (!sd.rx_power_type)
854 		rx_power_string = "Receiver signal OMA";
855 	else
856 		rx_power_string = "Rcvr signal avg optical power";
857 
858 	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
859 		snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)",
860 					rx_power_string, i+1);
861 		PRINT_xX_PWR(power_string, sd.scd[i].rx_power);
862 	}
863 
864 	if (sd.supports_alarms) {
865 		for (i = 0; sff8636_aw_flags[i].str; ++i) {
866 			printf("\t%-41s : %s\n", sff8636_aw_flags[i].str,
867 			       map->lower_memory[sff8636_aw_flags[i].offset]
868 			       & sff8636_aw_flags[i].value ? "On" : "Off");
869 		}
870 
871 		sff_show_thresholds(sd);
872 	}
873 }
874 
sff8636_show_signals(const struct sff8636_memory_map * map)875 static void sff8636_show_signals(const struct sff8636_memory_map *map)
876 {
877 	unsigned int v;
878 
879 	/* There appears to be no Rx LOS support bit, use Tx for both */
880 	if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_LOS) {
881 		v = map->lower_memory[SFF8636_LOS_AW_OFFSET] & 0xf;
882 		sff_show_lane_status("Rx loss of signal", 4, "Yes", "No", v);
883 		v = map->lower_memory[SFF8636_LOS_AW_OFFSET] >> 4;
884 		sff_show_lane_status("Tx loss of signal", 4, "Yes", "No", v);
885 	}
886 
887 	v = map->lower_memory[SFF8636_LOL_AW_OFFSET] & 0xf;
888 	if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_RX_LOL)
889 		sff_show_lane_status("Rx loss of lock", 4, "Yes", "No", v);
890 
891 	v = map->lower_memory[SFF8636_LOL_AW_OFFSET] >> 4;
892 	if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_TX_LOL)
893 		sff_show_lane_status("Tx loss of lock", 4, "Yes", "No", v);
894 
895 	v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] & 0xf;
896 	if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_FAULT)
897 		sff_show_lane_status("Tx fault", 4, "Yes", "No", v);
898 
899 	v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] >> 4;
900 	if (map->page_00h[SFF8636_OPTION_2_OFFSET] & SFF8636_O2_TX_EQ_AUTO)
901 		sff_show_lane_status("Tx adaptive eq fault", 4, "Yes", "No", v);
902 }
903 
sff8636_show_page_zero(const struct sff8636_memory_map * map)904 static void sff8636_show_page_zero(const struct sff8636_memory_map *map)
905 {
906 	sff8636_show_ext_identifier(map);
907 	sff8636_show_connector(map);
908 	sff8636_show_transceiver(map);
909 	sff8636_show_encoding(map);
910 	sff_show_value_with_unit(map->page_00h, SFF8636_BR_NOMINAL_OFFSET,
911 				 "BR, Nominal", 100, "Mbps");
912 	sff8636_show_rate_identifier(map);
913 	sff_show_value_with_unit(map->page_00h, SFF8636_SM_LEN_OFFSET,
914 				 "Length (SMF,km)", 1, "km");
915 	sff_show_value_with_unit(map->page_00h, SFF8636_OM3_LEN_OFFSET,
916 				 "Length (OM3 50um)", 2, "m");
917 	sff_show_value_with_unit(map->page_00h, SFF8636_OM2_LEN_OFFSET,
918 				 "Length (OM2 50um)", 1, "m");
919 	sff_show_value_with_unit(map->page_00h, SFF8636_OM1_LEN_OFFSET,
920 				 "Length (OM1 62.5um)", 1, "m");
921 	sff_show_value_with_unit(map->page_00h, SFF8636_CBL_LEN_OFFSET,
922 				 "Length (Copper or Active cable)", 1, "m");
923 	sff8636_show_wavelength_or_copper_compliance(map);
924 	sff_show_ascii(map->page_00h, SFF8636_VENDOR_NAME_START_OFFSET,
925 		       SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
926 	sff8024_show_oui(map->page_00h, SFF8636_VENDOR_OUI_OFFSET);
927 	sff_show_ascii(map->page_00h, SFF8636_VENDOR_PN_START_OFFSET,
928 		       SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
929 	sff_show_ascii(map->page_00h, SFF8636_VENDOR_REV_START_OFFSET,
930 		       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
931 	sff_show_ascii(map->page_00h, SFF8636_VENDOR_SN_START_OFFSET,
932 		       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
933 	sff_show_ascii(map->page_00h, SFF8636_DATE_YEAR_OFFSET,
934 		       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
935 	sff_show_revision_compliance(map->lower_memory,
936 				     SFF8636_REV_COMPLIANCE_OFFSET);
937 	sff8636_show_signals(map);
938 }
939 
sff8636_show_all_common(const struct sff8636_memory_map * map)940 static void sff8636_show_all_common(const struct sff8636_memory_map *map)
941 {
942 	sff8636_show_identifier(map);
943 	switch (map->lower_memory[SFF8636_ID_OFFSET]) {
944 	case SFF8024_ID_QSFP:
945 	case SFF8024_ID_QSFP_PLUS:
946 	case SFF8024_ID_QSFP28:
947 		sff8636_show_page_zero(map);
948 		sff8636_show_dom(map);
949 		break;
950 	}
951 }
952 
sff8636_memory_map_init_buf(struct sff8636_memory_map * map,const __u8 * id,__u32 eeprom_len)953 static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map,
954 					const __u8 *id, __u32 eeprom_len)
955 {
956 	/* Lower Memory and Page 00h are always present.
957 	 *
958 	 * Offset into Upper Memory is between page size and twice the page
959 	 * size. Therefore, set the base address of each page to base address
960 	 * plus page size multiplied by the page number.
961 	 */
962 	map->lower_memory = id;
963 	map->page_00h = id;
964 
965 	/* Page 03h is only present when the module memory model is paged and
966 	 * not flat and when we got a big enough buffer from the kernel.
967 	 */
968 	if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
969 	    SFF8636_STATUS_PAGE_3_PRESENT ||
970 	    eeprom_len != ETH_MODULE_SFF_8636_MAX_LEN)
971 		return;
972 
973 	map->page_03h = id + 3 * SFF8636_PAGE_SIZE;
974 }
975 
sff8636_show_all_ioctl(const __u8 * id,__u32 eeprom_len)976 void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
977 {
978 	struct sff8636_memory_map map = {};
979 
980 	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD ||
981 	    id[SFF8636_ID_OFFSET] == SFF8024_ID_OSFP ||
982 	    id[SFF8636_ID_OFFSET] == SFF8024_ID_DSFP) {
983 		cmis_show_all_ioctl(id);
984 		return;
985 	}
986 
987 	sff8636_memory_map_init_buf(&map, id, eeprom_len);
988 	sff8636_show_all_common(&map);
989 }
990 
sff8636_request_init(struct ethtool_module_eeprom * request,u8 page,u32 offset)991 static void sff8636_request_init(struct ethtool_module_eeprom *request, u8 page,
992 				 u32 offset)
993 {
994 	request->offset = offset;
995 	request->length = SFF8636_PAGE_SIZE;
996 	request->page = page;
997 	request->bank = 0;
998 	request->i2c_address = SFF8636_I2C_ADDRESS;
999 	request->data = NULL;
1000 }
1001 
1002 static int
sff8636_memory_map_init_pages(struct cmd_context * ctx,struct sff8636_memory_map * map)1003 sff8636_memory_map_init_pages(struct cmd_context *ctx,
1004 			      struct sff8636_memory_map *map)
1005 {
1006 	struct ethtool_module_eeprom request;
1007 	int ret;
1008 
1009 	/* Lower Memory and Page 00h are always present.
1010 	 *
1011 	 * Offset into Upper Memory is between page size and twice the page
1012 	 * size. Therefore, set the base address of each page to its base
1013 	 * address minus page size.
1014 	 */
1015 	sff8636_request_init(&request, 0x0, 0);
1016 	ret = nl_get_eeprom_page(ctx, &request);
1017 	if (ret < 0)
1018 		return ret;
1019 	map->lower_memory = request.data;
1020 
1021 	sff8636_request_init(&request, 0x0, SFF8636_PAGE_SIZE);
1022 	ret = nl_get_eeprom_page(ctx, &request);
1023 	if (ret < 0)
1024 		return ret;
1025 	map->page_00h = request.data - SFF8636_PAGE_SIZE;
1026 
1027 	/* Page 03h is only present when the module memory model is paged and
1028 	 * not flat.
1029 	 */
1030 	if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
1031 	    SFF8636_STATUS_PAGE_3_PRESENT)
1032 		return 0;
1033 
1034 	sff8636_request_init(&request, 0x3, SFF8636_PAGE_SIZE);
1035 	ret = nl_get_eeprom_page(ctx, &request);
1036 	if (ret < 0)
1037 		return ret;
1038 	map->page_03h = request.data - SFF8636_PAGE_SIZE;
1039 
1040 	return 0;
1041 }
1042 
sff8636_show_all_nl(struct cmd_context * ctx)1043 int sff8636_show_all_nl(struct cmd_context *ctx)
1044 {
1045 	struct sff8636_memory_map map = {};
1046 	int ret;
1047 
1048 	ret = sff8636_memory_map_init_pages(ctx, &map);
1049 	if (ret < 0)
1050 		return ret;
1051 	sff8636_show_all_common(&map);
1052 
1053 	return 0;
1054 }
1055