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