Branch data Line data Source code
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 : 0 : static bool page_addressing(nt_nim_identifier_t id)
23 : : {
24 [ # # ]: 0 : switch (id) {
25 : : case NT_NIM_QSFP:
26 : : case NT_NIM_QSFP_PLUS:
27 : : case NT_NIM_QSFP28:
28 : : return true;
29 : :
30 : 0 : default:
31 : 0 : NT_LOG(DBG, NTNIC, "Unknown NIM identifier %d", id);
32 : 0 : return false;
33 : : }
34 : : }
35 : :
36 : : static nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx)
37 : : {
38 : 0 : return (nt_nim_identifier_t)ctx->nim_id;
39 : : }
40 : :
41 : 0 : 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 : 0 : const uint8_t i2c_devaddr = i2c_addr / 2U;
47 : : (void)lin_addr; /* Unused */
48 : :
49 [ # # ]: 0 : if (do_write) {
50 [ # # ]: 0 : if (ctx->type == I2C_HWIIC) {
51 : 0 : 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 [ # # ]: 0 : } else if (ctx->type == I2C_HWIIC) {
59 : 0 : 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 : 0 : 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 [ # # ]: 0 : 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 [ # # ]: 0 : if (page_sel != curr_page_sel) {
92 [ # # ]: 0 : 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 : 0 : return -1;
96 : : }
97 : : }
98 : :
99 : : return 0;
100 : : }
101 : :
102 : 0 : 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 [ # # ]: 0 : 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 : 0 : max_seq_cnt = block_size - (lin_addr % block_size);
122 : :
123 [ # # ]: 0 : if (m_page_addressing) {
124 [ # # ]: 0 : 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 : 0 : a_reg_addr = (uint8_t)(block_size + (lin_addr % block_size));
129 : :
130 : : } else {
131 : 0 : a_reg_addr = (uint8_t)lin_addr;
132 : : }
133 : :
134 : : i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
135 : :
136 [ # # ]: 0 : } else if (lin_addr >= 256) {
137 : : /* Map to address [0..255] of 0xA2 device */
138 : 0 : a_reg_addr = (uint8_t)(lin_addr - 256);
139 : : i2c_addr = NIM_I2C_0XA2;
140 : :
141 : : } else {
142 : 0 : 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 : 0 : 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 [ # # ]: 0 : if (use_page_select) {
161 [ # # ]: 0 : if (nim_setup_page(ctx, (uint8_t)((lin_addr / 128) - 1)) != 0) {
162 : 0 : NT_LOG(ERR, NTNIC,
163 : : "Cannot set up page for linear address %u", lin_addr);
164 : 0 : return -1;
165 : : }
166 : : }
167 : :
168 [ # # ]: 0 : 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 : 0 : NT_LOG(ERR, NTNIC, " Call to nim_read_write_i2c_data failed");
171 : 0 : return -1;
172 : : }
173 : :
174 : 0 : p_data += seq_cnt;
175 : 0 : i = (uint16_t)(i + seq_cnt);
176 : 0 : lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));
177 : : }
178 : :
179 : : return 0;
180 : : }
181 : :
182 : 0 : 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 : 0 : 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 : 0 : read_data_lin(ctx, addr, sizeof(data), &data);
194 : 0 : 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 [ # # ]: 0 : 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 : 0 : static int i2c_nim_common_construct(nim_i2c_ctx_p ctx)
211 : : {
212 : 0 : ctx->nim_id = 0;
213 : : int res;
214 : :
215 [ # # ]: 0 : if (ctx->type == I2C_HWIIC)
216 : : res = nim_read_id(ctx);
217 : :
218 : : else
219 : : res = -1;
220 : :
221 : : if (res) {
222 : 0 : NT_LOG(ERR, NTNIC, "Can't read NIM id.");
223 : 0 : return res;
224 : : }
225 : :
226 : 0 : memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name));
227 : 0 : memset(ctx->prod_no, 0, sizeof(ctx->prod_no));
228 : 0 : memset(ctx->serial_no, 0, sizeof(ctx->serial_no));
229 : 0 : memset(ctx->date, 0, sizeof(ctx->date));
230 : 0 : memset(ctx->rev, 0, sizeof(ctx->rev));
231 : :
232 : 0 : ctx->content_valid = false;
233 : 0 : memset(ctx->len_info, 0, sizeof(ctx->len_info));
234 : 0 : ctx->pwr_level_req = 0;
235 : 0 : ctx->pwr_level_cur = 0;
236 : 0 : ctx->avg_pwr = false;
237 : 0 : ctx->tx_disable = false;
238 : 0 : ctx->lane_idx = -1;
239 : 0 : ctx->lane_count = 1;
240 : 0 : ctx->options = 0;
241 : 0 : 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 : 0 : static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data)
249 : : {
250 : 0 : 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 [ # # ]: 0 : 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 [ # # ]: 0 : for (i = 0; i < max_len - 1; i++) {
261 [ # # ]: 0 : if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || *p_data == '\v' ||
262 : : *p_data == '\f' || *p_data == '\r') {
263 : 0 : *p_data = '\0';
264 : 0 : return 0;
265 : : }
266 : :
267 : 0 : 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 : 0 : *p_data = '\0';
275 : 0 : return 0;
276 : : }
277 : :
278 : 0 : static void qsfp_read_vendor_info(nim_i2c_ctx_t *ctx)
279 : : {
280 : 0 : nim_read_vendor_info(ctx, QSFP_VENDOR_NAME_LIN_ADDR, sizeof(ctx->vendor_name),
281 : 0 : ctx->vendor_name);
282 : 0 : nim_read_vendor_info(ctx, QSFP_VENDOR_PN_LIN_ADDR, sizeof(ctx->prod_no), ctx->prod_no);
283 : 0 : nim_read_vendor_info(ctx, QSFP_VENDOR_SN_LIN_ADDR, sizeof(ctx->serial_no), ctx->serial_no);
284 : 0 : nim_read_vendor_info(ctx, QSFP_VENDOR_DATE_LIN_ADDR, sizeof(ctx->date), ctx->date);
285 : 0 : nim_read_vendor_info(ctx, QSFP_VENDOR_REV_LIN_ADDR, (uint8_t)(sizeof(ctx->rev) - 2),
286 : 0 : ctx->rev); /*OBS Only two bytes*/
287 : 0 : }
288 : 0 : 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 [ # # ]: 0 : assert(ctx && state);
293 [ # # ]: 0 : assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized");
294 : :
295 : : (void)memset(state, 0, sizeof(*state));
296 : :
297 [ # # # # ]: 0 : switch (ctx->nim_id) {
298 : 0 : case 12U:
299 : 0 : state->br = 10U;/* QSFP: 4 x 1G = 4G */
300 : 0 : break;
301 : :
302 : 0 : case 13U:
303 : 0 : state->br = 103U; /* QSFP+: 4 x 10G = 40G */
304 : 0 : break;
305 : :
306 : 0 : case 17U:
307 : 0 : state->br = 255U; /* QSFP28: 4 x 25G = 100G */
308 : 0 : break;
309 : :
310 : 0 : default:
311 : 0 : NT_LOG(INF, NTNIC, "nim_id = %u is not an QSFP/QSFP+/QSFP28 module", ctx->nim_id);
312 : : res = -1;
313 : : }
314 : :
315 : 0 : return res;
316 : : }
317 : :
318 : 0 : int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
319 : : {
320 : 0 : return qsfp_nim_state_build(ctx, state);
321 : : }
322 : :
323 : 0 : const char *nim_id_to_text(uint8_t nim_id)
324 : : {
325 [ # # # # : 0 : switch (nim_id) {
# ]
326 : : case 0x0:
327 : : return "UNKNOWN";
328 : :
329 : 0 : case 0x0C:
330 : 0 : return "QSFP";
331 : :
332 : 0 : case 0x0D:
333 : 0 : return "QSFP+";
334 : :
335 : 0 : case 0x11:
336 : 0 : return "QSFP28";
337 : :
338 : 0 : default:
339 : 0 : return "ILLEGAL!";
340 : : }
341 : : }
342 : :
343 : : /*
344 : : * Disable laser for specific lane or all lanes
345 : : */
346 : 0 : 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 : 0 : const bool pg_addr = page_addressing(ctx->nim_id);
351 : :
352 [ # # ]: 0 : if (lane_idx < 0) /* If no lane is specified then all lanes */
353 : : mask = QSFP_SOFT_TX_ALL_DISABLE_BITS;
354 : :
355 : : else
356 : 0 : mask = (uint8_t)(1U << lane_idx);
357 : :
358 [ # # ]: 0 : 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 [ # # ]: 0 : if (disable)
364 : 0 : value |= mask;
365 : :
366 : : else
367 : 0 : value &= (uint8_t)(~mask);
368 : :
369 [ # # ]: 0 : if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
370 : : &value, NIM_WRITE) != 0) {
371 : 0 : 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 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++)
385 [ # # ]: 0 : if (*(p_nim_len_info + i) == 255) {
386 : 0 : ctx->len_info[i] = 65535;
387 : :
388 : : } else {
389 : 0 : uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i);
390 : :
391 [ # # ]: 0 : if (len > 65535)
392 : 0 : ctx->len_info[i] = 65535;
393 : :
394 : : else
395 : 0 : ctx->len_info[i] = (uint16_t)len;
396 : : }
397 : : }
398 : :
399 : 0 : static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx)
400 : : {
401 : 0 : 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 : 0 : uint16_t nim_units[5] = { 1000, 2, 1, 1, 1 }; /* QSFP MSA units in meters */
406 : 0 : const char *yes_no[2] = { "No", "Yes" };
407 : : (void)yes_no;
408 : 0 : 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 [ # # ]: 0 : 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 : 0 : ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT;
418 : 0 : NT_LOG(DBG, NTNIC, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)", ctx->instance,
419 : : yes_no[ctx->avg_pwr]);
420 : :
421 : 0 : qsfp_read_vendor_info(ctx);
422 : 0 : 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 [ # # ]: 0 : 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 [ # # ]: 0 : 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 [ # # ]: 0 : if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) {
454 : : /* NIM in power class 1 - 4 */
455 : 0 : 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 : 0 : ctx->pwr_level_req = (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4);
460 : : }
461 : :
462 : : return 0;
463 : : }
464 : :
465 : 0 : 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 : 0 : read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed);
471 : :
472 [ # # ]: 0 : if (fiber_chan_speed & (1 << 7)) {
473 : : /* SFF-8024, Rev 4.7, Table 4-4 */
474 : 0 : uint8_t extended_specification_compliance_code = 0;
475 : 0 : read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1,
476 : : &extended_specification_compliance_code);
477 : :
478 [ # # # # : 0 : switch (extended_specification_compliance_code) {
# # # #
# ]
479 : 0 : case 0x02:
480 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_SR4;
481 : 0 : break;
482 : :
483 : 0 : case 0x03:
484 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_LR4;
485 : 0 : break;
486 : :
487 : 0 : case 0x0B:
488 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L;
489 : 0 : break;
490 : :
491 : 0 : case 0x0C:
492 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S;
493 : 0 : break;
494 : :
495 : 0 : case 0x0D:
496 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N;
497 : 0 : break;
498 : :
499 : 0 : case 0x25:
500 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_DR;
501 : 0 : break;
502 : :
503 : 0 : case 0x26:
504 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_FR;
505 : 0 : break;
506 : :
507 : 0 : case 0x27:
508 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28_LR;
509 : 0 : break;
510 : :
511 : 0 : default:
512 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28;
513 : : }
514 : :
515 : : } else {
516 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP28;
517 : : }
518 : 0 : }
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 : 0 : 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 : 0 : uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01; /* bit: 5 */
535 : :
536 [ # # ]: 0 : if (rate_select_ena == 0)
537 : : return false;
538 : :
539 : 0 : uint8_t rate_select_type =
540 : 0 : (read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03; /* bit 3..2 */
541 : :
542 [ # # ]: 0 : if (rate_select_type != 2) {
543 : 0 : NT_LOG(DBG, NTNIC, "NIM has unhandled rate select type (%d)", rate_select_type);
544 : 0 : return false;
545 : : }
546 : :
547 : 0 : uint8_t ext_rate_select_ver =
548 : : read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03; /* bit 1..0 */
549 : :
550 [ # # ]: 0 : if (ext_rate_select_ver != 0x02) {
551 : 0 : NT_LOG(DBG, NTNIC, "NIM has unhandled extended rate select version (%d)",
552 : : ext_rate_select_ver);
553 : 0 : return false;
554 : : }
555 : :
556 : : return true; /* When true selectRate() can be used */
557 : : }
558 : :
559 : 0 : static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx)
560 : : {
561 [ # # ]: 0 : 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 [ # # ]: 0 : if (ctx->lane_idx < 0)
564 : 0 : ctx->speed_mask = NT_LINK_SPEED_100G;
565 : :
566 : : else
567 : : /* PAM-4 modules can only run on all lanes together */
568 : 0 : ctx->speed_mask = 0;
569 : :
570 : : } else {
571 [ # # ]: 0 : if (ctx->lane_idx < 0)
572 : 0 : ctx->speed_mask = NT_LINK_SPEED_100G;
573 : :
574 : : else
575 : 0 : ctx->speed_mask = NT_LINK_SPEED_25G;
576 : :
577 [ # # ]: 0 : 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 [ # # ]: 0 : if (ctx->lane_idx < 0)
583 : 0 : ctx->speed_mask |= NT_LINK_SPEED_40G;
584 : :
585 : : else
586 : 0 : ctx->speed_mask = NT_LINK_SPEED_10G;
587 : : }
588 : : }
589 : 0 : }
590 : :
591 : 0 : static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx)
592 : : {
593 : : uint8_t device_tech;
594 : 0 : read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), &device_tech);
595 : :
596 [ # # ]: 0 : 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 : 0 : default:/* Optical */
606 : 0 : ctx->port_type = NT_PORT_TYPE_QSFP_PLUS;
607 : 0 : break;
608 : : }
609 : 0 : }
610 : :
611 : : static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx)
612 : : {
613 : 0 : 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 : 0 : assert(lane_idx < 4);
619 : 0 : ctx->specific_u.qsfp.qsfp28 = false;
620 : 0 : ctx->lane_idx = lane_idx;
621 : 0 : ctx->lane_count = 4;
622 : : }
623 : :
624 [ # # ]: 0 : static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
625 : : {
626 : : qsfpplus_construct(ctx, lane_idx);
627 : 0 : int res = qsfpplus_read_basic_data(ctx);
628 : :
629 [ # # ]: 0 : if (!res) {
630 : 0 : 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 : 0 : read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value);
640 : :
641 : 0 : ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0;
642 : :
643 [ # # ]: 0 : if (ctx->tx_disable)
644 : 0 : 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 [ # # ]: 0 : ctx->specific_u.qsfp.rx_only = (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0;
654 : : qsfpplus_set_speed_mask(ctx);
655 : : }
656 : :
657 : 0 : return res;
658 : : }
659 : :
660 : 0 : 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 : 0 : read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance),
671 : 0 : &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
672 : 0 : 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 [ # # ]: 0 : if (ctx->lane_idx > 0)
677 : 0 : return;
678 : :
679 [ # # ]: 0 : if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) {
680 : : /* Check if init complete flag is implemented */
681 : 0 : read_data_lin(ctx, 221, sizeof(data), &data);
682 : 0 : init_complete_flag_present = (data & (1 << 4)) != 0;
683 : : }
684 : :
685 : 0 : 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 [ # # ]: 0 : if (!init_complete_flag_present) {
695 : 0 : nt_os_wait_usec(500000);
696 : 0 : return;
697 : : }
698 : :
699 : : /* Otherwise wait for the init complete flag to be set */
700 : : int count = 0;
701 : :
702 : : while (true) {
703 [ # # ]: 0 : if (count > 10) { /* 1 s timeout */
704 : 0 : NT_LOG(WRN, NTHW, "Timeout waiting for module ready");
705 : 0 : break;
706 : : }
707 : :
708 : 0 : read_data_lin(ctx, 6, sizeof(data), &data);
709 : :
710 [ # # ]: 0 : if (data & 0x01) {
711 : 0 : NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100);
712 : 0 : break;
713 : : }
714 : :
715 : 0 : nt_os_wait_usec(100000);/* 100 ms */
716 : 0 : count++;
717 : : }
718 : : }
719 : :
720 : 0 : static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx)
721 : : {
722 : 0 : 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 [ # # ]: 0 : for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) {
732 [ # # ]: 0 : if (ctx->prod_no == nim_list[i]) {
733 : 0 : ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
734 : 0 : ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true;
735 : 0 : NT_LOG(DBG, NTHW, "Found FEC info via PN list");
736 : 0 : 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 : 0 : read_data_lin(ctx, addr, sizeof(data), &data);
749 : :
750 : : /* Check if the module has FEC support that can be controlled */
751 : 0 : ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0;
752 : 0 : ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0;
753 : :
754 [ # # ]: 0 : if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)
755 : 0 : ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
756 : :
757 [ # # ]: 0 : if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)
758 : 0 : ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC);
759 : : }
760 : :
761 : 0 : static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
762 : : {
763 : 0 : int res = qsfpplus_preinit(ctx, lane_idx);
764 : :
765 [ # # ]: 0 : if (!res) {
766 : 0 : qsfp28_wait_for_ready_after_reset(ctx);
767 : 0 : memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0,
768 : : sizeof(ctx->specific_u.qsfp.specific_u.qsfp28));
769 : 0 : ctx->specific_u.qsfp.qsfp28 = true;
770 : 0 : qsfp28_find_port_params(ctx);
771 : 0 : qsfp28_get_fec_options(ctx);
772 : 0 : qsfp28_set_speed_mask(ctx);
773 : : }
774 : :
775 : 0 : return res;
776 : : }
777 : :
778 : 0 : int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra)
779 : : {
780 : 0 : int res = i2c_nim_common_construct(ctx);
781 : :
782 [ # # # ]: 0 : switch (translate_nimid(ctx)) {
783 : 0 : case NT_NIM_QSFP_PLUS:
784 [ # # ]: 0 : qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
785 : 0 : break;
786 : :
787 : 0 : case NT_NIM_QSFP28:
788 [ # # ]: 0 : qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
789 : 0 : break;
790 : :
791 : 0 : default:
792 : : res = 1;
793 : 0 : NT_LOG(ERR, NTHW, "NIM type %s is not supported.", nim_id_to_text(ctx->nim_id));
794 : : }
795 : :
796 : 0 : return res;
797 : : }
|