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