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: 274783bd39SSerhii Iliushyk case NT_NIM_QSFP28: 2827c15342SSerhii Iliushyk return true; 2927c15342SSerhii Iliushyk 3027c15342SSerhii Iliushyk default: 31*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, "Unknown NIM identifier %d", 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, 163*3489b87bSDanylo Vodopianov "Cannot set up page for linear address %u", 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) { 170*3489b87bSDanylo Vodopianov NT_LOG(ERR, NTNIC, " Call to nim_read_write_i2c_data failed"); 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 1894783bd39SSerhii Iliushyk /* Read and return a single byte */ 1904783bd39SSerhii Iliushyk static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr) 1914783bd39SSerhii Iliushyk { 1924783bd39SSerhii Iliushyk uint8_t data; 1934783bd39SSerhii Iliushyk read_data_lin(ctx, addr, sizeof(data), &data); 1944783bd39SSerhii Iliushyk return data; 1954783bd39SSerhii Iliushyk } 1964783bd39SSerhii 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) { 222*3489b87bSDanylo Vodopianov NT_LOG(ERR, NTNIC, "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 3064783bd39SSerhii Iliushyk case 17U: 3074783bd39SSerhii Iliushyk state->br = 255U; /* QSFP28: 4 x 25G = 100G */ 3084783bd39SSerhii Iliushyk break; 3094783bd39SSerhii Iliushyk 31027c15342SSerhii Iliushyk default: 311*3489b87bSDanylo Vodopianov NT_LOG(INF, NTNIC, "nim_id = %u is not an QSFP/QSFP+/QSFP28 module", 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 3354783bd39SSerhii Iliushyk case 0x11: 3364783bd39SSerhii Iliushyk return "QSFP28"; 3374783bd39SSerhii 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; 408*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, "Instance %d: NIM id: %s (%d)", 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; 418*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)", ctx->instance, 41927c15342SSerhii Iliushyk yes_no[ctx->avg_pwr]); 42027c15342SSerhii Iliushyk 42127c15342SSerhii Iliushyk qsfp_read_vendor_info(ctx); 422*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, 423*3489b87bSDanylo Vodopianov "Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)", 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 4654783bd39SSerhii Iliushyk static void qsfp28_find_port_params(nim_i2c_ctx_p ctx) 4664783bd39SSerhii Iliushyk { 4674783bd39SSerhii Iliushyk uint8_t fiber_chan_speed; 4684783bd39SSerhii Iliushyk 4694783bd39SSerhii Iliushyk /* Table 6-17 SFF-8636 */ 4704783bd39SSerhii Iliushyk read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed); 4714783bd39SSerhii Iliushyk 4724783bd39SSerhii Iliushyk if (fiber_chan_speed & (1 << 7)) { 4734783bd39SSerhii Iliushyk /* SFF-8024, Rev 4.7, Table 4-4 */ 4744783bd39SSerhii Iliushyk uint8_t extended_specification_compliance_code = 0; 4754783bd39SSerhii Iliushyk read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1, 4764783bd39SSerhii Iliushyk &extended_specification_compliance_code); 4774783bd39SSerhii Iliushyk 4784783bd39SSerhii Iliushyk switch (extended_specification_compliance_code) { 4794783bd39SSerhii Iliushyk case 0x02: 4804783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_SR4; 4814783bd39SSerhii Iliushyk break; 4824783bd39SSerhii Iliushyk 4834783bd39SSerhii Iliushyk case 0x03: 4844783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_LR4; 4854783bd39SSerhii Iliushyk break; 4864783bd39SSerhii Iliushyk 4874783bd39SSerhii Iliushyk case 0x0B: 4884783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L; 4894783bd39SSerhii Iliushyk break; 4904783bd39SSerhii Iliushyk 4914783bd39SSerhii Iliushyk case 0x0C: 4924783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S; 4934783bd39SSerhii Iliushyk break; 4944783bd39SSerhii Iliushyk 4954783bd39SSerhii Iliushyk case 0x0D: 4964783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N; 4974783bd39SSerhii Iliushyk break; 4984783bd39SSerhii Iliushyk 4994783bd39SSerhii Iliushyk case 0x25: 5004783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_DR; 5014783bd39SSerhii Iliushyk break; 5024783bd39SSerhii Iliushyk 5034783bd39SSerhii Iliushyk case 0x26: 5044783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_FR; 5054783bd39SSerhii Iliushyk break; 5064783bd39SSerhii Iliushyk 5074783bd39SSerhii Iliushyk case 0x27: 5084783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28_LR; 5094783bd39SSerhii Iliushyk break; 5104783bd39SSerhii Iliushyk 5114783bd39SSerhii Iliushyk default: 5124783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28; 5134783bd39SSerhii Iliushyk } 5144783bd39SSerhii Iliushyk 5154783bd39SSerhii Iliushyk } else { 5164783bd39SSerhii Iliushyk ctx->port_type = NT_PORT_TYPE_QSFP28; 5174783bd39SSerhii Iliushyk } 5184783bd39SSerhii Iliushyk } 5194783bd39SSerhii Iliushyk 5204783bd39SSerhii Iliushyk /* 5214783bd39SSerhii Iliushyk * If true the user must actively select the desired rate. If false the module 5224783bd39SSerhii Iliushyk * however can still support several rates without the user is required to select 5234783bd39SSerhii Iliushyk * one of them. Supported rates must then be deduced from the product number. 5244783bd39SSerhii Iliushyk * SFF-8636, Rev 2.10a: 5254783bd39SSerhii Iliushyk * p40: 6.2.7 Rate Select 5264783bd39SSerhii Iliushyk * p85: A.2 Rate Select 5274783bd39SSerhii Iliushyk */ 5284783bd39SSerhii Iliushyk static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx) 5294783bd39SSerhii Iliushyk { 5304783bd39SSerhii Iliushyk const uint8_t ext_rate_select_compl_reg_addr = 141; 5314783bd39SSerhii Iliushyk const uint8_t options_reg_addr = 195; 5324783bd39SSerhii Iliushyk const uint8_t enh_options_reg_addr = 221; 5334783bd39SSerhii Iliushyk 5344783bd39SSerhii Iliushyk uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01; /* bit: 5 */ 5354783bd39SSerhii Iliushyk 5364783bd39SSerhii Iliushyk if (rate_select_ena == 0) 5374783bd39SSerhii Iliushyk return false; 5384783bd39SSerhii Iliushyk 5394783bd39SSerhii Iliushyk uint8_t rate_select_type = 5404783bd39SSerhii Iliushyk (read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03; /* bit 3..2 */ 5414783bd39SSerhii Iliushyk 5424783bd39SSerhii Iliushyk if (rate_select_type != 2) { 543*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, "NIM has unhandled rate select type (%d)", rate_select_type); 5444783bd39SSerhii Iliushyk return false; 5454783bd39SSerhii Iliushyk } 5464783bd39SSerhii Iliushyk 5474783bd39SSerhii Iliushyk uint8_t ext_rate_select_ver = 5484783bd39SSerhii Iliushyk read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03; /* bit 1..0 */ 5494783bd39SSerhii Iliushyk 5504783bd39SSerhii Iliushyk if (ext_rate_select_ver != 0x02) { 551*3489b87bSDanylo Vodopianov NT_LOG(DBG, NTNIC, "NIM has unhandled extended rate select version (%d)", 5524783bd39SSerhii Iliushyk ext_rate_select_ver); 5534783bd39SSerhii Iliushyk return false; 5544783bd39SSerhii Iliushyk } 5554783bd39SSerhii Iliushyk 5564783bd39SSerhii Iliushyk return true; /* When true selectRate() can be used */ 5574783bd39SSerhii Iliushyk } 5584783bd39SSerhii Iliushyk 5594783bd39SSerhii Iliushyk static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx) 5604783bd39SSerhii Iliushyk { 5614783bd39SSerhii Iliushyk if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || ctx->port_type == NT_PORT_TYPE_QSFP28_DR || 5624783bd39SSerhii Iliushyk ctx->port_type == NT_PORT_TYPE_QSFP28_LR) { 5634783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 5644783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_100G; 5654783bd39SSerhii Iliushyk 5664783bd39SSerhii Iliushyk else 5674783bd39SSerhii Iliushyk /* PAM-4 modules can only run on all lanes together */ 5684783bd39SSerhii Iliushyk ctx->speed_mask = 0; 5694783bd39SSerhii Iliushyk 5704783bd39SSerhii Iliushyk } else { 5714783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 5724783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_100G; 5734783bd39SSerhii Iliushyk 5744783bd39SSerhii Iliushyk else 5754783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_25G; 5764783bd39SSerhii Iliushyk 5774783bd39SSerhii Iliushyk if (qsfp28_is_rate_selection_enabled(ctx)) { 5784783bd39SSerhii Iliushyk /* 5794783bd39SSerhii Iliushyk * It is assumed that if the module supports dual rates then the other rate 5804783bd39SSerhii Iliushyk * is 10G per lane or 40G for all lanes. 5814783bd39SSerhii Iliushyk */ 5824783bd39SSerhii Iliushyk if (ctx->lane_idx < 0) 5834783bd39SSerhii Iliushyk ctx->speed_mask |= NT_LINK_SPEED_40G; 5844783bd39SSerhii Iliushyk 5854783bd39SSerhii Iliushyk else 5864783bd39SSerhii Iliushyk ctx->speed_mask = NT_LINK_SPEED_10G; 5874783bd39SSerhii Iliushyk } 5884783bd39SSerhii Iliushyk } 5894783bd39SSerhii Iliushyk } 5904783bd39SSerhii 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); 6194783bd39SSerhii 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 6604783bd39SSerhii Iliushyk static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx) 6614783bd39SSerhii Iliushyk { 6624783bd39SSerhii Iliushyk uint8_t data; 6634783bd39SSerhii Iliushyk bool init_complete_flag_present = false; 6644783bd39SSerhii Iliushyk 6654783bd39SSerhii Iliushyk /* 6664783bd39SSerhii Iliushyk * Revision compliance 6674783bd39SSerhii Iliushyk * 7: SFF-8636 Rev 2.5, 2.6 and 2.7 6684783bd39SSerhii Iliushyk * 8: SFF-8636 Rev 2.8, 2.9 and 2.10 6694783bd39SSerhii Iliushyk */ 6704783bd39SSerhii Iliushyk read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance), 6714783bd39SSerhii Iliushyk &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); 6724783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "NIM RevCompliance = %d", 6734783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); 6744783bd39SSerhii Iliushyk 6754783bd39SSerhii Iliushyk /* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */ 6764783bd39SSerhii Iliushyk if (ctx->lane_idx > 0) 6774783bd39SSerhii Iliushyk return; 6784783bd39SSerhii Iliushyk 6794783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) { 6804783bd39SSerhii Iliushyk /* Check if init complete flag is implemented */ 6814783bd39SSerhii Iliushyk read_data_lin(ctx, 221, sizeof(data), &data); 6824783bd39SSerhii Iliushyk init_complete_flag_present = (data & (1 << 4)) != 0; 6834783bd39SSerhii Iliushyk } 6844783bd39SSerhii Iliushyk 6854783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", init_complete_flag_present); 6864783bd39SSerhii Iliushyk 6874783bd39SSerhii Iliushyk /* 6884783bd39SSerhii Iliushyk * If the init complete flag is not present then wait 500ms that together with 500ms 6894783bd39SSerhii Iliushyk * after reset (in the adapter code) should be enough to read data from upper pages 6904783bd39SSerhii Iliushyk * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been 6914783bd39SSerhii Iliushyk * prone to this when trying to read sensor options using getQsfpOptionsFromData() 6924783bd39SSerhii Iliushyk * Probably because access to the paged address space is required. 6934783bd39SSerhii Iliushyk */ 6944783bd39SSerhii Iliushyk if (!init_complete_flag_present) { 6954783bd39SSerhii Iliushyk nt_os_wait_usec(500000); 6964783bd39SSerhii Iliushyk return; 6974783bd39SSerhii Iliushyk } 6984783bd39SSerhii Iliushyk 6994783bd39SSerhii Iliushyk /* Otherwise wait for the init complete flag to be set */ 7004783bd39SSerhii Iliushyk int count = 0; 7014783bd39SSerhii Iliushyk 7024783bd39SSerhii Iliushyk while (true) { 7034783bd39SSerhii Iliushyk if (count > 10) { /* 1 s timeout */ 7044783bd39SSerhii Iliushyk NT_LOG(WRN, NTHW, "Timeout waiting for module ready"); 7054783bd39SSerhii Iliushyk break; 7064783bd39SSerhii Iliushyk } 7074783bd39SSerhii Iliushyk 7084783bd39SSerhii Iliushyk read_data_lin(ctx, 6, sizeof(data), &data); 7094783bd39SSerhii Iliushyk 7104783bd39SSerhii Iliushyk if (data & 0x01) { 7114783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100); 7124783bd39SSerhii Iliushyk break; 7134783bd39SSerhii Iliushyk } 7144783bd39SSerhii Iliushyk 7154783bd39SSerhii Iliushyk nt_os_wait_usec(100000);/* 100 ms */ 7164783bd39SSerhii Iliushyk count++; 7174783bd39SSerhii Iliushyk } 7184783bd39SSerhii Iliushyk } 7194783bd39SSerhii Iliushyk 7204783bd39SSerhii Iliushyk static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx) 7214783bd39SSerhii Iliushyk { 7224783bd39SSerhii Iliushyk const char *const nim_list[] = { 7234783bd39SSerhii Iliushyk "AFBR-89BDDZ", /* Avago BiDi */ 7244783bd39SSerhii Iliushyk "AFBR-89BRDZ", /* Avago BiDi, RxOnly */ 7254783bd39SSerhii Iliushyk "FTLC4352RKPL", /* Finisar QSFP28-LR */ 7264783bd39SSerhii Iliushyk "FTLC4352RHPL", /* Finisar QSFP28-DR */ 7274783bd39SSerhii Iliushyk "FTLC4352RJPL", /* Finisar QSFP28-FR */ 7284783bd39SSerhii Iliushyk "SFBR-89BDDZ-CS4", /* Foxconn, QSFP28 100G/40G BiDi */ 7294783bd39SSerhii Iliushyk }; 7304783bd39SSerhii Iliushyk 7314783bd39SSerhii Iliushyk for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) { 7324783bd39SSerhii Iliushyk if (ctx->prod_no == nim_list[i]) { 7334783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); 7344783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true; 7354783bd39SSerhii Iliushyk NT_LOG(DBG, NTHW, "Found FEC info via PN list"); 7364783bd39SSerhii Iliushyk return; 7374783bd39SSerhii Iliushyk } 7384783bd39SSerhii Iliushyk } 7394783bd39SSerhii Iliushyk 7404783bd39SSerhii Iliushyk /* 7414783bd39SSerhii Iliushyk * For modules not in the list find FEC info via registers 7424783bd39SSerhii Iliushyk * Read if the module has controllable FEC 7434783bd39SSerhii Iliushyk * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing) 7444783bd39SSerhii Iliushyk * (Page 03h, Bytes 224-229) 7454783bd39SSerhii Iliushyk */ 7464783bd39SSerhii Iliushyk uint8_t data; 7474783bd39SSerhii Iliushyk uint16_t addr = 227 + 3 * 128; 7484783bd39SSerhii Iliushyk read_data_lin(ctx, addr, sizeof(data), &data); 7494783bd39SSerhii Iliushyk 7504783bd39SSerhii Iliushyk /* Check if the module has FEC support that can be controlled */ 7514783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0; 7524783bd39SSerhii Iliushyk ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0; 7534783bd39SSerhii Iliushyk 7544783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl) 7554783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); 7564783bd39SSerhii Iliushyk 7574783bd39SSerhii Iliushyk if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl) 7584783bd39SSerhii Iliushyk ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC); 7594783bd39SSerhii Iliushyk } 7604783bd39SSerhii Iliushyk 7614783bd39SSerhii Iliushyk static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx) 7624783bd39SSerhii Iliushyk { 7634783bd39SSerhii Iliushyk int res = qsfpplus_preinit(ctx, lane_idx); 7644783bd39SSerhii Iliushyk 7654783bd39SSerhii Iliushyk if (!res) { 7664783bd39SSerhii Iliushyk qsfp28_wait_for_ready_after_reset(ctx); 7674783bd39SSerhii Iliushyk memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0, 7684783bd39SSerhii Iliushyk sizeof(ctx->specific_u.qsfp.specific_u.qsfp28)); 7694783bd39SSerhii Iliushyk ctx->specific_u.qsfp.qsfp28 = true; 7704783bd39SSerhii Iliushyk qsfp28_find_port_params(ctx); 7714783bd39SSerhii Iliushyk qsfp28_get_fec_options(ctx); 7724783bd39SSerhii Iliushyk qsfp28_set_speed_mask(ctx); 7734783bd39SSerhii Iliushyk } 7744783bd39SSerhii Iliushyk 7754783bd39SSerhii Iliushyk return res; 7764783bd39SSerhii Iliushyk } 7774783bd39SSerhii 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 7874783bd39SSerhii Iliushyk case NT_NIM_QSFP28: 7884783bd39SSerhii Iliushyk qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1); 7894783bd39SSerhii Iliushyk break; 7904783bd39SSerhii Iliushyk 79127c15342SSerhii Iliushyk default: 79227c15342SSerhii Iliushyk res = 1; 793*3489b87bSDanylo Vodopianov NT_LOG(ERR, NTHW, "NIM type %s is not supported.", nim_id_to_text(ctx->nim_id)); 79427c15342SSerhii Iliushyk } 79527c15342SSerhii Iliushyk 796eaf1ebdcSSerhii Iliushyk return res; 797eaf1ebdcSSerhii Iliushyk } 798