1eaf1ebdcSSerhii Iliushyk /* 2eaf1ebdcSSerhii Iliushyk * SPDX-License-Identifier: BSD-3-Clause 3eaf1ebdcSSerhii Iliushyk * Copyright(c) 2023 Napatech A/S 4eaf1ebdcSSerhii Iliushyk */ 5eaf1ebdcSSerhii Iliushyk 6eaf1ebdcSSerhii Iliushyk #include <string.h> 7eaf1ebdcSSerhii Iliushyk 8eaf1ebdcSSerhii Iliushyk #include "nthw_drv.h" 9eaf1ebdcSSerhii Iliushyk #include "i2c_nim.h" 10eaf1ebdcSSerhii Iliushyk #include "ntlog.h" 11eaf1ebdcSSerhii Iliushyk #include "nt_util.h" 12eaf1ebdcSSerhii Iliushyk #include "ntnic_mod_reg.h" 1327c15342SSerhii Iliushyk #include "qsfp_registers.h" 14eaf1ebdcSSerhii Iliushyk #include "nim_defines.h" 15eaf1ebdcSSerhii Iliushyk 16eaf1ebdcSSerhii Iliushyk #define NIM_READ false 17eaf1ebdcSSerhii Iliushyk #define NIM_WRITE true 18eaf1ebdcSSerhii Iliushyk #define NIM_PAGE_SEL_REGISTER 127 19eaf1ebdcSSerhii Iliushyk #define NIM_I2C_0XA0 0xA0 /* Basic I2C address */ 20eaf1ebdcSSerhii Iliushyk 2127c15342SSerhii Iliushyk 2227c15342SSerhii Iliushyk static bool page_addressing(nt_nim_identifier_t id) 2327c15342SSerhii Iliushyk { 2427c15342SSerhii Iliushyk switch (id) { 2527c15342SSerhii Iliushyk case NT_NIM_QSFP: 2627c15342SSerhii Iliushyk case NT_NIM_QSFP_PLUS: 27*4783bd39SSerhii Iliushyk case NT_NIM_QSFP28: 2827c15342SSerhii Iliushyk return true; 2927c15342SSerhii Iliushyk 3027c15342SSerhii Iliushyk default: 3127c15342SSerhii Iliushyk NT_LOG(DBG, NTNIC, "Unknown NIM identifier %d\n", id); 3227c15342SSerhii Iliushyk return false; 3327c15342SSerhii Iliushyk } 3427c15342SSerhii Iliushyk } 3527c15342SSerhii Iliushyk 3627c15342SSerhii Iliushyk static nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx) 3727c15342SSerhii Iliushyk { 3827c15342SSerhii Iliushyk return (nt_nim_identifier_t)ctx->nim_id; 3927c15342SSerhii Iliushyk } 4027c15342SSerhii Iliushyk 41eaf1ebdcSSerhii Iliushyk static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write, uint16_t lin_addr, 42eaf1ebdcSSerhii Iliushyk uint8_t i2c_addr, uint8_t a_reg_addr, uint8_t seq_cnt, 43eaf1ebdcSSerhii Iliushyk uint8_t *p_data) 44eaf1ebdcSSerhii Iliushyk { 45eaf1ebdcSSerhii Iliushyk /* Divide i2c_addr by 2 because nthw_iic_read/writeData multiplies by 2 */ 46eaf1ebdcSSerhii Iliushyk const uint8_t i2c_devaddr = i2c_addr / 2U; 47eaf1ebdcSSerhii Iliushyk (void)lin_addr; /* Unused */ 48eaf1ebdcSSerhii Iliushyk 49eaf1ebdcSSerhii Iliushyk if (do_write) { 50eaf1ebdcSSerhii Iliushyk if (ctx->type == I2C_HWIIC) { 51eaf1ebdcSSerhii Iliushyk return nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt, 52eaf1ebdcSSerhii Iliushyk p_data); 53eaf1ebdcSSerhii Iliushyk 54eaf1ebdcSSerhii Iliushyk } else { 55eaf1ebdcSSerhii Iliushyk return 0; 56eaf1ebdcSSerhii Iliushyk } 57eaf1ebdcSSerhii Iliushyk 58eaf1ebdcSSerhii Iliushyk } else if (ctx->type == I2C_HWIIC) { 59eaf1ebdcSSerhii Iliushyk return nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt, p_data); 60eaf1ebdcSSerhii Iliushyk 61eaf1ebdcSSerhii Iliushyk } else { 62eaf1ebdcSSerhii Iliushyk return 0; 63eaf1ebdcSSerhii Iliushyk } 64eaf1ebdcSSerhii Iliushyk } 65eaf1ebdcSSerhii Iliushyk 66eaf1ebdcSSerhii Iliushyk /* 67eaf1ebdcSSerhii Iliushyk * ------------------------------------------------------------------------------ 68eaf1ebdcSSerhii Iliushyk * Selects a new page for page addressing. This is only relevant if the NIM 69eaf1ebdcSSerhii Iliushyk * supports this. Since page switching can take substantial time the current page 70eaf1ebdcSSerhii Iliushyk * select is read and subsequently only changed if necessary. 71eaf1ebdcSSerhii Iliushyk * Important: 72eaf1ebdcSSerhii Iliushyk * XFP Standard 8077, Ver 4.5, Page 61 states that: 73eaf1ebdcSSerhii Iliushyk * If the host attempts to write a table select value which is not supported in 74eaf1ebdcSSerhii Iliushyk * a particular module, the table select byte will revert to 01h. 75eaf1ebdcSSerhii Iliushyk * This can lead to some surprising result that some pages seems to be duplicated. 76eaf1ebdcSSerhii Iliushyk * ------------------------------------------------------------------------------ 77eaf1ebdcSSerhii Iliushyk */ 78eaf1ebdcSSerhii Iliushyk 79eaf1ebdcSSerhii Iliushyk static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel) 80eaf1ebdcSSerhii Iliushyk { 81eaf1ebdcSSerhii Iliushyk uint8_t curr_page_sel; 82eaf1ebdcSSerhii Iliushyk 83eaf1ebdcSSerhii Iliushyk /* Read the current page select value */ 84eaf1ebdcSSerhii Iliushyk if (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0, 85eaf1ebdcSSerhii Iliushyk NIM_PAGE_SEL_REGISTER, sizeof(curr_page_sel), 86eaf1ebdcSSerhii Iliushyk &curr_page_sel) != 0) { 87eaf1ebdcSSerhii Iliushyk return -1; 88eaf1ebdcSSerhii Iliushyk } 89eaf1ebdcSSerhii Iliushyk 90eaf1ebdcSSerhii Iliushyk /* Only write new page select value if necessary */ 91eaf1ebdcSSerhii Iliushyk if (page_sel != curr_page_sel) { 92eaf1ebdcSSerhii Iliushyk if (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0, 93eaf1ebdcSSerhii Iliushyk NIM_PAGE_SEL_REGISTER, sizeof(page_sel), 94eaf1ebdcSSerhii Iliushyk &page_sel) != 0) { 95eaf1ebdcSSerhii Iliushyk return -1; 96eaf1ebdcSSerhii Iliushyk } 97eaf1ebdcSSerhii Iliushyk } 98eaf1ebdcSSerhii Iliushyk 99eaf1ebdcSSerhii Iliushyk return 0; 100eaf1ebdcSSerhii Iliushyk } 101eaf1ebdcSSerhii Iliushyk 102eaf1ebdcSSerhii Iliushyk static int nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing, uint16_t lin_addr, 103eaf1ebdcSSerhii Iliushyk uint16_t length, uint8_t *p_data, bool do_write) 104eaf1ebdcSSerhii Iliushyk { 105eaf1ebdcSSerhii Iliushyk uint16_t i; 106eaf1ebdcSSerhii Iliushyk uint8_t a_reg_addr; /* The actual register address in I2C device */ 107eaf1ebdcSSerhii Iliushyk uint8_t i2c_addr; 108eaf1ebdcSSerhii Iliushyk int block_size = 128; /* Equal to size of MSA pages */ 109eaf1ebdcSSerhii Iliushyk int seq_cnt; 110eaf1ebdcSSerhii Iliushyk int max_seq_cnt = 1; 111eaf1ebdcSSerhii Iliushyk int multi_byte = 1; /* One byte per I2C register is default */ 112eaf1ebdcSSerhii Iliushyk 113eaf1ebdcSSerhii Iliushyk for (i = 0; i < length;) { 114eaf1ebdcSSerhii Iliushyk bool use_page_select = false; 115eaf1ebdcSSerhii Iliushyk 116eaf1ebdcSSerhii Iliushyk /* 117eaf1ebdcSSerhii Iliushyk * Find out how much can be read from the current block in case of 118eaf1ebdcSSerhii Iliushyk * single byte access 119eaf1ebdcSSerhii Iliushyk */ 120eaf1ebdcSSerhii Iliushyk if (multi_byte == 1) 121eaf1ebdcSSerhii Iliushyk max_seq_cnt = block_size - (lin_addr % block_size); 122eaf1ebdcSSerhii Iliushyk 123eaf1ebdcSSerhii Iliushyk if (m_page_addressing) { 124eaf1ebdcSSerhii Iliushyk if (lin_addr >= 128) { /* Only page setup above this address */ 125eaf1ebdcSSerhii Iliushyk use_page_select = true; 126eaf1ebdcSSerhii Iliushyk 127eaf1ebdcSSerhii Iliushyk /* Map to [128..255] of 0xA0 device */ 128eaf1ebdcSSerhii Iliushyk a_reg_addr = (uint8_t)(block_size + (lin_addr % block_size)); 129eaf1ebdcSSerhii Iliushyk 130eaf1ebdcSSerhii Iliushyk } else { 131eaf1ebdcSSerhii Iliushyk a_reg_addr = (uint8_t)lin_addr; 132eaf1ebdcSSerhii Iliushyk } 133eaf1ebdcSSerhii Iliushyk 134eaf1ebdcSSerhii Iliushyk i2c_addr = NIM_I2C_0XA0;/* Base I2C address */ 135eaf1ebdcSSerhii Iliushyk 136eaf1ebdcSSerhii Iliushyk } else if (lin_addr >= 256) { 137eaf1ebdcSSerhii Iliushyk /* Map to address [0..255] of 0xA2 device */ 138eaf1ebdcSSerhii Iliushyk a_reg_addr = (uint8_t)(lin_addr - 256); 139eaf1ebdcSSerhii Iliushyk i2c_addr = NIM_I2C_0XA2; 140eaf1ebdcSSerhii Iliushyk 141eaf1ebdcSSerhii Iliushyk } else { 142eaf1ebdcSSerhii Iliushyk a_reg_addr = (uint8_t)lin_addr; 143eaf1ebdcSSerhii Iliushyk i2c_addr = NIM_I2C_0XA0;/* Base I2C address */ 144eaf1ebdcSSerhii Iliushyk } 145eaf1ebdcSSerhii Iliushyk 146eaf1ebdcSSerhii Iliushyk /* Now actually do the reading/writing */ 147eaf1ebdcSSerhii Iliushyk seq_cnt = length - i; /* Number of remaining bytes */ 148eaf1ebdcSSerhii Iliushyk 149eaf1ebdcSSerhii Iliushyk if (seq_cnt > max_seq_cnt) 150eaf1ebdcSSerhii Iliushyk seq_cnt = max_seq_cnt; 151eaf1ebdcSSerhii Iliushyk 152eaf1ebdcSSerhii Iliushyk /* 153eaf1ebdcSSerhii Iliushyk * Read a number of bytes without explicitly specifying a new address. 154eaf1ebdcSSerhii Iliushyk * This can speed up I2C access since automatic incrementation of the 155eaf1ebdcSSerhii Iliushyk * I2C device internal address counter can be used. It also allows 156eaf1ebdcSSerhii Iliushyk * a HW implementation, that can deal with block access. 157eaf1ebdcSSerhii Iliushyk * Furthermore it also allows for access to data that must be accessed 158eaf1ebdcSSerhii Iliushyk * as 16bit words reading two bytes at each address eg PHYs. 159eaf1ebdcSSerhii Iliushyk */ 160eaf1ebdcSSerhii Iliushyk if (use_page_select) { 161eaf1ebdcSSerhii Iliushyk if (nim_setup_page(ctx, (uint8_t)((lin_addr / 128) - 1)) != 0) { 162eaf1ebdcSSerhii Iliushyk NT_LOG(ERR, NTNIC, 163eaf1ebdcSSerhii Iliushyk "Cannot set up page for linear address %u\n", lin_addr); 164eaf1ebdcSSerhii Iliushyk return -1; 165eaf1ebdcSSerhii Iliushyk } 166eaf1ebdcSSerhii Iliushyk } 167eaf1ebdcSSerhii Iliushyk 168eaf1ebdcSSerhii Iliushyk if (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr, a_reg_addr, 169eaf1ebdcSSerhii Iliushyk (uint8_t)seq_cnt, p_data) != 0) { 170eaf1ebdcSSerhii Iliushyk NT_LOG(ERR, NTNIC, " Call to nim_read_write_i2c_data failed\n"); 171eaf1ebdcSSerhii Iliushyk return -1; 172eaf1ebdcSSerhii Iliushyk } 173eaf1ebdcSSerhii Iliushyk 174eaf1ebdcSSerhii Iliushyk p_data += seq_cnt; 175eaf1ebdcSSerhii Iliushyk i = (uint16_t)(i + seq_cnt); 176eaf1ebdcSSerhii Iliushyk lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte)); 177eaf1ebdcSSerhii Iliushyk } 178eaf1ebdcSSerhii Iliushyk 179eaf1ebdcSSerhii Iliushyk return 0; 180eaf1ebdcSSerhii Iliushyk } 181eaf1ebdcSSerhii Iliushyk 18227c15342SSerhii Iliushyk static int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data) 18327c15342SSerhii Iliushyk { 18427c15342SSerhii Iliushyk /* Wrapper for using Mutex for QSFP TODO */ 18527c15342SSerhii Iliushyk return nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), lin_addr, length, data, 18627c15342SSerhii Iliushyk NIM_READ); 18727c15342SSerhii Iliushyk } 18827c15342SSerhii Iliushyk 189*4783bd39SSerhii Iliushyk /* Read and return a single byte */ 190*4783bd39SSerhii Iliushyk static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr) 191*4783bd39SSerhii Iliushyk { 192*4783bd39SSerhii Iliushyk uint8_t data; 193*4783bd39SSerhii Iliushyk read_data_lin(ctx, addr, sizeof(data), &data); 194*4783bd39SSerhii Iliushyk return data; 195*4783bd39SSerhii Iliushyk } 196*4783bd39SSerhii Iliushyk 197eaf1ebdcSSerhii Iliushyk static int nim_read_id(nim_i2c_ctx_t *ctx) 198eaf1ebdcSSerhii Iliushyk { 199eaf1ebdcSSerhii Iliushyk /* We are only reading the first byte so we don't care about pages here. */ 200eaf1ebdcSSerhii Iliushyk const bool USE_PAGE_ADDRESSING = false; 201eaf1ebdcSSerhii Iliushyk 202eaf1ebdcSSerhii Iliushyk if (nim_read_write_data_lin(ctx, USE_PAGE_ADDRESSING, NIM_IDENTIFIER_ADDR, 203eaf1ebdcSSerhii Iliushyk sizeof(ctx->nim_id), &ctx->nim_id, NIM_READ) != 0) { 204eaf1ebdcSSerhii Iliushyk return -1; 205eaf1ebdcSSerhii Iliushyk } 206eaf1ebdcSSerhii Iliushyk 207eaf1ebdcSSerhii Iliushyk return 0; 208eaf1ebdcSSerhii Iliushyk } 209eaf1ebdcSSerhii Iliushyk 210eaf1ebdcSSerhii Iliushyk static int i2c_nim_common_construct(nim_i2c_ctx_p ctx) 211eaf1ebdcSSerhii Iliushyk { 212eaf1ebdcSSerhii Iliushyk ctx->nim_id = 0; 213eaf1ebdcSSerhii Iliushyk int res; 214eaf1ebdcSSerhii Iliushyk 215eaf1ebdcSSerhii Iliushyk if (ctx->type == I2C_HWIIC) 216eaf1ebdcSSerhii Iliushyk res = nim_read_id(ctx); 217eaf1ebdcSSerhii Iliushyk 218eaf1ebdcSSerhii Iliushyk else 219eaf1ebdcSSerhii Iliushyk res = -1; 220eaf1ebdcSSerhii Iliushyk 221eaf1ebdcSSerhii Iliushyk if (res) { 222eaf1ebdcSSerhii Iliushyk NT_LOG(ERR, PMD, "Can't read NIM id."); 223eaf1ebdcSSerhii Iliushyk return res; 224eaf1ebdcSSerhii Iliushyk } 225eaf1ebdcSSerhii Iliushyk 226eaf1ebdcSSerhii Iliushyk memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name)); 227eaf1ebdcSSerhii Iliushyk memset(ctx->prod_no, 0, sizeof(ctx->prod_no)); 228eaf1ebdcSSerhii Iliushyk memset(ctx->serial_no, 0, sizeof(ctx->serial_no)); 229eaf1ebdcSSerhii Iliushyk memset(ctx->date, 0, sizeof(ctx->date)); 230eaf1ebdcSSerhii Iliushyk memset(ctx->rev, 0, sizeof(ctx->rev)); 231eaf1ebdcSSerhii Iliushyk 232eaf1ebdcSSerhii Iliushyk ctx->content_valid = false; 233eaf1ebdcSSerhii Iliushyk memset(ctx->len_info, 0, sizeof(ctx->len_info)); 234eaf1ebdcSSerhii Iliushyk ctx->pwr_level_req = 0; 235eaf1ebdcSSerhii Iliushyk ctx->pwr_level_cur = 0; 236eaf1ebdcSSerhii Iliushyk ctx->avg_pwr = false; 237eaf1ebdcSSerhii Iliushyk ctx->tx_disable = false; 238eaf1ebdcSSerhii Iliushyk ctx->lane_idx = -1; 239eaf1ebdcSSerhii Iliushyk ctx->lane_count = 1; 240eaf1ebdcSSerhii Iliushyk ctx->options = 0; 241eaf1ebdcSSerhii Iliushyk return 0; 242eaf1ebdcSSerhii Iliushyk } 243eaf1ebdcSSerhii Iliushyk 24427c15342SSerhii Iliushyk /* 24527c15342SSerhii Iliushyk * Read vendor information at a certain address. Any trailing whitespace is 24627c15342SSerhii Iliushyk * removed and a missing string termination in the NIM data is handled. 24727c15342SSerhii Iliushyk */ 24827c15342SSerhii Iliushyk static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data) 24927c15342SSerhii Iliushyk { 25027c15342SSerhii Iliushyk const bool pg_addr = page_addressing(ctx->nim_id); 25127c15342SSerhii Iliushyk int i; 25227c15342SSerhii Iliushyk /* Subtract "1" from max_len that includes a terminating "0" */ 25327c15342SSerhii Iliushyk 25427c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1), (uint8_t *)p_data, 25527c15342SSerhii Iliushyk NIM_READ) != 0) { 25627c15342SSerhii Iliushyk return -1; 25727c15342SSerhii Iliushyk } 25827c15342SSerhii Iliushyk 25927c15342SSerhii Iliushyk /* Terminate at first found white space */ 26027c15342SSerhii Iliushyk for (i = 0; i < max_len - 1; i++) { 26127c15342SSerhii Iliushyk if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || *p_data == '\v' || 26227c15342SSerhii Iliushyk *p_data == '\f' || *p_data == '\r') { 26327c15342SSerhii Iliushyk *p_data = '\0'; 26427c15342SSerhii Iliushyk return 0; 26527c15342SSerhii Iliushyk } 26627c15342SSerhii Iliushyk 26727c15342SSerhii Iliushyk p_data++; 26827c15342SSerhii Iliushyk } 26927c15342SSerhii Iliushyk 27027c15342SSerhii Iliushyk /* 27127c15342SSerhii Iliushyk * Add line termination as the very last character, if it was missing in the 27227c15342SSerhii Iliushyk * NIM data 27327c15342SSerhii Iliushyk */ 27427c15342SSerhii Iliushyk *p_data = '\0'; 27527c15342SSerhii Iliushyk return 0; 27627c15342SSerhii Iliushyk } 27727c15342SSerhii Iliushyk 27827c15342SSerhii Iliushyk static void qsfp_read_vendor_info(nim_i2c_ctx_t *ctx) 27927c15342SSerhii Iliushyk { 28027c15342SSerhii Iliushyk nim_read_vendor_info(ctx, QSFP_VENDOR_NAME_LIN_ADDR, sizeof(ctx->vendor_name), 28127c15342SSerhii Iliushyk ctx->vendor_name); 28227c15342SSerhii Iliushyk nim_read_vendor_info(ctx, QSFP_VENDOR_PN_LIN_ADDR, sizeof(ctx->prod_no), ctx->prod_no); 28327c15342SSerhii Iliushyk nim_read_vendor_info(ctx, QSFP_VENDOR_SN_LIN_ADDR, sizeof(ctx->serial_no), ctx->serial_no); 28427c15342SSerhii Iliushyk nim_read_vendor_info(ctx, QSFP_VENDOR_DATE_LIN_ADDR, sizeof(ctx->date), ctx->date); 28527c15342SSerhii Iliushyk nim_read_vendor_info(ctx, QSFP_VENDOR_REV_LIN_ADDR, (uint8_t)(sizeof(ctx->rev) - 2), 28627c15342SSerhii Iliushyk ctx->rev); /*OBS Only two bytes*/ 28727c15342SSerhii Iliushyk } 28827c15342SSerhii Iliushyk static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state) 28927c15342SSerhii Iliushyk { 29027c15342SSerhii Iliushyk int res = 0; /* unused due to no readings from HW */ 29127c15342SSerhii Iliushyk 29227c15342SSerhii Iliushyk assert(ctx && state); 29327c15342SSerhii Iliushyk assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized"); 29427c15342SSerhii Iliushyk 29527c15342SSerhii Iliushyk (void)memset(state, 0, sizeof(*state)); 29627c15342SSerhii Iliushyk 29727c15342SSerhii Iliushyk switch (ctx->nim_id) { 29827c15342SSerhii Iliushyk case 12U: 29927c15342SSerhii Iliushyk state->br = 10U;/* QSFP: 4 x 1G = 4G */ 30027c15342SSerhii Iliushyk break; 30127c15342SSerhii Iliushyk 30227c15342SSerhii Iliushyk case 13U: 30327c15342SSerhii Iliushyk state->br = 103U; /* QSFP+: 4 x 10G = 40G */ 30427c15342SSerhii Iliushyk break; 30527c15342SSerhii Iliushyk 306*4783bd39SSerhii Iliushyk case 17U: 307*4783bd39SSerhii Iliushyk state->br = 255U; /* QSFP28: 4 x 25G = 100G */ 308*4783bd39SSerhii Iliushyk break; 309*4783bd39SSerhii Iliushyk 31027c15342SSerhii Iliushyk default: 311*4783bd39SSerhii Iliushyk NT_LOG(INF, NIM, "nim_id = %u is not an QSFP/QSFP+/QSFP28 module\n", ctx->nim_id); 31227c15342SSerhii Iliushyk res = -1; 31327c15342SSerhii Iliushyk } 31427c15342SSerhii Iliushyk 31527c15342SSerhii Iliushyk return res; 31627c15342SSerhii Iliushyk } 31727c15342SSerhii Iliushyk 31827c15342SSerhii Iliushyk int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state) 31927c15342SSerhii Iliushyk { 32027c15342SSerhii Iliushyk return qsfp_nim_state_build(ctx, state); 32127c15342SSerhii Iliushyk } 32227c15342SSerhii Iliushyk 323eaf1ebdcSSerhii Iliushyk const char *nim_id_to_text(uint8_t nim_id) 324eaf1ebdcSSerhii Iliushyk { 325eaf1ebdcSSerhii Iliushyk switch (nim_id) { 326eaf1ebdcSSerhii Iliushyk case 0x0: 327eaf1ebdcSSerhii Iliushyk return "UNKNOWN"; 328eaf1ebdcSSerhii Iliushyk 32927c15342SSerhii Iliushyk case 0x0C: 33027c15342SSerhii Iliushyk return "QSFP"; 33127c15342SSerhii Iliushyk 33227c15342SSerhii Iliushyk case 0x0D: 33327c15342SSerhii Iliushyk return "QSFP+"; 33427c15342SSerhii Iliushyk 335*4783bd39SSerhii Iliushyk case 0x11: 336*4783bd39SSerhii Iliushyk return "QSFP28"; 337*4783bd39SSerhii Iliushyk 338eaf1ebdcSSerhii Iliushyk default: 339eaf1ebdcSSerhii Iliushyk return "ILLEGAL!"; 340eaf1ebdcSSerhii Iliushyk } 341eaf1ebdcSSerhii Iliushyk } 342eaf1ebdcSSerhii Iliushyk 34327c15342SSerhii Iliushyk /* 34427c15342SSerhii Iliushyk * Disable laser for specific lane or all lanes 34527c15342SSerhii Iliushyk */ 34627c15342SSerhii Iliushyk int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable, int lane_idx) 34727c15342SSerhii Iliushyk { 34827c15342SSerhii Iliushyk uint8_t value; 34927c15342SSerhii Iliushyk uint8_t mask; 35027c15342SSerhii Iliushyk const bool pg_addr = page_addressing(ctx->nim_id); 35127c15342SSerhii Iliushyk 35227c15342SSerhii Iliushyk if (lane_idx < 0) /* If no lane is specified then all lanes */ 35327c15342SSerhii Iliushyk mask = QSFP_SOFT_TX_ALL_DISABLE_BITS; 35427c15342SSerhii Iliushyk 35527c15342SSerhii Iliushyk else 35627c15342SSerhii Iliushyk mask = (uint8_t)(1U << lane_idx); 35727c15342SSerhii Iliushyk 35827c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value), 35927c15342SSerhii Iliushyk &value, NIM_READ) != 0) { 36027c15342SSerhii Iliushyk return -1; 36127c15342SSerhii Iliushyk } 36227c15342SSerhii Iliushyk 36327c15342SSerhii Iliushyk if (disable) 36427c15342SSerhii Iliushyk value |= mask; 36527c15342SSerhii Iliushyk 36627c15342SSerhii Iliushyk else 36727c15342SSerhii Iliushyk value &= (uint8_t)(~mask); 36827c15342SSerhii Iliushyk 36927c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value), 37027c15342SSerhii Iliushyk &value, NIM_WRITE) != 0) { 37127c15342SSerhii Iliushyk return -1; 37227c15342SSerhii Iliushyk } 37327c15342SSerhii Iliushyk 37427c15342SSerhii Iliushyk return 0; 37527c15342SSerhii Iliushyk } 37627c15342SSerhii Iliushyk 37727c15342SSerhii Iliushyk /* 37827c15342SSerhii Iliushyk * Import length info in various units from NIM module data and convert to meters 37927c15342SSerhii Iliushyk */ 38027c15342SSerhii Iliushyk static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info, uint16_t *p_nim_units) 38127c15342SSerhii Iliushyk { 38227c15342SSerhii Iliushyk size_t i; 38327c15342SSerhii Iliushyk 38427c15342SSerhii Iliushyk for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++) 38527c15342SSerhii Iliushyk if (*(p_nim_len_info + i) == 255) { 38627c15342SSerhii Iliushyk ctx->len_info[i] = 65535; 38727c15342SSerhii Iliushyk 38827c15342SSerhii Iliushyk } else { 38927c15342SSerhii Iliushyk uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i); 39027c15342SSerhii Iliushyk 39127c15342SSerhii Iliushyk if (len > 65535) 39227c15342SSerhii Iliushyk ctx->len_info[i] = 65535; 39327c15342SSerhii Iliushyk 39427c15342SSerhii Iliushyk else 39527c15342SSerhii Iliushyk ctx->len_info[i] = (uint16_t)len; 39627c15342SSerhii Iliushyk } 39727c15342SSerhii Iliushyk } 39827c15342SSerhii Iliushyk 39927c15342SSerhii Iliushyk static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx) 40027c15342SSerhii Iliushyk { 40127c15342SSerhii Iliushyk const bool pg_addr = page_addressing(ctx->nim_id); 40227c15342SSerhii Iliushyk uint8_t options; 40327c15342SSerhii Iliushyk uint8_t value; 40427c15342SSerhii Iliushyk uint8_t nim_len_info[5]; 40527c15342SSerhii Iliushyk uint16_t nim_units[5] = { 1000, 2, 1, 1, 1 }; /* QSFP MSA units in meters */ 40627c15342SSerhii Iliushyk const char *yes_no[2] = { "No", "Yes" }; 40727c15342SSerhii Iliushyk (void)yes_no; 40827c15342SSerhii Iliushyk NT_LOG(DBG, NTNIC, "Instance %d: NIM id: %s (%d)\n", ctx->instance, 40927c15342SSerhii Iliushyk nim_id_to_text(ctx->nim_id), ctx->nim_id); 41027c15342SSerhii Iliushyk 41127c15342SSerhii Iliushyk /* Read DMI options */ 41227c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR, sizeof(options), 41327c15342SSerhii Iliushyk &options, NIM_READ) != 0) { 41427c15342SSerhii Iliushyk return -1; 41527c15342SSerhii Iliushyk } 41627c15342SSerhii Iliushyk 41727c15342SSerhii Iliushyk ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT; 41827c15342SSerhii Iliushyk NT_LOG(DBG, NTNIC, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)\n", ctx->instance, 41927c15342SSerhii Iliushyk yes_no[ctx->avg_pwr]); 42027c15342SSerhii Iliushyk 42127c15342SSerhii Iliushyk qsfp_read_vendor_info(ctx); 42227c15342SSerhii Iliushyk NT_LOG(DBG, PMD, 42327c15342SSerhii Iliushyk "Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)\n", 42427c15342SSerhii Iliushyk ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no, ctx->date, ctx->rev); 42527c15342SSerhii Iliushyk 42627c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info), 42727c15342SSerhii Iliushyk nim_len_info, NIM_READ) != 0) { 42827c15342SSerhii Iliushyk return -1; 42927c15342SSerhii Iliushyk } 43027c15342SSerhii Iliushyk 43127c15342SSerhii Iliushyk /* 43227c15342SSerhii Iliushyk * Returns supported length information in meters for various fibers as 5 indivi- 43327c15342SSerhii Iliushyk * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper] 43427c15342SSerhii Iliushyk * If no length information is available for a certain entry, the returned value 43527c15342SSerhii Iliushyk * will be zero. This will be the case for SFP modules - EBW entry. 43627c15342SSerhii Iliushyk * If the MSBit is set the returned value in the lower 31 bits indicates that the 43727c15342SSerhii Iliushyk * supported length is greater than this. 43827c15342SSerhii Iliushyk */ 43927c15342SSerhii Iliushyk 44027c15342SSerhii Iliushyk nim_import_len_info(ctx, nim_len_info, nim_units); 44127c15342SSerhii Iliushyk 44227c15342SSerhii Iliushyk /* Read required power level */ 44327c15342SSerhii Iliushyk if (nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER, sizeof(value), &value, 44427c15342SSerhii Iliushyk NIM_READ) != 0) { 44527c15342SSerhii Iliushyk return -1; 44627c15342SSerhii Iliushyk } 44727c15342SSerhii Iliushyk 44827c15342SSerhii Iliushyk /* 44927c15342SSerhii Iliushyk * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43: 45027c15342SSerhii Iliushyk * If power class >= 5 setHighPower must be called for the module to be fully 45127c15342SSerhii Iliushyk * functional 45227c15342SSerhii Iliushyk */ 45327c15342SSerhii Iliushyk if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) { 45427c15342SSerhii Iliushyk /* NIM in power class 1 - 4 */ 45527c15342SSerhii Iliushyk ctx->pwr_level_req = (uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) + 1); 45627c15342SSerhii Iliushyk 45727c15342SSerhii Iliushyk } else { 45827c15342SSerhii Iliushyk /* NIM in power class 5 - 7 */ 45927c15342SSerhii Iliushyk ctx->pwr_level_req = (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4); 46027c15342SSerhii Iliushyk } 46127c15342SSerhii Iliushyk 46227c15342SSerhii Iliushyk return 0; 46327c15342SSerhii Iliushyk } 46427c15342SSerhii Iliushyk 465*4783bd39SSerhii Iliushyk static void qsfp28_find_port_params(nim_i2c_ctx_p ctx) 466*4783bd39SSerhii Iliushyk { 467*4783bd39SSerhii Iliushyk uint8_t fiber_chan_speed; 468*4783bd39SSerhii Iliushyk 469*4783bd39SSerhii Iliushyk /* Table 6-17 SFF-8636 */ 470*4783bd39SSerhii Iliushyk read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed); 471*4783bd39SSerhii Iliushyk 472*4783bd39SSerhii Iliushyk if (fiber_chan_speed & (1 << 7)) { 473*4783bd39SSerhii Iliushyk /* SFF-8024, Rev 4.7, Table 4-4 */ 474*4783bd39SSerhii Iliushyk uint8_t extended_specification_compliance_code = 0; 475*4783bd39SSerhii Iliushyk read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1, 476*4783bd39SSerhii Iliushyk &extended_specification_compliance_code); 477*4783bd39SSerhii Iliushyk 478*4783bd39SSerhii Iliushyk switch (extended_specification_compliance_code) { 479*4783bd39SSerhii Iliushyk case 0x02: 480*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_SR4; 481*4783bd39SSerhii Iliushyk break; 482*4783bd39SSerhii Iliushyk 483*4783bd39SSerhii Iliushyk case 0x03: 484*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_LR4; 485*4783bd39SSerhii Iliushyk break; 486*4783bd39SSerhii Iliushyk 487*4783bd39SSerhii Iliushyk case 0x0B: 488*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L; 489*4783bd39SSerhii Iliushyk break; 490*4783bd39SSerhii Iliushyk 491*4783bd39SSerhii Iliushyk case 0x0C: 492*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S; 493*4783bd39SSerhii Iliushyk break; 494*4783bd39SSerhii Iliushyk 495*4783bd39SSerhii Iliushyk case 0x0D: 496*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N; 497*4783bd39SSerhii Iliushyk break; 498*4783bd39SSerhii Iliushyk 499*4783bd39SSerhii Iliushyk case 0x25: 500*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_DR; 501*4783bd39SSerhii Iliushyk break; 502*4783bd39SSerhii Iliushyk 503*4783bd39SSerhii Iliushyk case 0x26: 504*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_FR; 505*4783bd39SSerhii Iliushyk break; 506*4783bd39SSerhii Iliushyk 507*4783bd39SSerhii Iliushyk case 0x27: 508*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_LR; 509*4783bd39SSerhii Iliushyk break; 510*4783bd39SSerhii Iliushyk 511*4783bd39SSerhii Iliushyk default: 512*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28; 513*4783bd39SSerhii Iliushyk } 514*4783bd39SSerhii Iliushyk 515*4783bd39SSerhii Iliushyk } else { 516*4783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28; 517*4783bd39SSerhii Iliushyk } 518*4783bd39SSerhii Iliushyk } 519*4783bd39SSerhii Iliushyk 520*4783bd39SSerhii Iliushyk /* 521*4783bd39SSerhii Iliushyk * If true the user must actively select the desired rate. If false the module 522*4783bd39SSerhii Iliushyk * however can still support several rates without the user is required to select 523*4783bd39SSerhii Iliushyk * one of them. Supported rates must then be deduced from the product number. 524*4783bd39SSerhii Iliushyk * SFF-8636, Rev 2.10a: 525*4783bd39SSerhii Iliushyk * p40: 6.2.7 Rate Select 526*4783bd39SSerhii Iliushyk * p85: A.2 Rate Select 527*4783bd39SSerhii Iliushyk */ 528*4783bd39SSerhii Iliushyk static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx) 529*4783bd39SSerhii Iliushyk { 530*4783bd39SSerhii Iliushyk const uint8_t ext_rate_select_compl_reg_addr = 141; 531*4783bd39SSerhii Iliushyk const uint8_t options_reg_addr = 195; 532*4783bd39SSerhii Iliushyk const uint8_t enh_options_reg_addr = 221; 533*4783bd39SSerhii Iliushyk 534*4783bd39SSerhii Iliushyk uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01; /* bit: 5 */ 535*4783bd39SSerhii Iliushyk 536*4783bd39SSerhii Iliushyk if (rate_select_ena == 0) 537*4783bd39SSerhii Iliushyk return false; 538*4783bd39SSerhii Iliushyk 539*4783bd39SSerhii Iliushyk uint8_t rate_select_type = 540*4783bd39SSerhii Iliushyk (read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03; /* bit 3..2 */ 541*4783bd39SSerhii Iliushyk 542*4783bd39SSerhii Iliushyk if (rate_select_type != 2) { 543*4783bd39SSerhii Iliushyk NT_LOG(DBG, PMD, "NIM has unhandled rate select type (%d)", rate_select_type); 544*4783bd39SSerhii Iliushyk return false; 545*4783bd39SSerhii Iliushyk } 546*4783bd39SSerhii Iliushyk 547*4783bd39SSerhii Iliushyk uint8_t ext_rate_select_ver = 548*4783bd39SSerhii Iliushyk read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03; /* bit 1..0 */ 549*4783bd39SSerhii Iliushyk 550*4783bd39SSerhii Iliushyk if (ext_rate_select_ver != 0x02) { 551*4783bd39SSerhii Iliushyk NT_LOG(DBG, PMD, "NIM has unhandled extended rate select version (%d)", 552*4783bd39SSerhii Iliushyk ext_rate_select_ver); 553*4783bd39SSerhii Iliushyk return false; 554*4783bd39SSerhii Iliushyk } 555*4783bd39SSerhii Iliushyk 556*4783bd39SSerhii Iliushyk return true; /* When true selectRate() can be used */ 557*4783bd39SSerhii Iliushyk } 558*4783bd39SSerhii Iliushyk 559*4783bd39SSerhii Iliushyk static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx) 560*4783bd39SSerhii Iliushyk { 561*4783bd39SSerhii Iliushyk if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || ctx->port_type == NT_PORT_TYPE_QSFP28_DR || 562*4783bd39SSerhii Iliushyk ctx->port_type == NT_PORT_TYPE_QSFP28_LR) { 563*4783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 564*4783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_100G; 565*4783bd39SSerhii Iliushyk 566*4783bd39SSerhii Iliushyk else 567*4783bd39SSerhii Iliushyk /* PAM-4 modules can only run on all lanes together */ 568*4783bd39SSerhii Iliushyk ctx->speed_mask = 0; 569*4783bd39SSerhii Iliushyk 570*4783bd39SSerhii Iliushyk } else { 571*4783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 572*4783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_100G; 573*4783bd39SSerhii Iliushyk 574*4783bd39SSerhii Iliushyk else 575*4783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_25G; 576*4783bd39SSerhii Iliushyk 577*4783bd39SSerhii Iliushyk if (qsfp28_is_rate_selection_enabled(ctx)) { 578*4783bd39SSerhii Iliushyk /* 579*4783bd39SSerhii Iliushyk * It is assumed that if the module supports dual rates then the other rate 580*4783bd39SSerhii Iliushyk * is 10G per lane or 40G for all lanes. 581*4783bd39SSerhii Iliushyk */ 582*4783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 583*4783bd39SSerhii Iliushyk ctx->speed_mask |= NT_LINK_SPEED_40G; 584*4783bd39SSerhii Iliushyk 585*4783bd39SSerhii Iliushyk else 586*4783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_10G; 587*4783bd39SSerhii Iliushyk } 588*4783bd39SSerhii Iliushyk } 589*4783bd39SSerhii Iliushyk } 590*4783bd39SSerhii Iliushyk 59127c15342SSerhii Iliushyk static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx) 59227c15342SSerhii Iliushyk { 59327c15342SSerhii Iliushyk uint8_t device_tech; 59427c15342SSerhii Iliushyk read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), &device_tech); 59527c15342SSerhii Iliushyk 59627c15342SSerhii Iliushyk switch (device_tech & 0xF0) { 59727c15342SSerhii Iliushyk case 0xA0: /* Copper cable unequalized */ 59827c15342SSerhii Iliushyk break; 59927c15342SSerhii Iliushyk 60027c15342SSerhii Iliushyk case 0xC0: /* Copper cable, near and far end limiting active equalizers */ 60127c15342SSerhii Iliushyk case 0xD0: /* Copper cable, far end limiting active equalizers */ 60227c15342SSerhii Iliushyk case 0xE0: /* Copper cable, near end limiting active equalizers */ 60327c15342SSerhii Iliushyk break; 60427c15342SSerhii Iliushyk 60527c15342SSerhii Iliushyk default:/* Optical */ 60627c15342SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP_PLUS; 60727c15342SSerhii Iliushyk break; 60827c15342SSerhii Iliushyk } 60927c15342SSerhii Iliushyk } 61027c15342SSerhii Iliushyk 61127c15342SSerhii Iliushyk static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx) 61227c15342SSerhii Iliushyk { 61327c15342SSerhii Iliushyk ctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G : (NT_LINK_SPEED_10G); 61427c15342SSerhii Iliushyk } 61527c15342SSerhii Iliushyk 61627c15342SSerhii Iliushyk static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx) 61727c15342SSerhii Iliushyk { 61827c15342SSerhii Iliushyk assert(lane_idx < 4); 619*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.qsfp28 = false; 62027c15342SSerhii Iliushyk ctx->lane_idx = lane_idx; 62127c15342SSerhii Iliushyk ctx->lane_count = 4; 62227c15342SSerhii Iliushyk } 62327c15342SSerhii Iliushyk 62427c15342SSerhii Iliushyk static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx) 62527c15342SSerhii Iliushyk { 62627c15342SSerhii Iliushyk qsfpplus_construct(ctx, lane_idx); 62727c15342SSerhii Iliushyk int res = qsfpplus_read_basic_data(ctx); 62827c15342SSerhii Iliushyk 62927c15342SSerhii Iliushyk if (!res) { 63027c15342SSerhii Iliushyk qsfpplus_find_port_params(ctx); 63127c15342SSerhii Iliushyk 63227c15342SSerhii Iliushyk /* 63327c15342SSerhii Iliushyk * Read if TX_DISABLE has been implemented 63427c15342SSerhii Iliushyk * For passive optical modules this is required while it for copper and active 63527c15342SSerhii Iliushyk * optical modules is optional. Under all circumstances register 195.4 will 63627c15342SSerhii Iliushyk * indicate, if TX_DISABLE has been implemented in register 86.0-3 63727c15342SSerhii Iliushyk */ 63827c15342SSerhii Iliushyk uint8_t value; 63927c15342SSerhii Iliushyk read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value); 64027c15342SSerhii Iliushyk 64127c15342SSerhii Iliushyk ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0; 64227c15342SSerhii Iliushyk 64327c15342SSerhii Iliushyk if (ctx->tx_disable) 64427c15342SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_TX_DISABLE); 64527c15342SSerhii Iliushyk 64627c15342SSerhii Iliushyk /* 64727c15342SSerhii Iliushyk * Previously - considering AFBR-89BRDZ - code tried to establish if a module was 64827c15342SSerhii Iliushyk * RxOnly by testing the state of the lasers after reset. Lasers were for this 64927c15342SSerhii Iliushyk * module default disabled. 65027c15342SSerhii Iliushyk * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was 65127c15342SSerhii Iliushyk * decided that this option should not be detected automatically but from PN 65227c15342SSerhii Iliushyk */ 65327c15342SSerhii Iliushyk ctx->specific_u.qsfp.rx_only = (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0; 65427c15342SSerhii Iliushyk qsfpplus_set_speed_mask(ctx); 65527c15342SSerhii Iliushyk } 65627c15342SSerhii Iliushyk 65727c15342SSerhii Iliushyk return res; 65827c15342SSerhii Iliushyk } 65927c15342SSerhii Iliushyk 660*4783bd39SSerhii Iliushyk static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx) 661*4783bd39SSerhii Iliushyk { 662*4783bd39SSerhii Iliushyk uint8_t data; 663*4783bd39SSerhii Iliushyk bool init_complete_flag_present = false; 664*4783bd39SSerhii Iliushyk 665*4783bd39SSerhii Iliushyk /* 666*4783bd39SSerhii Iliushyk * Revision compliance 667*4783bd39SSerhii Iliushyk * 7: SFF-8636 Rev 2.5, 2.6 and 2.7 668*4783bd39SSerhii Iliushyk * 8: SFF-8636 Rev 2.8, 2.9 and 2.10 669*4783bd39SSerhii Iliushyk */ 670*4783bd39SSerhii Iliushyk read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance), 671*4783bd39SSerhii Iliushyk &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); 672*4783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "NIM RevCompliance = %d", 673*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); 674*4783bd39SSerhii Iliushyk 675*4783bd39SSerhii Iliushyk /* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */ 676*4783bd39SSerhii Iliushyk if (ctx->lane_idx > 0) 677*4783bd39SSerhii Iliushyk return; 678*4783bd39SSerhii Iliushyk 679*4783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) { 680*4783bd39SSerhii Iliushyk /* Check if init complete flag is implemented */ 681*4783bd39SSerhii Iliushyk read_data_lin(ctx, 221, sizeof(data), &data); 682*4783bd39SSerhii Iliushyk init_complete_flag_present = (data & (1 << 4)) != 0; 683*4783bd39SSerhii Iliushyk } 684*4783bd39SSerhii Iliushyk 685*4783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", init_complete_flag_present); 686*4783bd39SSerhii Iliushyk 687*4783bd39SSerhii Iliushyk /* 688*4783bd39SSerhii Iliushyk * If the init complete flag is not present then wait 500ms that together with 500ms 689*4783bd39SSerhii Iliushyk * after reset (in the adapter code) should be enough to read data from upper pages 690*4783bd39SSerhii Iliushyk * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been 691*4783bd39SSerhii Iliushyk * prone to this when trying to read sensor options using getQsfpOptionsFromData() 692*4783bd39SSerhii Iliushyk * Probably because access to the paged address space is required. 693*4783bd39SSerhii Iliushyk */ 694*4783bd39SSerhii Iliushyk if (!init_complete_flag_present) { 695*4783bd39SSerhii Iliushyk nt_os_wait_usec(500000); 696*4783bd39SSerhii Iliushyk return; 697*4783bd39SSerhii Iliushyk } 698*4783bd39SSerhii Iliushyk 699*4783bd39SSerhii Iliushyk /* Otherwise wait for the init complete flag to be set */ 700*4783bd39SSerhii Iliushyk int count = 0; 701*4783bd39SSerhii Iliushyk 702*4783bd39SSerhii Iliushyk while (true) { 703*4783bd39SSerhii Iliushyk if (count > 10) { /* 1 s timeout */ 704*4783bd39SSerhii Iliushyk NT_LOG(WRN, NTHW, "Timeout waiting for module ready"); 705*4783bd39SSerhii Iliushyk break; 706*4783bd39SSerhii Iliushyk } 707*4783bd39SSerhii Iliushyk 708*4783bd39SSerhii Iliushyk read_data_lin(ctx, 6, sizeof(data), &data); 709*4783bd39SSerhii Iliushyk 710*4783bd39SSerhii Iliushyk if (data & 0x01) { 711*4783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100); 712*4783bd39SSerhii Iliushyk break; 713*4783bd39SSerhii Iliushyk } 714*4783bd39SSerhii Iliushyk 715*4783bd39SSerhii Iliushyk nt_os_wait_usec(100000);/* 100 ms */ 716*4783bd39SSerhii Iliushyk count++; 717*4783bd39SSerhii Iliushyk } 718*4783bd39SSerhii Iliushyk } 719*4783bd39SSerhii Iliushyk 720*4783bd39SSerhii Iliushyk static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx) 721*4783bd39SSerhii Iliushyk { 722*4783bd39SSerhii Iliushyk const char *const nim_list[] = { 723*4783bd39SSerhii Iliushyk "AFBR-89BDDZ", /* Avago BiDi */ 724*4783bd39SSerhii Iliushyk "AFBR-89BRDZ", /* Avago BiDi, RxOnly */ 725*4783bd39SSerhii Iliushyk "FTLC4352RKPL", /* Finisar QSFP28-LR */ 726*4783bd39SSerhii Iliushyk "FTLC4352RHPL", /* Finisar QSFP28-DR */ 727*4783bd39SSerhii Iliushyk "FTLC4352RJPL", /* Finisar QSFP28-FR */ 728*4783bd39SSerhii Iliushyk "SFBR-89BDDZ-CS4", /* Foxconn, QSFP28 100G/40G BiDi */ 729*4783bd39SSerhii Iliushyk }; 730*4783bd39SSerhii Iliushyk 731*4783bd39SSerhii Iliushyk for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) { 732*4783bd39SSerhii Iliushyk if (ctx->prod_no == nim_list[i]) { 733*4783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); 734*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true; 735*4783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "Found FEC info via PN list"); 736*4783bd39SSerhii Iliushyk return; 737*4783bd39SSerhii Iliushyk } 738*4783bd39SSerhii Iliushyk } 739*4783bd39SSerhii Iliushyk 740*4783bd39SSerhii Iliushyk /* 741*4783bd39SSerhii Iliushyk * For modules not in the list find FEC info via registers 742*4783bd39SSerhii Iliushyk * Read if the module has controllable FEC 743*4783bd39SSerhii Iliushyk * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing) 744*4783bd39SSerhii Iliushyk * (Page 03h, Bytes 224-229) 745*4783bd39SSerhii Iliushyk */ 746*4783bd39SSerhii Iliushyk uint8_t data; 747*4783bd39SSerhii Iliushyk uint16_t addr = 227 + 3 * 128; 748*4783bd39SSerhii Iliushyk read_data_lin(ctx, addr, sizeof(data), &data); 749*4783bd39SSerhii Iliushyk 750*4783bd39SSerhii Iliushyk /* Check if the module has FEC support that can be controlled */ 751*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0; 752*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0; 753*4783bd39SSerhii Iliushyk 754*4783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl) 755*4783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); 756*4783bd39SSerhii Iliushyk 757*4783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl) 758*4783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC); 759*4783bd39SSerhii Iliushyk } 760*4783bd39SSerhii Iliushyk 761*4783bd39SSerhii Iliushyk static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx) 762*4783bd39SSerhii Iliushyk { 763*4783bd39SSerhii Iliushyk int res = qsfpplus_preinit(ctx, lane_idx); 764*4783bd39SSerhii Iliushyk 765*4783bd39SSerhii Iliushyk if (!res) { 766*4783bd39SSerhii Iliushyk qsfp28_wait_for_ready_after_reset(ctx); 767*4783bd39SSerhii Iliushyk memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0, 768*4783bd39SSerhii Iliushyk sizeof(ctx->specific_u.qsfp.specific_u.qsfp28)); 769*4783bd39SSerhii Iliushyk ctx->specific_u.qsfp.qsfp28 = true; 770*4783bd39SSerhii Iliushyk qsfp28_find_port_params(ctx); 771*4783bd39SSerhii Iliushyk qsfp28_get_fec_options(ctx); 772*4783bd39SSerhii Iliushyk qsfp28_set_speed_mask(ctx); 773*4783bd39SSerhii Iliushyk } 774*4783bd39SSerhii Iliushyk 775*4783bd39SSerhii Iliushyk return res; 776*4783bd39SSerhii Iliushyk } 777*4783bd39SSerhii Iliushyk 77827c15342SSerhii Iliushyk int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra) 779eaf1ebdcSSerhii Iliushyk { 780eaf1ebdcSSerhii Iliushyk int res = i2c_nim_common_construct(ctx); 781eaf1ebdcSSerhii Iliushyk 78227c15342SSerhii Iliushyk switch (translate_nimid(ctx)) { 78327c15342SSerhii Iliushyk case NT_NIM_QSFP_PLUS: 78427c15342SSerhii Iliushyk qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1); 78527c15342SSerhii Iliushyk break; 78627c15342SSerhii Iliushyk 787*4783bd39SSerhii Iliushyk case NT_NIM_QSFP28: 788*4783bd39SSerhii Iliushyk qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1); 789*4783bd39SSerhii Iliushyk break; 790*4783bd39SSerhii Iliushyk 79127c15342SSerhii Iliushyk default: 79227c15342SSerhii Iliushyk res = 1; 79327c15342SSerhii Iliushyk NT_LOG(ERR, NTHW, "NIM type %s is not supported.\n", nim_id_to_text(ctx->nim_id)); 79427c15342SSerhii Iliushyk } 79527c15342SSerhii Iliushyk 796eaf1ebdcSSerhii Iliushyk return res; 797eaf1ebdcSSerhii Iliushyk } 798