xref: /dpdk/drivers/net/ntnic/nim/i2c_nim.c (revision 3489b87b497ed477257f5ed5b112c27c1407a68d)
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright(c) 2023 Napatech A/S
4  */
5 
6 #include <string.h>
7 
8 #include "nthw_drv.h"
9 #include "i2c_nim.h"
10 #include "ntlog.h"
11 #include "nt_util.h"
12 #include "ntnic_mod_reg.h"
13 #include "qsfp_registers.h"
14 #include "nim_defines.h"
15 
16 #define NIM_READ false
17 #define NIM_WRITE true
18 #define NIM_PAGE_SEL_REGISTER 127
19 #define NIM_I2C_0XA0 0xA0	/* Basic I2C address */
20 
21 
22 static bool page_addressing(nt_nim_identifier_t id)
23 {
24 	switch (id) {
25 	case NT_NIM_QSFP:
26 	case NT_NIM_QSFP_PLUS:
27 	case NT_NIM_QSFP28:
28 		return true;
29 
30 	default:
31 		NT_LOG(DBG, NTNIC, "Unknown NIM identifier %d", id);
32 		return false;
33 	}
34 }
35 
36 static nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx)
37 {
38 	return (nt_nim_identifier_t)ctx->nim_id;
39 }
40 
41 static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write, uint16_t lin_addr,
42 	uint8_t i2c_addr, uint8_t a_reg_addr, uint8_t seq_cnt,
43 	uint8_t *p_data)
44 {
45 	/* Divide i2c_addr by 2 because nthw_iic_read/writeData multiplies by 2 */
46 	const uint8_t i2c_devaddr = i2c_addr / 2U;
47 	(void)lin_addr;	/* Unused */
48 
49 	if (do_write) {
50 		if (ctx->type == I2C_HWIIC) {
51 			return nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt,
52 					p_data);
53 
54 		} else {
55 			return 0;
56 		}
57 
58 	} else if (ctx->type == I2C_HWIIC) {
59 		return nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt, p_data);
60 
61 	} else {
62 		return 0;
63 	}
64 }
65 
66 /*
67  * ------------------------------------------------------------------------------
68  * Selects a new page for page addressing. This is only relevant if the NIM
69  * supports this. Since page switching can take substantial time the current page
70  * select is read and subsequently only changed if necessary.
71  * Important:
72  * XFP Standard 8077, Ver 4.5, Page 61 states that:
73  * If the host attempts to write a table select value which is not supported in
74  * a particular module, the table select byte will revert to 01h.
75  * This can lead to some surprising result that some pages seems to be duplicated.
76  * ------------------------------------------------------------------------------
77  */
78 
79 static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel)
80 {
81 	uint8_t curr_page_sel;
82 
83 	/* Read the current page select value */
84 	if (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
85 			NIM_PAGE_SEL_REGISTER, sizeof(curr_page_sel),
86 			&curr_page_sel) != 0) {
87 		return -1;
88 	}
89 
90 	/* Only write new page select value if necessary */
91 	if (page_sel != curr_page_sel) {
92 		if (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
93 				NIM_PAGE_SEL_REGISTER, sizeof(page_sel),
94 				&page_sel) != 0) {
95 			return -1;
96 		}
97 	}
98 
99 	return 0;
100 }
101 
102 static int nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing, uint16_t lin_addr,
103 	uint16_t length, uint8_t *p_data, bool do_write)
104 {
105 	uint16_t i;
106 	uint8_t a_reg_addr;	/* The actual register address in I2C device */
107 	uint8_t i2c_addr;
108 	int block_size = 128;	/* Equal to size of MSA pages */
109 	int seq_cnt;
110 	int max_seq_cnt = 1;
111 	int multi_byte = 1;	/* One byte per I2C register is default */
112 
113 	for (i = 0; i < length;) {
114 		bool use_page_select = false;
115 
116 		/*
117 		 * Find out how much can be read from the current block in case of
118 		 * single byte access
119 		 */
120 		if (multi_byte == 1)
121 			max_seq_cnt = block_size - (lin_addr % block_size);
122 
123 		if (m_page_addressing) {
124 			if (lin_addr >= 128) {	/* Only page setup above this address */
125 				use_page_select = true;
126 
127 				/* Map to [128..255] of 0xA0 device */
128 				a_reg_addr = (uint8_t)(block_size + (lin_addr % block_size));
129 
130 			} else {
131 				a_reg_addr = (uint8_t)lin_addr;
132 			}
133 
134 			i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
135 
136 		} else if (lin_addr >= 256) {
137 			/* Map to address [0..255] of 0xA2 device */
138 			a_reg_addr = (uint8_t)(lin_addr - 256);
139 			i2c_addr = NIM_I2C_0XA2;
140 
141 		} else {
142 			a_reg_addr = (uint8_t)lin_addr;
143 			i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
144 		}
145 
146 		/* Now actually do the reading/writing */
147 		seq_cnt = length - i;	/* Number of remaining bytes */
148 
149 		if (seq_cnt > max_seq_cnt)
150 			seq_cnt = max_seq_cnt;
151 
152 		/*
153 		 * Read a number of bytes without explicitly specifying a new address.
154 		 * This can speed up I2C access since automatic incrementation of the
155 		 * I2C device internal address counter can be used. It also allows
156 		 * a HW implementation, that can deal with block access.
157 		 * Furthermore it also allows for access to data that must be accessed
158 		 * as 16bit words reading two bytes at each address eg PHYs.
159 		 */
160 		if (use_page_select) {
161 			if (nim_setup_page(ctx, (uint8_t)((lin_addr / 128) - 1)) != 0) {
162 				NT_LOG(ERR, NTNIC,
163 					"Cannot set up page for linear address %u", lin_addr);
164 				return -1;
165 			}
166 		}
167 
168 		if (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr, a_reg_addr,
169 				(uint8_t)seq_cnt, p_data) != 0) {
170 			NT_LOG(ERR, NTNIC, " Call to nim_read_write_i2c_data failed");
171 			return -1;
172 		}
173 
174 		p_data += seq_cnt;
175 		i = (uint16_t)(i + seq_cnt);
176 		lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));
177 	}
178 
179 	return 0;
180 }
181 
182 static int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data)
183 {
184 	/* Wrapper for using Mutex for QSFP TODO */
185 	return nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), lin_addr, length, data,
186 			NIM_READ);
187 }
188 
189 /* Read and return a single byte */
190 static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr)
191 {
192 	uint8_t data;
193 	read_data_lin(ctx, addr, sizeof(data), &data);
194 	return data;
195 }
196 
197 static int nim_read_id(nim_i2c_ctx_t *ctx)
198 {
199 	/* We are only reading the first byte so we don't care about pages here. */
200 	const bool USE_PAGE_ADDRESSING = false;
201 
202 	if (nim_read_write_data_lin(ctx, USE_PAGE_ADDRESSING, NIM_IDENTIFIER_ADDR,
203 			sizeof(ctx->nim_id), &ctx->nim_id, NIM_READ) != 0) {
204 		return -1;
205 	}
206 
207 	return 0;
208 }
209 
210 static int i2c_nim_common_construct(nim_i2c_ctx_p ctx)
211 {
212 	ctx->nim_id = 0;
213 	int res;
214 
215 	if (ctx->type == I2C_HWIIC)
216 		res = nim_read_id(ctx);
217 
218 	else
219 		res = -1;
220 
221 	if (res) {
222 		NT_LOG(ERR, NTNIC, "Can't read NIM id.");
223 		return res;
224 	}
225 
226 	memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name));
227 	memset(ctx->prod_no, 0, sizeof(ctx->prod_no));
228 	memset(ctx->serial_no, 0, sizeof(ctx->serial_no));
229 	memset(ctx->date, 0, sizeof(ctx->date));
230 	memset(ctx->rev, 0, sizeof(ctx->rev));
231 
232 	ctx->content_valid = false;
233 	memset(ctx->len_info, 0, sizeof(ctx->len_info));
234 	ctx->pwr_level_req = 0;
235 	ctx->pwr_level_cur = 0;
236 	ctx->avg_pwr = false;
237 	ctx->tx_disable = false;
238 	ctx->lane_idx = -1;
239 	ctx->lane_count = 1;
240 	ctx->options = 0;
241 	return 0;
242 }
243 
244 /*
245  * Read vendor information at a certain address. Any trailing whitespace is
246  * removed and a missing string termination in the NIM data is handled.
247  */
248 static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data)
249 {
250 	const bool pg_addr = page_addressing(ctx->nim_id);
251 	int i;
252 	/* Subtract "1" from max_len that includes a terminating "0" */
253 
254 	if (nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1), (uint8_t *)p_data,
255 			NIM_READ) != 0) {
256 		return -1;
257 	}
258 
259 	/* Terminate at first found white space */
260 	for (i = 0; i < max_len - 1; i++) {
261 		if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || *p_data == '\v' ||
262 			*p_data == '\f' || *p_data == '\r') {
263 			*p_data = '\0';
264 			return 0;
265 		}
266 
267 		p_data++;
268 	}
269 
270 	/*
271 	 * Add line termination as the very last character, if it was missing in the
272 	 * NIM data
273 	 */
274 	*p_data = '\0';
275 	return 0;
276 }
277 
278 static void qsfp_read_vendor_info(nim_i2c_ctx_t *ctx)
279 {
280 	nim_read_vendor_info(ctx, QSFP_VENDOR_NAME_LIN_ADDR, sizeof(ctx->vendor_name),
281 		ctx->vendor_name);
282 	nim_read_vendor_info(ctx, QSFP_VENDOR_PN_LIN_ADDR, sizeof(ctx->prod_no), ctx->prod_no);
283 	nim_read_vendor_info(ctx, QSFP_VENDOR_SN_LIN_ADDR, sizeof(ctx->serial_no), ctx->serial_no);
284 	nim_read_vendor_info(ctx, QSFP_VENDOR_DATE_LIN_ADDR, sizeof(ctx->date), ctx->date);
285 	nim_read_vendor_info(ctx, QSFP_VENDOR_REV_LIN_ADDR, (uint8_t)(sizeof(ctx->rev) - 2),
286 		ctx->rev);	/*OBS Only two bytes*/
287 }
288 static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
289 {
290 	int res = 0;	/* unused due to no readings from HW */
291 
292 	assert(ctx && state);
293 	assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized");
294 
295 	(void)memset(state, 0, sizeof(*state));
296 
297 	switch (ctx->nim_id) {
298 	case 12U:
299 		state->br = 10U;/* QSFP: 4 x 1G = 4G */
300 		break;
301 
302 	case 13U:
303 		state->br = 103U;	/* QSFP+: 4 x 10G = 40G */
304 		break;
305 
306 	case 17U:
307 		state->br = 255U;	/* QSFP28: 4 x 25G = 100G */
308 		break;
309 
310 	default:
311 		NT_LOG(INF, NTNIC, "nim_id = %u is not an QSFP/QSFP+/QSFP28 module", ctx->nim_id);
312 		res = -1;
313 	}
314 
315 	return res;
316 }
317 
318 int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
319 {
320 	return qsfp_nim_state_build(ctx, state);
321 }
322 
323 const char *nim_id_to_text(uint8_t nim_id)
324 {
325 	switch (nim_id) {
326 	case 0x0:
327 		return "UNKNOWN";
328 
329 	case 0x0C:
330 		return "QSFP";
331 
332 	case 0x0D:
333 		return "QSFP+";
334 
335 	case 0x11:
336 		return "QSFP28";
337 
338 	default:
339 		return "ILLEGAL!";
340 	}
341 }
342 
343 /*
344  * Disable laser for specific lane or all lanes
345  */
346 int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable, int lane_idx)
347 {
348 	uint8_t value;
349 	uint8_t mask;
350 	const bool pg_addr = page_addressing(ctx->nim_id);
351 
352 	if (lane_idx < 0)	/* If no lane is specified then all lanes */
353 		mask = QSFP_SOFT_TX_ALL_DISABLE_BITS;
354 
355 	else
356 		mask = (uint8_t)(1U << lane_idx);
357 
358 	if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
359 			&value, NIM_READ) != 0) {
360 		return -1;
361 	}
362 
363 	if (disable)
364 		value |= mask;
365 
366 	else
367 		value &= (uint8_t)(~mask);
368 
369 	if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
370 			&value, NIM_WRITE) != 0) {
371 		return -1;
372 	}
373 
374 	return 0;
375 }
376 
377 /*
378  * Import length info in various units from NIM module data and convert to meters
379  */
380 static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info, uint16_t *p_nim_units)
381 {
382 	size_t i;
383 
384 	for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++)
385 		if (*(p_nim_len_info + i) == 255) {
386 			ctx->len_info[i] = 65535;
387 
388 		} else {
389 			uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i);
390 
391 			if (len > 65535)
392 				ctx->len_info[i] = 65535;
393 
394 			else
395 				ctx->len_info[i] = (uint16_t)len;
396 		}
397 }
398 
399 static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx)
400 {
401 	const bool pg_addr = page_addressing(ctx->nim_id);
402 	uint8_t options;
403 	uint8_t value;
404 	uint8_t nim_len_info[5];
405 	uint16_t nim_units[5] = { 1000, 2, 1, 1, 1 };	/* QSFP MSA units in meters */
406 	const char *yes_no[2] = { "No", "Yes" };
407 	(void)yes_no;
408 	NT_LOG(DBG, NTNIC, "Instance %d: NIM id: %s (%d)", ctx->instance,
409 		nim_id_to_text(ctx->nim_id), ctx->nim_id);
410 
411 	/* Read DMI options */
412 	if (nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR, sizeof(options),
413 			&options, NIM_READ) != 0) {
414 		return -1;
415 	}
416 
417 	ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT;
418 	NT_LOG(DBG, NTNIC, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)", ctx->instance,
419 		yes_no[ctx->avg_pwr]);
420 
421 	qsfp_read_vendor_info(ctx);
422 	NT_LOG(DBG, NTNIC,
423 		"Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)",
424 		ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no, ctx->date, ctx->rev);
425 
426 	if (nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info),
427 			nim_len_info, NIM_READ) != 0) {
428 		return -1;
429 	}
430 
431 	/*
432 	 * Returns supported length information in meters for various fibers as 5 indivi-
433 	 * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper]
434 	 * If no length information is available for a certain entry, the returned value
435 	 * will be zero. This will be the case for SFP modules - EBW entry.
436 	 * If the MSBit is set the returned value in the lower 31 bits indicates that the
437 	 * supported length is greater than this.
438 	 */
439 
440 	nim_import_len_info(ctx, nim_len_info, nim_units);
441 
442 	/* Read required power level */
443 	if (nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER, sizeof(value), &value,
444 			NIM_READ) != 0) {
445 		return -1;
446 	}
447 
448 	/*
449 	 * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43:
450 	 * If power class >= 5 setHighPower must be called for the module to be fully
451 	 * functional
452 	 */
453 	if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) {
454 		/* NIM in power class 1 - 4 */
455 		ctx->pwr_level_req = (uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) + 1);
456 
457 	} else {
458 		/* NIM in power class 5 - 7 */
459 		ctx->pwr_level_req = (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4);
460 	}
461 
462 	return 0;
463 }
464 
465 static void qsfp28_find_port_params(nim_i2c_ctx_p ctx)
466 {
467 	uint8_t fiber_chan_speed;
468 
469 	/* Table 6-17 SFF-8636 */
470 	read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed);
471 
472 	if (fiber_chan_speed & (1 << 7)) {
473 		/* SFF-8024, Rev 4.7, Table 4-4 */
474 		uint8_t extended_specification_compliance_code = 0;
475 		read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1,
476 			&extended_specification_compliance_code);
477 
478 		switch (extended_specification_compliance_code) {
479 		case 0x02:
480 			ctx->port_type = NT_PORT_TYPE_QSFP28_SR4;
481 			break;
482 
483 		case 0x03:
484 			ctx->port_type = NT_PORT_TYPE_QSFP28_LR4;
485 			break;
486 
487 		case 0x0B:
488 			ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L;
489 			break;
490 
491 		case 0x0C:
492 			ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S;
493 			break;
494 
495 		case 0x0D:
496 			ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N;
497 			break;
498 
499 		case 0x25:
500 			ctx->port_type = NT_PORT_TYPE_QSFP28_DR;
501 			break;
502 
503 		case 0x26:
504 			ctx->port_type = NT_PORT_TYPE_QSFP28_FR;
505 			break;
506 
507 		case 0x27:
508 			ctx->port_type = NT_PORT_TYPE_QSFP28_LR;
509 			break;
510 
511 		default:
512 			ctx->port_type = NT_PORT_TYPE_QSFP28;
513 		}
514 
515 	} else {
516 		ctx->port_type = NT_PORT_TYPE_QSFP28;
517 	}
518 }
519 
520 /*
521  * If true the user must actively select the desired rate. If false the module
522  * however can still support several rates without the user is required to select
523  * one of them. Supported rates must then be deduced from the product number.
524  * SFF-8636, Rev 2.10a:
525  * p40: 6.2.7 Rate Select
526  * p85: A.2 Rate Select
527  */
528 static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx)
529 {
530 	const uint8_t ext_rate_select_compl_reg_addr = 141;
531 	const uint8_t options_reg_addr = 195;
532 	const uint8_t enh_options_reg_addr = 221;
533 
534 	uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01;	/* bit: 5 */
535 
536 	if (rate_select_ena == 0)
537 		return false;
538 
539 	uint8_t rate_select_type =
540 		(read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03;	/* bit 3..2 */
541 
542 	if (rate_select_type != 2) {
543 		NT_LOG(DBG, NTNIC, "NIM has unhandled rate select type (%d)", rate_select_type);
544 		return false;
545 	}
546 
547 	uint8_t ext_rate_select_ver =
548 		read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03;	/* bit 1..0 */
549 
550 	if (ext_rate_select_ver != 0x02) {
551 		NT_LOG(DBG, NTNIC, "NIM has unhandled extended rate select version (%d)",
552 			ext_rate_select_ver);
553 		return false;
554 	}
555 
556 	return true;	/* When true selectRate() can be used */
557 }
558 
559 static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx)
560 {
561 	if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || ctx->port_type == NT_PORT_TYPE_QSFP28_DR ||
562 		ctx->port_type == NT_PORT_TYPE_QSFP28_LR) {
563 		if (ctx->lane_idx < 0)
564 			ctx->speed_mask = NT_LINK_SPEED_100G;
565 
566 		else
567 			/* PAM-4 modules can only run on all lanes together */
568 			ctx->speed_mask = 0;
569 
570 	} else {
571 		if (ctx->lane_idx < 0)
572 			ctx->speed_mask = NT_LINK_SPEED_100G;
573 
574 		else
575 			ctx->speed_mask = NT_LINK_SPEED_25G;
576 
577 		if (qsfp28_is_rate_selection_enabled(ctx)) {
578 			/*
579 			 * It is assumed that if the module supports dual rates then the other rate
580 			 * is 10G per lane or 40G for all lanes.
581 			 */
582 			if (ctx->lane_idx < 0)
583 				ctx->speed_mask |= NT_LINK_SPEED_40G;
584 
585 			else
586 				ctx->speed_mask = NT_LINK_SPEED_10G;
587 		}
588 	}
589 }
590 
591 static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx)
592 {
593 	uint8_t device_tech;
594 	read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), &device_tech);
595 
596 	switch (device_tech & 0xF0) {
597 	case 0xA0:	/* Copper cable unequalized */
598 		break;
599 
600 	case 0xC0:	/* Copper cable, near and far end limiting active equalizers */
601 	case 0xD0:	/* Copper cable, far end limiting active equalizers */
602 	case 0xE0:	/* Copper cable, near end limiting active equalizers */
603 		break;
604 
605 	default:/* Optical */
606 		ctx->port_type = NT_PORT_TYPE_QSFP_PLUS;
607 		break;
608 	}
609 }
610 
611 static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx)
612 {
613 	ctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G : (NT_LINK_SPEED_10G);
614 }
615 
616 static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx)
617 {
618 	assert(lane_idx < 4);
619 	ctx->specific_u.qsfp.qsfp28 = false;
620 	ctx->lane_idx = lane_idx;
621 	ctx->lane_count = 4;
622 }
623 
624 static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
625 {
626 	qsfpplus_construct(ctx, lane_idx);
627 	int res = qsfpplus_read_basic_data(ctx);
628 
629 	if (!res) {
630 		qsfpplus_find_port_params(ctx);
631 
632 		/*
633 		 * Read if TX_DISABLE has been implemented
634 		 * For passive optical modules this is required while it for copper and active
635 		 * optical modules is optional. Under all circumstances register 195.4 will
636 		 * indicate, if TX_DISABLE has been implemented in register 86.0-3
637 		 */
638 		uint8_t value;
639 		read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value);
640 
641 		ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0;
642 
643 		if (ctx->tx_disable)
644 			ctx->options |= (1 << NIM_OPTION_TX_DISABLE);
645 
646 		/*
647 		 * Previously - considering AFBR-89BRDZ - code tried to establish if a module was
648 		 * RxOnly by testing the state of the lasers after reset. Lasers were for this
649 		 * module default disabled.
650 		 * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was
651 		 * decided that this option should not be detected automatically but from PN
652 		 */
653 		ctx->specific_u.qsfp.rx_only = (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0;
654 		qsfpplus_set_speed_mask(ctx);
655 	}
656 
657 	return res;
658 }
659 
660 static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx)
661 {
662 	uint8_t data;
663 	bool init_complete_flag_present = false;
664 
665 	/*
666 	 * Revision compliance
667 	 * 7: SFF-8636 Rev 2.5, 2.6 and 2.7
668 	 * 8: SFF-8636 Rev 2.8, 2.9 and 2.10
669 	 */
670 	read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance),
671 		&ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
672 	NT_LOG(DBG, NTHW, "NIM RevCompliance = %d",
673 		ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
674 
675 	/* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */
676 	if (ctx->lane_idx > 0)
677 		return;
678 
679 	if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) {
680 		/* Check if init complete flag is implemented */
681 		read_data_lin(ctx, 221, sizeof(data), &data);
682 		init_complete_flag_present = (data & (1 << 4)) != 0;
683 	}
684 
685 	NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", init_complete_flag_present);
686 
687 	/*
688 	 * If the init complete flag is not present then wait 500ms that together with 500ms
689 	 * after reset (in the adapter code) should be enough to read data from upper pages
690 	 * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been
691 	 * prone to this when trying to read sensor options using getQsfpOptionsFromData()
692 	 * Probably because access to the paged address space is required.
693 	 */
694 	if (!init_complete_flag_present) {
695 		nt_os_wait_usec(500000);
696 		return;
697 	}
698 
699 	/* Otherwise wait for the init complete flag to be set */
700 	int count = 0;
701 
702 	while (true) {
703 		if (count > 10) {	/* 1 s timeout */
704 			NT_LOG(WRN, NTHW, "Timeout waiting for module ready");
705 			break;
706 		}
707 
708 		read_data_lin(ctx, 6, sizeof(data), &data);
709 
710 		if (data & 0x01) {
711 			NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100);
712 			break;
713 		}
714 
715 		nt_os_wait_usec(100000);/* 100 ms */
716 		count++;
717 	}
718 }
719 
720 static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx)
721 {
722 	const char *const nim_list[] = {
723 		"AFBR-89BDDZ",	/* Avago BiDi */
724 		"AFBR-89BRDZ",	/* Avago BiDi, RxOnly */
725 		"FTLC4352RKPL",	/* Finisar QSFP28-LR */
726 		"FTLC4352RHPL",	/* Finisar QSFP28-DR */
727 		"FTLC4352RJPL",	/* Finisar QSFP28-FR */
728 		"SFBR-89BDDZ-CS4",	/* Foxconn, QSFP28 100G/40G BiDi */
729 	};
730 
731 	for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) {
732 		if (ctx->prod_no == nim_list[i]) {
733 			ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
734 			ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true;
735 			NT_LOG(DBG, NTHW, "Found FEC info via PN list");
736 			return;
737 		}
738 	}
739 
740 	/*
741 	 * For modules not in the list find FEC info via registers
742 	 * Read if the module has controllable FEC
743 	 * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing)
744 	 * (Page 03h, Bytes 224-229)
745 	 */
746 	uint8_t data;
747 	uint16_t addr = 227 + 3 * 128;
748 	read_data_lin(ctx, addr, sizeof(data), &data);
749 
750 	/* Check if the module has FEC support that can be controlled */
751 	ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0;
752 	ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0;
753 
754 	if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)
755 		ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
756 
757 	if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)
758 		ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC);
759 }
760 
761 static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
762 {
763 	int res = qsfpplus_preinit(ctx, lane_idx);
764 
765 	if (!res) {
766 		qsfp28_wait_for_ready_after_reset(ctx);
767 		memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0,
768 			sizeof(ctx->specific_u.qsfp.specific_u.qsfp28));
769 		ctx->specific_u.qsfp.qsfp28 = true;
770 		qsfp28_find_port_params(ctx);
771 		qsfp28_get_fec_options(ctx);
772 		qsfp28_set_speed_mask(ctx);
773 	}
774 
775 	return res;
776 }
777 
778 int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra)
779 {
780 	int res = i2c_nim_common_construct(ctx);
781 
782 	switch (translate_nimid(ctx)) {
783 	case NT_NIM_QSFP_PLUS:
784 		qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
785 		break;
786 
787 	case NT_NIM_QSFP28:
788 		qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
789 		break;
790 
791 	default:
792 		res = 1;
793 		NT_LOG(ERR, NTHW, "NIM type %s is not supported.", nim_id_to_text(ctx->nim_id));
794 	}
795 
796 	return res;
797 }
798