xref: /aosp_15_r20/external/ethtool/cmis.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /**
2  * Description:
3  *
4  * This module adds CMIS support to ethtool. The changes are similar to
5  * the ones already existing in qsfp.c, but customized to use the memory
6  * addresses and logic as defined in the specification's document.
7  *
8  */
9 
10 #include <stdio.h>
11 #include <math.h>
12 #include <errno.h>
13 #include "internal.h"
14 #include "sff-common.h"
15 #include "cmis.h"
16 #include "netlink/extapi.h"
17 
18 /* The maximum number of supported Banks. Relevant documents:
19  * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40
20  */
21 #define CMIS_MAX_BANKS	4
22 #define CMIS_CHANNELS_PER_BANK	8
23 #define CMIS_MAX_CHANNEL_NUM	(CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK)
24 
25 /* We are not parsing further than Page 11h. */
26 #define CMIS_MAX_PAGES	18
27 
28 struct cmis_memory_map {
29 	const __u8 *lower_memory;
30 	const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES];
31 #define page_00h upper_memory[0x0][0x0]
32 #define page_01h upper_memory[0x0][0x1]
33 #define page_02h upper_memory[0x0][0x2]
34 };
35 
36 #define CMIS_PAGE_SIZE		0x80
37 #define CMIS_I2C_ADDRESS	0x50
38 
39 static struct {
40 	const char *str;
41 	int offset;
42 	__u8 value;	/* Alarm is on if (offset & value) != 0. */
43 } cmis_aw_mod_flags[] = {
44 	{ "Module temperature high alarm",
45 	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS },
46 	{ "Module temperature low alarm",
47 	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS },
48 	{ "Module temperature high warning",
49 	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS },
50 	{ "Module temperature low warning",
51 	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS },
52 
53 	{ "Module voltage high alarm",
54 	  CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS },
55 	{ "Module voltage low alarm",
56 	  CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS },
57 	{ "Module voltage high warning",
58 	  CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS },
59 	{ "Module voltage low warning",
60 	  CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS },
61 
62 	{ NULL, 0, 0 },
63 };
64 
65 static struct {
66 	const char *fmt_str;
67 	int offset;
68 	int adver_offset;	/* In Page 01h. */
69 	__u8 adver_value;	/* Supported if (offset & value) != 0. */
70 } cmis_aw_chan_flags[] = {
71 	{ "Laser bias current high alarm   (Chan %d)",
72 	  CMIS_TX_BIAS_AW_HALARM_OFFSET,
73 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
74 	{ "Laser bias current low alarm    (Chan %d)",
75 	  CMIS_TX_BIAS_AW_LALARM_OFFSET,
76 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
77 	{ "Laser bias current high warning (Chan %d)",
78 	  CMIS_TX_BIAS_AW_HWARN_OFFSET,
79 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
80 	{ "Laser bias current low warning  (Chan %d)",
81 	  CMIS_TX_BIAS_AW_LWARN_OFFSET,
82 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
83 
84 	{ "Laser tx power high alarm   (Channel %d)",
85 	  CMIS_TX_PWR_AW_HALARM_OFFSET,
86 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
87 	{ "Laser tx power low alarm    (Channel %d)",
88 	  CMIS_TX_PWR_AW_LALARM_OFFSET,
89 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
90 	{ "Laser tx power high warning (Channel %d)",
91 	  CMIS_TX_PWR_AW_HWARN_OFFSET,
92 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
93 	{ "Laser tx power low warning  (Channel %d)",
94 	  CMIS_TX_PWR_AW_LWARN_OFFSET,
95 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
96 
97 	{ "Laser rx power high alarm   (Channel %d)",
98 	  CMIS_RX_PWR_AW_HALARM_OFFSET,
99 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
100 	{ "Laser rx power low alarm    (Channel %d)",
101 	  CMIS_RX_PWR_AW_LALARM_OFFSET,
102 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
103 	{ "Laser rx power high warning (Channel %d)",
104 	  CMIS_RX_PWR_AW_HWARN_OFFSET,
105 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
106 	{ "Laser rx power low warning  (Channel %d)",
107 	  CMIS_RX_PWR_AW_LWARN_OFFSET,
108 	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
109 
110 	{ NULL, 0, 0, 0 },
111 };
112 
cmis_show_identifier(const struct cmis_memory_map * map)113 static void cmis_show_identifier(const struct cmis_memory_map *map)
114 {
115 	sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET);
116 }
117 
cmis_show_connector(const struct cmis_memory_map * map)118 static void cmis_show_connector(const struct cmis_memory_map *map)
119 {
120 	sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET);
121 }
122 
cmis_show_oui(const struct cmis_memory_map * map)123 static void cmis_show_oui(const struct cmis_memory_map *map)
124 {
125 	sff8024_show_oui(map->page_00h, CMIS_VENDOR_OUI_OFFSET);
126 }
127 
128 /**
129  * Print the revision compliance. Relevant documents:
130  * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
131  * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
132  */
cmis_show_rev_compliance(const struct cmis_memory_map * map)133 static void cmis_show_rev_compliance(const struct cmis_memory_map *map)
134 {
135 	__u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET];
136 	int major = (rev >> 4) & 0x0F;
137 	int minor = rev & 0x0F;
138 
139 	printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
140 }
141 
142 static void
cmis_show_signals_one(const struct cmis_memory_map * map,const char * name,int off,int ioff,unsigned int imask)143 cmis_show_signals_one(const struct cmis_memory_map *map, const char *name,
144 		      int off, int ioff, unsigned int imask)
145 {
146 	unsigned int v;
147 	int i;
148 
149 	if (!map->page_01h)
150 		return;
151 
152 	v = 0;
153 	for (i = 0; i < CMIS_MAX_BANKS && map->upper_memory[i][0x11]; i++)
154 		v |= map->upper_memory[i][0x11][off] << (i * 8);
155 
156 	if (map->page_01h[ioff] & imask)
157 		sff_show_lane_status(name, i * 8, "Yes", "No", v);
158 }
159 
cmis_show_signals(const struct cmis_memory_map * map)160 static void cmis_show_signals(const struct cmis_memory_map *map)
161 {
162 	cmis_show_signals_one(map, "Rx loss of signal", CMIS_RX_LOS_OFFSET,
163 			      CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOS);
164 	cmis_show_signals_one(map, "Tx loss of signal", CMIS_TX_LOS_OFFSET,
165 			      CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOS);
166 
167 	cmis_show_signals_one(map, "Rx loss of lock", CMIS_RX_LOL_OFFSET,
168 			      CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOL);
169 	cmis_show_signals_one(map, "Tx loss of lock", CMIS_TX_LOL_OFFSET,
170 			      CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOL);
171 
172 	cmis_show_signals_one(map, "Tx fault", CMIS_TX_FAIL_OFFSET,
173 			      CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_FAIL);
174 
175 	cmis_show_signals_one(map, "Tx adaptive eq fault",
176 			      CMIS_TX_EQ_FAIL_OFFSET, CMIS_DIAG_FLAGS_TX_OFFSET,
177 			      CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL);
178 }
179 
180 /**
181  * Print information about the device's power consumption.
182  * Relevant documents:
183  * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
184  * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
185  * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
186  */
cmis_show_power_info(const struct cmis_memory_map * map)187 static void cmis_show_power_info(const struct cmis_memory_map *map)
188 {
189 	float max_power = 0.0f;
190 	__u8 base_power = 0;
191 	__u8 power_class;
192 
193 	/* Get the power class (first 3 most significat bytes) */
194 	power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07;
195 
196 	/* Get the base power in multiples of 0.25W */
197 	base_power = map->page_00h[CMIS_PWR_MAX_POWER_OFFSET];
198 	max_power = base_power * 0.25f;
199 
200 	printf("\t%-41s : %d\n", "Power class", power_class + 1);
201 	printf("\t%-41s : %.02fW\n", "Max power", max_power);
202 }
203 
204 /**
205  * Print the cable assembly length, for both passive copper and active
206  * optical or electrical cables. The base length (bits 5-0) must be
207  * multiplied with the SMF length multiplier (bits 7-6) to obtain the
208  * correct value. Relevant documents:
209  * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
210  * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
211  */
cmis_show_cbl_asm_len(const struct cmis_memory_map * map)212 static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map)
213 {
214 	static const char *fn = "Cable assembly length";
215 	float mul = 1.0f;
216 	float val = 0.0f;
217 
218 	/* Check if max length */
219 	if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) {
220 		printf("\t%-41s : > 6.3km\n", fn);
221 		return;
222 	}
223 
224 	/* Get the multiplier from the first two bits */
225 	switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
226 	case CMIS_MULTIPLIER_00:
227 		mul = 0.1f;
228 		break;
229 	case CMIS_MULTIPLIER_01:
230 		mul = 1.0f;
231 		break;
232 	case CMIS_MULTIPLIER_10:
233 		mul = 10.0f;
234 		break;
235 	case CMIS_MULTIPLIER_11:
236 		mul = 100.0f;
237 		break;
238 	default:
239 		break;
240 	}
241 
242 	/* Get base value from first 6 bits and multiply by mul */
243 	val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
244 	val = (float)val * mul;
245 	printf("\t%-41s : %0.2fm\n", fn, val);
246 }
247 
248 /**
249  * Print the length for SMF fiber. The base length (bits 5-0) must be
250  * multiplied with the SMF length multiplier (bits 7-6) to obtain the
251  * correct value. Relevant documents:
252  * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
253  * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
254  */
cmis_print_smf_cbl_len(const struct cmis_memory_map * map)255 static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map)
256 {
257 	static const char *fn = "Length (SMF)";
258 	float mul = 1.0f;
259 	float val = 0.0f;
260 
261 	if (!map->page_01h)
262 		return;
263 
264 	/* Get the multiplier from the first two bits */
265 	switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
266 	case CMIS_MULTIPLIER_00:
267 		mul = 0.1f;
268 		break;
269 	case CMIS_MULTIPLIER_01:
270 		mul = 1.0f;
271 		break;
272 	default:
273 		break;
274 	}
275 
276 	/* Get base value from first 6 bits and multiply by mul */
277 	val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
278 	val = (float)val * mul;
279 	printf("\t%-41s : %0.2fkm\n", fn, val);
280 }
281 
282 /**
283  * Print relevant signal integrity control properties. Relevant documents:
284  * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
285  * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
286  */
cmis_show_sig_integrity(const struct cmis_memory_map * map)287 static void cmis_show_sig_integrity(const struct cmis_memory_map *map)
288 {
289 	if (!map->page_01h)
290 		return;
291 
292 	/* CDR Bypass control: 2nd bit from each byte */
293 	printf("\t%-41s : ", "Tx CDR bypass control");
294 	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02));
295 
296 	printf("\t%-41s : ", "Rx CDR bypass control");
297 	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02));
298 
299 	/* CDR Implementation: 1st bit from each byte */
300 	printf("\t%-41s : ", "Tx CDR");
301 	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01));
302 
303 	printf("\t%-41s : ", "Rx CDR");
304 	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x01));
305 }
306 
307 /**
308  * Print relevant media interface technology info. Relevant documents:
309  * [1] CMIS Rev. 3
310  * --> pag. 61, section 1.7.3.14, Table 36
311  * --> pag. 64, section 1.7.4.3, 1.7.4.4
312  * [2] CMIS Rev. 4
313  * --> pag. 97, section 8.3.14, Table 8-24
314  * --> pag. 98, section 8.4, Table 8-25
315  * --> page 100, section 8.4.3, 8.4.4
316  */
cmis_show_mit_compliance(const struct cmis_memory_map * map)317 static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
318 {
319 	static const char *cc = " (Copper cable,";
320 
321 	printf("\t%-41s : 0x%02x", "Transmitter technology",
322 	       map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]);
323 
324 	switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) {
325 	case CMIS_850_VCSEL:
326 		printf(" (850 nm VCSEL)\n");
327 		break;
328 	case CMIS_1310_VCSEL:
329 		printf(" (1310 nm VCSEL)\n");
330 		break;
331 	case CMIS_1550_VCSEL:
332 		printf(" (1550 nm VCSEL)\n");
333 		break;
334 	case CMIS_1310_FP:
335 		printf(" (1310 nm FP)\n");
336 		break;
337 	case CMIS_1310_DFB:
338 		printf(" (1310 nm DFB)\n");
339 		break;
340 	case CMIS_1550_DFB:
341 		printf(" (1550 nm DFB)\n");
342 		break;
343 	case CMIS_1310_EML:
344 		printf(" (1310 nm EML)\n");
345 		break;
346 	case CMIS_1550_EML:
347 		printf(" (1550 nm EML)\n");
348 		break;
349 	case CMIS_OTHERS:
350 		printf(" (Others/Undefined)\n");
351 		break;
352 	case CMIS_1490_DFB:
353 		printf(" (1490 nm DFB)\n");
354 		break;
355 	case CMIS_COPPER_UNEQUAL:
356 		printf("%s unequalized)\n", cc);
357 		break;
358 	case CMIS_COPPER_PASS_EQUAL:
359 		printf("%s passive equalized)\n", cc);
360 		break;
361 	case CMIS_COPPER_NF_EQUAL:
362 		printf("%s near and far end limiting active equalizers)\n", cc);
363 		break;
364 	case CMIS_COPPER_F_EQUAL:
365 		printf("%s far end limiting active equalizers)\n", cc);
366 		break;
367 	case CMIS_COPPER_N_EQUAL:
368 		printf("%s near end limiting active equalizers)\n", cc);
369 		break;
370 	case CMIS_COPPER_LINEAR_EQUAL:
371 		printf("%s linear active equalizers)\n", cc);
372 		break;
373 	}
374 
375 	if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) {
376 		printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
377 		       map->page_00h[CMIS_COPPER_ATT_5GHZ]);
378 		printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
379 		       map->page_00h[CMIS_COPPER_ATT_7GHZ]);
380 		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
381 		       map->page_00h[CMIS_COPPER_ATT_12P9GHZ]);
382 		printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
383 		       map->page_00h[CMIS_COPPER_ATT_25P8GHZ]);
384 	} else if (map->page_01h) {
385 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
386 		       (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) |
387 			  map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05));
388 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
389 		       (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) |
390 			  map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005));
391 	}
392 }
393 
394 /**
395  * Print relevant info about the maximum supported fiber media length
396  * for each type of fiber media at the maximum module-supported bit rate.
397  * Relevant documents:
398  * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
399  * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
400  */
cmis_show_link_len(const struct cmis_memory_map * map)401 static void cmis_show_link_len(const struct cmis_memory_map *map)
402 {
403 	cmis_print_smf_cbl_len(map);
404 	if (!map->page_01h)
405 		return;
406 	sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET,
407 				 "Length (OM5)", 2, "m");
408 	sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET,
409 				 "Length (OM4)", 2, "m");
410 	sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET,
411 				 "Length (OM3 50/125um)", 2, "m");
412 	sff_show_value_with_unit(map->page_01h, CMIS_OM2_LEN_OFFSET,
413 				 "Length (OM2 50/125um)", 1, "m");
414 }
415 
416 /**
417  * Show relevant information about the vendor. Relevant documents:
418  * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
419  * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
420  */
cmis_show_vendor_info(const struct cmis_memory_map * map)421 static void cmis_show_vendor_info(const struct cmis_memory_map *map)
422 {
423 	const char *clei;
424 
425 	sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET,
426 		       CMIS_VENDOR_NAME_END_OFFSET, "Vendor name");
427 	cmis_show_oui(map);
428 	sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET,
429 		       CMIS_VENDOR_PN_END_OFFSET, "Vendor PN");
430 	sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET,
431 		       CMIS_VENDOR_REV_END_OFFSET, "Vendor rev");
432 	sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET,
433 		       CMIS_VENDOR_SN_END_OFFSET, "Vendor SN");
434 	sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET,
435 		       CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
436 
437 	clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET);
438 	if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN))
439 		sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET,
440 			       CMIS_CLEI_END_OFFSET, "CLEI code");
441 }
442 
443 /* Print the current Module State. Relevant documents:
444  * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3
445  * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4
446  * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6
447  */
cmis_show_mod_state(const struct cmis_memory_map * map)448 static void cmis_show_mod_state(const struct cmis_memory_map *map)
449 {
450 	__u8 mod_state;
451 
452 	mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
453 		     CMIS_MODULE_STATE_MASK) >> 1;
454 	printf("\t%-41s : 0x%02x", "Module State", mod_state);
455 	switch (mod_state) {
456 	case CMIS_MODULE_STATE_MODULE_LOW_PWR:
457 		printf(" (ModuleLowPwr)\n");
458 		break;
459 	case CMIS_MODULE_STATE_MODULE_PWR_UP:
460 		printf(" (ModulePwrUp)\n");
461 		break;
462 	case CMIS_MODULE_STATE_MODULE_READY:
463 		printf(" (ModuleReady)\n");
464 		break;
465 	case CMIS_MODULE_STATE_MODULE_PWR_DN:
466 		printf(" (ModulePwrDn)\n");
467 		break;
468 	case CMIS_MODULE_STATE_MODULE_FAULT:
469 		printf(" (ModuleFault)\n");
470 		break;
471 	default:
472 		printf(" (reserved or unknown)\n");
473 		break;
474 	}
475 }
476 
477 /* Print the Module Fault Information. Relevant documents:
478  * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12
479  * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15
480  */
cmis_show_mod_fault_cause(const struct cmis_memory_map * map)481 static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map)
482 {
483 	__u8 mod_state, fault_cause;
484 
485 	mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
486 		     CMIS_MODULE_STATE_MASK) >> 1;
487 	if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT)
488 		return;
489 
490 	fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET];
491 	printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause);
492 	switch (fault_cause) {
493 	case CMIS_MODULE_FAULT_NO_FAULT:
494 		printf(" (No fault detected / not supported)\n");
495 		break;
496 	case CMIS_MODULE_FAULT_TEC_RUNAWAY:
497 		printf(" (TEC runaway)\n");
498 		break;
499 	case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED:
500 		printf(" (Data memory corrupted)\n");
501 		break;
502 	case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED:
503 		printf(" (Program memory corrupted)\n");
504 		break;
505 	default:
506 		printf(" (reserved or unknown)\n");
507 		break;
508 	}
509 }
510 
511 /* Print the current Module-Level Controls. Relevant documents:
512  * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12
513  * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10
514  */
cmis_show_mod_lvl_controls(const struct cmis_memory_map * map)515 static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map)
516 {
517 	printf("\t%-41s : ", "LowPwrAllowRequestHW");
518 	printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
519 			     CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK));
520 	printf("\t%-41s : ", "LowPwrRequestSW");
521 	printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
522 			     CMIS_LOW_PWR_REQUEST_SW_MASK));
523 }
524 
cmis_parse_dom_power_type(const struct cmis_memory_map * map,struct sff_diags * sd)525 static void cmis_parse_dom_power_type(const struct cmis_memory_map *map,
526 				      struct sff_diags *sd)
527 {
528 	sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] &
529 			    CMIS_RX_PWR_TYPE_MASK;
530 	sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
531 			    CMIS_TX_PWR_MON_MASK;
532 }
533 
cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map * map,struct sff_diags * sd)534 static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map,
535 					    struct sff_diags *sd)
536 {
537 	sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory,
538 						   CMIS_CURR_VCC_OFFSET);
539 	sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory,
540 						       CMIS_CURR_TEMP_OFFSET);
541 }
542 
cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map * map,struct sff_diags * sd)543 static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map,
544 					  struct sff_diags *sd)
545 {
546 	/* Page is not present in IOCTL path. */
547 	if (!map->page_02h)
548 		return;
549 	sd->supports_alarms = 1;
550 
551 	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
552 						   CMIS_VCC_HALRM_OFFSET);
553 	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
554 						   CMIS_VCC_LALRM_OFFSET);
555 	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
556 						   CMIS_VCC_HWARN_OFFSET);
557 	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
558 						   CMIS_VCC_LWARN_OFFSET);
559 
560 	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
561 						       CMIS_TEMP_HALRM_OFFSET);
562 	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
563 						       CMIS_TEMP_LALRM_OFFSET);
564 	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
565 						       CMIS_TEMP_HWARN_OFFSET);
566 	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
567 						       CMIS_TEMP_LWARN_OFFSET);
568 }
569 
cmis_tx_bias_mul(const struct cmis_memory_map * map)570 static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map)
571 {
572 	switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
573 		CMIS_TX_BIAS_MUL_MASK) {
574 	case CMIS_TX_BIAS_MUL_1:
575 		return 0;
576 	case CMIS_TX_BIAS_MUL_2:
577 		return 1;
578 	case CMIS_TX_BIAS_MUL_4:
579 		return 2;
580 	}
581 
582 	return 0;
583 }
584 
585 static void
cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map * map,struct sff_diags * sd,int bank)586 cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map,
587 				      struct sff_diags *sd, int bank)
588 {
589 	const __u8 *page_11h = map->upper_memory[bank][0x11];
590 	int i;
591 
592 	if (!page_11h)
593 		return;
594 
595 	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
596 		__u8 tx_bias_offset, rx_power_offset, tx_power_offset;
597 		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
598 		__u8 bias_mul = cmis_tx_bias_mul(map);
599 
600 		tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16);
601 		rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16);
602 		tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16);
603 
604 		sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h,
605 							   tx_bias_offset);
606 		sd->scd[chan].bias_cur >>= bias_mul;
607 		sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h,
608 							   rx_power_offset);
609 		sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h,
610 							   tx_power_offset);
611 	}
612 }
613 
cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map * map,struct sff_diags * sd)614 static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
615 					     struct sff_diags *sd)
616 {
617 	int i;
618 
619 	for (i = 0; i < CMIS_MAX_BANKS; i++)
620 		cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i);
621 }
622 
cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map * map,struct sff_diags * sd)623 static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map,
624 					   struct sff_diags *sd)
625 {
626 	__u8 bias_mul = cmis_tx_bias_mul(map);
627 
628 	if (!map->page_02h)
629 		return;
630 
631 	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
632 						CMIS_TX_BIAS_HALRM_OFFSET);
633 	sd->bias_cur[HALRM] >>= bias_mul;
634 	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
635 						CMIS_TX_BIAS_LALRM_OFFSET);
636 	sd->bias_cur[LALRM] >>= bias_mul;
637 	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
638 						CMIS_TX_BIAS_HWARN_OFFSET);
639 	sd->bias_cur[HWARN] >>= bias_mul;
640 	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
641 						CMIS_TX_BIAS_LWARN_OFFSET);
642 	sd->bias_cur[LWARN] >>= bias_mul;
643 
644 	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
645 						CMIS_TX_PWR_HALRM_OFFSET);
646 	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
647 						CMIS_TX_PWR_LALRM_OFFSET);
648 	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
649 						CMIS_TX_PWR_HWARN_OFFSET);
650 	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
651 						CMIS_TX_PWR_LWARN_OFFSET);
652 
653 	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
654 						CMIS_RX_PWR_HALRM_OFFSET);
655 	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
656 						CMIS_RX_PWR_LALRM_OFFSET);
657 	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
658 						CMIS_RX_PWR_HWARN_OFFSET);
659 	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
660 						CMIS_RX_PWR_LWARN_OFFSET);
661 }
662 
cmis_parse_dom(const struct cmis_memory_map * map,struct sff_diags * sd)663 static void cmis_parse_dom(const struct cmis_memory_map *map,
664 			   struct sff_diags *sd)
665 {
666 	cmis_parse_dom_power_type(map, sd);
667 	cmis_parse_dom_mod_lvl_monitors(map, sd);
668 	cmis_parse_dom_mod_lvl_thresh(map, sd);
669 	cmis_parse_dom_chan_lvl_monitors(map, sd);
670 	cmis_parse_dom_chan_lvl_thresh(map, sd);
671 }
672 
673 /* Print module-level monitoring values. Relevant documents:
674  * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9
675  */
cmis_show_dom_mod_lvl_monitors(const struct sff_diags * sd)676 static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd)
677 {
678 	PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]);
679 	PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]);
680 }
681 
682 /* Print channel Tx laser bias current. Relevant documents:
683  * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
684  */
685 static void
cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map * map,const struct sff_diags * sd,int bank)686 cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map,
687 				    const struct sff_diags *sd, int bank)
688 {
689 	const __u8 *page_11h = map->upper_memory[bank][0x11];
690 	int i;
691 
692 	if (!page_11h)
693 		return;
694 
695 	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
696 		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
697 		char fmt_str[80];
698 
699 		snprintf(fmt_str, 80, "%s (Channel %d)",
700 			 "Laser tx bias current", chan + 1);
701 		PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur);
702 	}
703 }
704 
cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map * map,const struct sff_diags * sd)705 static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map,
706 					   const struct sff_diags *sd)
707 {
708 	int i;
709 
710 	if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
711 	     CMIS_TX_BIAS_MON_MASK))
712 		return;
713 
714 	for (i = 0; i < CMIS_MAX_BANKS; i++)
715 		cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i);
716 }
717 
718 /* Print channel Tx average optical power. Relevant documents:
719  * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
720  */
721 static void
cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map * map,const struct sff_diags * sd,int bank)722 cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map,
723 				     const struct sff_diags *sd, int bank)
724 {
725 	const __u8 *page_11h = map->upper_memory[bank][0x11];
726 	int i;
727 
728 	if (!page_11h)
729 		return;
730 
731 	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
732 		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
733 		char fmt_str[80];
734 
735 		snprintf(fmt_str, 80, "%s (Channel %d)",
736 			 "Transmit avg optical power", chan + 1);
737 		PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power);
738 	}
739 }
740 
cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map * map,const struct sff_diags * sd)741 static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map,
742 					    const struct sff_diags *sd)
743 {
744 	int i;
745 
746 	if (!sd->tx_power_type)
747 		return;
748 
749 	for (i = 0; i < CMIS_MAX_BANKS; i++)
750 		cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i);
751 }
752 
753 /* Print channel Rx input optical power. Relevant documents:
754  * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
755  */
756 static void
cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map * map,const struct sff_diags * sd,int bank)757 cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map,
758 				     const struct sff_diags *sd, int bank)
759 {
760 	const __u8 *page_11h = map->upper_memory[bank][0x11];
761 	int i;
762 
763 	if (!page_11h)
764 		return;
765 
766 	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
767 		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
768 		char *rx_power_str;
769 		char fmt_str[80];
770 
771 		if (!sd->rx_power_type)
772 			rx_power_str = "Receiver signal OMA";
773 		else
774 			rx_power_str = "Rcvr signal avg optical power";
775 
776 		snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str,
777 			 chan + 1);
778 		PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power);
779 	}
780 }
781 
cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map * map,const struct sff_diags * sd)782 static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map,
783 					    const struct sff_diags *sd)
784 {
785 	int i;
786 
787 	if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK))
788 		return;
789 
790 	for (i = 0; i < CMIS_MAX_BANKS; i++)
791 		cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i);
792 }
793 
cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map * map,const struct sff_diags * sd)794 static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
795 					    const struct sff_diags *sd)
796 {
797 	cmis_show_dom_chan_lvl_tx_bias(map, sd);
798 	cmis_show_dom_chan_lvl_tx_power(map, sd);
799 	cmis_show_dom_chan_lvl_rx_power(map, sd);
800 }
801 
802 /* Print module-level flags. Relevant documents:
803  * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8
804  */
cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map * map)805 static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map)
806 {
807 	int i;
808 
809 	for (i = 0; cmis_aw_mod_flags[i].str; i++) {
810 		printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str,
811 		       map->lower_memory[cmis_aw_mod_flags[i].offset] &
812 		       cmis_aw_mod_flags[i].value ? "On" : "Off");
813 	}
814 }
815 
816 /* Print channel-level flags. Relevant documents:
817  * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77
818  * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78
819  */
cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map * map,int bank,int chan)820 static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map,
821 					      int bank, int chan)
822 {
823 	const __u8 *page_11h = map->upper_memory[bank][0x11];
824 	int i;
825 
826 	for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) {
827 		char str[80];
828 
829 		if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] &
830 		      cmis_aw_chan_flags[i].adver_value))
831 			continue;
832 
833 		snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1);
834 		printf("\t%-41s : %s\n", str,
835 		       page_11h[cmis_aw_chan_flags[i].offset] & chan ?
836 		       "On" : "Off");
837 	}
838 }
839 
840 static void
cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map * map,int bank)841 cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map,
842 				  int bank)
843 {
844 	const __u8 *page_11h = map->upper_memory[bank][0x11];
845 	int i;
846 
847 	if (!page_11h)
848 		return;
849 
850 	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
851 		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
852 
853 		cmis_show_dom_chan_lvl_flags_chan(map, bank, chan);
854 	}
855 }
856 
cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map * map)857 static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map)
858 {
859 	int i;
860 
861 	for (i = 0; i < CMIS_MAX_BANKS; i++)
862 		cmis_show_dom_chan_lvl_flags_bank(map, i);
863 }
864 
865 
cmis_show_dom(const struct cmis_memory_map * map)866 static void cmis_show_dom(const struct cmis_memory_map *map)
867 {
868 	struct sff_diags sd = {};
869 
870 	/* Diagnostic information is only relevant when the module memory
871 	 * model is paged and not flat.
872 	 */
873 	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
874 	    CMIS_MEMORY_MODEL_MASK)
875 		return;
876 
877 	cmis_parse_dom(map, &sd);
878 
879 	cmis_show_dom_mod_lvl_monitors(&sd);
880 	cmis_show_dom_chan_lvl_monitors(map, &sd);
881 	cmis_show_dom_mod_lvl_flags(map);
882 	cmis_show_dom_chan_lvl_flags(map);
883 	if (sd.supports_alarms)
884 		sff_show_thresholds(sd);
885 }
886 
cmis_show_all_common(const struct cmis_memory_map * map)887 static void cmis_show_all_common(const struct cmis_memory_map *map)
888 {
889 	cmis_show_identifier(map);
890 	cmis_show_power_info(map);
891 	cmis_show_connector(map);
892 	cmis_show_cbl_asm_len(map);
893 	cmis_show_sig_integrity(map);
894 	cmis_show_mit_compliance(map);
895 	cmis_show_link_len(map);
896 	cmis_show_vendor_info(map);
897 	cmis_show_rev_compliance(map);
898 	cmis_show_signals(map);
899 	cmis_show_mod_state(map);
900 	cmis_show_mod_fault_cause(map);
901 	cmis_show_mod_lvl_controls(map);
902 	cmis_show_dom(map);
903 }
904 
cmis_memory_map_init_buf(struct cmis_memory_map * map,const __u8 * id)905 static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
906 				     const __u8 *id)
907 {
908 	/* Lower Memory and Page 00h are always present.
909 	 *
910 	 * Offset into Upper Memory is between page size and twice the page
911 	 * size. Therefore, set the base address of each page to base address
912 	 * plus page size multiplied by the page number.
913 	 */
914 	map->lower_memory = id;
915 	map->page_00h = id;
916 
917 	/* Page 01h is only present when the module memory model is paged and
918 	 * not flat.
919 	 */
920 	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
921 	    CMIS_MEMORY_MODEL_MASK)
922 		return;
923 
924 	map->page_01h = id + CMIS_PAGE_SIZE;
925 }
926 
cmis_show_all_ioctl(const __u8 * id)927 void cmis_show_all_ioctl(const __u8 *id)
928 {
929 	struct cmis_memory_map map = {};
930 
931 	cmis_memory_map_init_buf(&map, id);
932 	cmis_show_all_common(&map);
933 }
934 
cmis_request_init(struct ethtool_module_eeprom * request,u8 bank,u8 page,u32 offset)935 static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank,
936 			      u8 page, u32 offset)
937 {
938 	request->offset = offset;
939 	request->length = CMIS_PAGE_SIZE;
940 	request->page = page;
941 	request->bank = bank;
942 	request->i2c_address = CMIS_I2C_ADDRESS;
943 	request->data = NULL;
944 }
945 
cmis_num_banks_get(const struct cmis_memory_map * map,int * p_num_banks)946 static int cmis_num_banks_get(const struct cmis_memory_map *map,
947 			      int *p_num_banks)
948 {
949 	switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] &
950 		CMIS_BANKS_SUPPORTED_MASK) {
951 	case CMIS_BANK_0_SUPPORTED:
952 		*p_num_banks = 1;
953 		break;
954 	case CMIS_BANK_0_1_SUPPORTED:
955 		*p_num_banks = 2;
956 		break;
957 	case CMIS_BANK_0_3_SUPPORTED:
958 		*p_num_banks = 4;
959 		break;
960 	default:
961 		return -EINVAL;
962 	}
963 
964 	return 0;
965 }
966 
967 static int
cmis_memory_map_init_pages(struct cmd_context * ctx,struct cmis_memory_map * map)968 cmis_memory_map_init_pages(struct cmd_context *ctx,
969 			   struct cmis_memory_map *map)
970 {
971 	struct ethtool_module_eeprom request;
972 	int num_banks, i, ret;
973 
974 	/* Lower Memory and Page 00h are always present.
975 	 *
976 	 * Offset into Upper Memory is between page size and twice the page
977 	 * size. Therefore, set the base address of each page to its base
978 	 * address minus page size.
979 	 */
980 	cmis_request_init(&request, 0, 0x0, 0);
981 	ret = nl_get_eeprom_page(ctx, &request);
982 	if (ret < 0)
983 		return ret;
984 	map->lower_memory = request.data;
985 
986 	cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE);
987 	ret = nl_get_eeprom_page(ctx, &request);
988 	if (ret < 0)
989 		return ret;
990 	map->page_00h = request.data - CMIS_PAGE_SIZE;
991 
992 	/* Pages 01h and 02h are only present when the module memory model is
993 	 * paged and not flat.
994 	 */
995 	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
996 	    CMIS_MEMORY_MODEL_MASK)
997 		return 0;
998 
999 	cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE);
1000 	ret = nl_get_eeprom_page(ctx, &request);
1001 	if (ret < 0)
1002 		return ret;
1003 	map->page_01h = request.data - CMIS_PAGE_SIZE;
1004 
1005 	cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE);
1006 	ret = nl_get_eeprom_page(ctx, &request);
1007 	if (ret < 0)
1008 		return ret;
1009 	map->page_02h = request.data - CMIS_PAGE_SIZE;
1010 
1011 	/* Bank 0 of Page 11h provides lane-specific registers for the first 8
1012 	 * lanes, and each additional Banks provides support for an additional
1013 	 * 8 lanes. Only initialize supported Banks.
1014 	 */
1015 	ret = cmis_num_banks_get(map, &num_banks);
1016 	if (ret < 0)
1017 		return ret;
1018 
1019 	for (i = 0; i < num_banks; i++) {
1020 		cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE);
1021 		ret = nl_get_eeprom_page(ctx, &request);
1022 		if (ret < 0)
1023 			return ret;
1024 		map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE;
1025 	}
1026 
1027 	return 0;
1028 }
1029 
cmis_show_all_nl(struct cmd_context * ctx)1030 int cmis_show_all_nl(struct cmd_context *ctx)
1031 {
1032 	struct cmis_memory_map map = {};
1033 	int ret;
1034 
1035 	ret = cmis_memory_map_init_pages(ctx, &map);
1036 	if (ret < 0)
1037 		return ret;
1038 	cmis_show_all_common(&map);
1039 
1040 	return 0;
1041 }
1042