From: Serhii Iliushyk <sil-plv@napatech.com>
To: dev@dpdk.org
Cc: mko-plv@napatech.com, ckm@napatech.com,
andrew.rybchenko@oktetlabs.ru, ferruh.yigit@amd.com
Subject: [PATCH v1 17/17] net/ntnic: add NIM module
Date: Thu, 30 May 2024 16:49:14 +0200 [thread overview]
Message-ID: <20240530144929.4127931-17-sil-plv@napatech.com> (raw)
In-Reply-To: <20240530144929.4127931-1-sil-plv@napatech.com>
Includes support for NIM QSFP and QSFP+.
Signed-off-by: Serhii Iliushyk <sil-plv@napatech.com>
---
drivers/net/ntnic/meson.build | 20 +-
drivers/net/ntnic/nim/i2c_nim.c | 1359 +++++++++++++++++
drivers/net/ntnic/nim/i2c_nim.h | 77 +
.../net/ntnic/nim/include/qsfp_registers.h | 58 +
drivers/net/ntnic/nim/nim_defines.h | 69 +
drivers/net/ntnic/nim/nt_link_speed.c | 115 ++
drivers/net/ntnic/nim/nt_link_speed.h | 16 +
7 files changed, 1707 insertions(+), 7 deletions(-)
create mode 100644 drivers/net/ntnic/nim/i2c_nim.c
create mode 100644 drivers/net/ntnic/nim/i2c_nim.h
create mode 100644 drivers/net/ntnic/nim/include/qsfp_registers.h
create mode 100644 drivers/net/ntnic/nim/nim_defines.h
create mode 100644 drivers/net/ntnic/nim/nt_link_speed.c
create mode 100644 drivers/net/ntnic/nim/nt_link_speed.h
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index 9b1d75ef48..53c38c9f0b 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -40,7 +40,7 @@ includes = [
# include_directories('sensors/avr_sensors'),
# include_directories('sensors/board_sensors'),
# include_directories('nim/nim_sensors'),
- # include_directories('nim/include'),
+ include_directories('nim/include'),
# include_directories('nim/sfp_sensors'),
# include_directories('nim/qsfp_sensors'),
# include_directories('sensors/ntavr'),
@@ -64,9 +64,9 @@ if fs.is_dir('nthw/core/nt400dxx')
includes += include_directories('nthw/core/nt400dxx')
includes += include_directories('nthw/core/nt400dxx/reset')
endif
-# includes += include_directories('adapter')
-# includes += include_directories('link_mgmt')
-# includes += include_directories('nim')
+includes += include_directories('adapter')
+includes += include_directories('link_mgmt')
+includes += include_directories('nim')
# includes += include_directories('nim/nim_sensors')
if fs.is_dir('nim/sfp_sensors')
includes += include_directories('nim/sfp_sensors')
@@ -83,10 +83,14 @@ headers = files('rte_pmd_ntnic.h')
# all sources
sources = files(
+ 'adapter/nt4ga_adapter.c',
+ 'adapter/nt4ga_pci_ta_tg.c',
+ 'adapter/nt4ga_tfg.c',
'dpdk_mod_reg.c',
- 'drivers/net/ntnic/driver/modules/adapter/nt4ga_adapter.c',
- 'drivers/net/ntnic/driver/modules/adapter/nt4ga_pci_ta_tg.c',
- 'drivers/net/ntnic/driver/modules/adapter/nt4ga_tfg.c',
+ 'link_mgmt/link_100g/nt4ga_link_100g.c',
+ 'link_mgmt/nt4ga_link.c',
+ 'nim/i2c_nim.c',
+ 'nim/nt_link_speed.c',
'nthw/supported/nthw_fpga_9563_055_039_0000.c',
'nthw/supported/nthw_fpga_instances.c',
'nthw/supported/nthw_fpga_mod_str_map.c',
@@ -98,9 +102,11 @@ sources = files(
'nthw/core/nthw_fpga_rst.c',
'nthw/core/nthw_gfg.c',
'nthw/core/nthw_gmf.c',
+ 'nthw/core/nthw_gpio_phy.c',
'nthw/core/nthw_hif.c',
'nthw/core/nthw_i2cm.c',
'nthw/core/nthw_iic.c',
+ 'nthw/core/nthw_mac_pcs.c',
'nthw/core/nthw_mac_pcs_xxv.c',
'nthw/core/nthw_pcie3.c',
'nthw/core/nthw_pci_rd_tg.c',
diff --git a/drivers/net/ntnic/nim/i2c_nim.c b/drivers/net/ntnic/nim/i2c_nim.c
new file mode 100644
index 0000000000..c09f94d515
--- /dev/null
+++ b/drivers/net/ntnic/nim/i2c_nim.c
@@ -0,0 +1,1359 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "nthw_drv.h"
+#include "i2c_nim.h"
+#include "ntlog.h"
+#include "nt_util.h"
+#include "ntnic_mod_reg.h"
+
+#include "qsfp_registers.h"
+
+#include <assert.h>
+#include <string.h> /* memcmp, memset */
+
+/*
+ * Nim functions
+ */
+#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */
+#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */
+#define QSFP_CONTROL_STATUS_LIN_ADDR 86
+#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F
+#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */
+#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */
+#define NIM_READ false
+#define NIM_WRITE true
+#define NIM_PAGE_SEL_REGISTER 127
+#define NIM_I2C_0XA0 0xA0 /* Basic I2C address */
+#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */
+#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */
+#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */
+#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */
+#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */
+#define QSFP_DMI_OPTION_LIN_ADDR 220
+
+#define QSFP_EXTENDED_IDENTIFIER 129
+#define QSFP_POWER_CLASS_BITS_1_4 0xC0
+#define QSFP_POWER_CLASS_BITS_5_7 0x03
+
+static bool page_addressing(nt_nim_identifier_t id)
+{
+ switch (id) {
+ case NT_NIM_SFP_SFP_PLUS:
+ return false;
+
+ case NT_NIM_XFP:
+ return true;
+
+ case NT_NIM_QSFP:
+ case NT_NIM_QSFP_PLUS:
+ case NT_NIM_QSFP28:
+ return true;
+
+ default:
+ NT_LOG(DBG, ETHDEV, "%s: Unknown NIM identifier %d\n", __func__, id);
+ return false;
+ }
+}
+
+nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx)
+{
+ return (nt_nim_identifier_t)ctx->nim_id;
+}
+
+static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write, uint16_t lin_addr,
+ uint8_t i2c_addr, uint8_t a_reg_addr, uint8_t seq_cnt,
+ uint8_t *p_data)
+{
+ /* Divide i2c_addr by 2 because nthw_iic_read/writeData multiplies by 2 */
+ const uint8_t i2c_devaddr = i2c_addr / 2U;
+ (void)lin_addr; /* Unused */
+
+ if (do_write) {
+ if (ctx->type == I2C_HWIIC) {
+ return nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt,
+ p_data);
+
+ } else {
+ return 0;
+ }
+
+ } else if (ctx->type == I2C_HWIIC) {
+ return nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt, p_data);
+
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------------
+ * Selects a new page for page addressing. This is only relevant if the NIM
+ * supports this. Since page switching can take substantial time the current page
+ * select is read and subsequently only changed if necessary.
+ * Important:
+ * XFP Standard 8077, Ver 4.5, Page 61 states that:
+ * If the host attempts to write a table select value which is not supported in
+ * a particular module, the table select byte will revert to 01h.
+ * This can lead to some surprising result that some pages seems to be duplicated.
+ * ------------------------------------------------------------------------------
+ */
+
+static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel)
+{
+ uint8_t curr_page_sel;
+
+ /* Read the current page select value */
+ if (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
+ NIM_PAGE_SEL_REGISTER, sizeof(curr_page_sel),
+ &curr_page_sel) != 0) {
+ return -1;
+ }
+
+ /* Only write new page select value if necessary */
+ if (page_sel != curr_page_sel) {
+ if (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
+ NIM_PAGE_SEL_REGISTER, sizeof(page_sel),
+ &page_sel) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing, uint16_t lin_addr,
+ uint16_t length, uint8_t *p_data, bool do_write)
+{
+ uint16_t i;
+ uint8_t a_reg_addr; /* The actual register address in I2C device */
+ uint8_t i2c_addr;
+ int block_size = 128; /* Equal to size of MSA pages */
+ int seq_cnt;
+ int max_seq_cnt = 1;
+ int multi_byte = 1; /* One byte per I2C register is default */
+
+ for (i = 0; i < length;) {
+ bool use_page_select = false;
+
+ /*
+ * Find out how much can be read from the current block in case of
+ * single byte access
+ */
+ if (multi_byte == 1)
+ max_seq_cnt = block_size - (lin_addr % block_size);
+
+ if (m_page_addressing) {
+ if (lin_addr >= 128) { /* Only page setup above this address */
+ use_page_select = true;
+
+ /* Map to [128..255] of 0xA0 device */
+ a_reg_addr = (uint8_t)(block_size + (lin_addr % block_size));
+
+ } else {
+ a_reg_addr = (uint8_t)lin_addr;
+ }
+
+ i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
+
+ } else if (lin_addr >= 256) {
+ /* Map to address [0..255] of 0xA2 device */
+ a_reg_addr = (uint8_t)(lin_addr - 256);
+ i2c_addr = NIM_I2C_0XA2;
+
+ } else {
+ a_reg_addr = (uint8_t)lin_addr;
+ i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
+ }
+
+ /* Now actually do the reading/writing */
+ seq_cnt = length - i; /* Number of remaining bytes */
+
+ if (seq_cnt > max_seq_cnt)
+ seq_cnt = max_seq_cnt;
+
+ /*
+ * Read a number of bytes without explicitly specifying a new address.
+ * This can speed up I2C access since automatic incrementation of the
+ * I2C device internal address counter can be used. It also allows
+ * a HW implementation, that can deal with block access.
+ * Furthermore it also allows for access to data that must be accessed
+ * as 16bit words reading two bytes at each address eg PHYs.
+ */
+ if (use_page_select) {
+ if (nim_setup_page(ctx, (uint8_t)((lin_addr / 128) - 1)) != 0) {
+ NT_LOG(ERR, ETHDEV,
+ "%s: Cannot set up page for linear address %u\n", __func__,
+ lin_addr);
+ return -1;
+ }
+ }
+
+ if (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr, a_reg_addr,
+ (uint8_t)seq_cnt, p_data) != 0) {
+ NT_LOG(ERR, ETHDEV, "%s: Call to nim_read_write_i2c_data failed\n",
+ __func__);
+ return -1;
+ }
+
+ p_data += seq_cnt;
+ i = (uint16_t)(i + seq_cnt);
+ lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));
+ }
+
+ return 0;
+}
+
+int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data)
+{
+ /* Wrapper for using Mutex for QSFP TODO */
+ return nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), lin_addr, length, data,
+ NIM_READ);
+}
+
+/* Read and return a single byte */
+static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr)
+{
+ uint8_t data;
+ read_data_lin(ctx, addr, sizeof(data), &data);
+ return data;
+}
+
+static int nim_read_id(nim_i2c_ctx_t *ctx)
+{
+ /* We are only reading the first byte so we don't care about pages here. */
+ const bool USE_PAGE_ADDRESSING = false;
+
+ if (nim_read_write_data_lin(ctx, USE_PAGE_ADDRESSING, NIM_IDENTIFIER_ADDR,
+ sizeof(ctx->nim_id), &ctx->nim_id, NIM_READ) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int i2c_nim_common_construct(nim_i2c_ctx_p ctx)
+{
+ ctx->nim_id = 0;
+ int res;
+
+ if (ctx->type == I2C_HWIIC)
+ res = nim_read_id(ctx);
+
+ else
+ res = -1;
+
+ if (res) {
+ NT_LOG(ERR, PMD, "Can't read NIM id.");
+ return res;
+ }
+
+ memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name));
+ memset(ctx->prod_no, 0, sizeof(ctx->prod_no));
+ memset(ctx->serial_no, 0, sizeof(ctx->serial_no));
+ memset(ctx->date, 0, sizeof(ctx->date));
+ memset(ctx->rev, 0, sizeof(ctx->rev));
+
+ ctx->content_valid = false;
+ memset(ctx->len_info, 0, sizeof(ctx->len_info));
+ ctx->pwr_level_req = 0;
+ ctx->pwr_level_cur = 0;
+ ctx->avg_pwr = false;
+ ctx->tx_disable = false;
+ ctx->lane_idx = -1;
+ ctx->lane_count = 1;
+ ctx->options = 0;
+ return 0;
+}
+
+static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data);
+
+static void qsfp_read_vendor_info(nim_i2c_ctx_t *ctx)
+{
+ nim_read_vendor_info(ctx, QSFP_VENDOR_NAME_LIN_ADDR, sizeof(ctx->vendor_name),
+ ctx->vendor_name);
+ nim_read_vendor_info(ctx, QSFP_VENDOR_PN_LIN_ADDR, sizeof(ctx->prod_no), ctx->prod_no);
+ nim_read_vendor_info(ctx, QSFP_VENDOR_SN_LIN_ADDR, sizeof(ctx->serial_no), ctx->serial_no);
+ nim_read_vendor_info(ctx, QSFP_VENDOR_DATE_LIN_ADDR, sizeof(ctx->date), ctx->date);
+ nim_read_vendor_info(ctx, QSFP_VENDOR_REV_LIN_ADDR, (uint8_t)(sizeof(ctx->rev) - 2),
+ ctx->rev); /*OBS Only two bytes*/
+}
+static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
+{
+ int res = 0; /* unused due to no readings from HW */
+
+ assert(ctx && state);
+ assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized");
+
+ (void)memset(state, 0, sizeof(*state));
+
+ switch (ctx->nim_id) {
+ case 12U:
+ state->br = 10U;/* QSFP: 4 x 1G = 4G */
+ break;
+
+ case 13U:
+ state->br = 103U; /* QSFP+: 4 x 10G = 40G */
+ break;
+
+ case 17U:
+ state->br = 255U; /* QSFP28: 4 x 25G = 100G */
+ break;
+
+ default:
+ NT_LOG(INF, PMD, "%s:%d nim_id = %u is not an QSFP/QSFP+/QSFP28 module\n",
+ __func__, __LINE__, ctx->nim_id);
+ res = -1;
+ }
+
+ return res;
+}
+
+int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
+{
+ if (translate_nimid(ctx) == NT_NIM_SFP_SFP_PLUS)
+ return -1;
+
+ else
+ return qsfp_nim_state_build(ctx, state);
+}
+
+const char *nim_id_to_text(uint8_t nim_id)
+{
+ switch (nim_id) {
+ case 0x0:
+ return "UNKNOWN";
+
+ case 0x1:
+ return "GBIC";
+
+ case 0x2:
+ return "FIXED";
+
+ case 0x3:
+ return "SFP/SFP+";
+
+ case 0x04:
+ return "300 pin XBI";
+
+ case 0x05:
+ return "XEN-PAK";
+
+ case 0x06:
+ return "XFP";
+
+ case 0x07:
+ return "XFF";
+
+ case 0x08:
+ return "XFP-E";
+
+ case 0x09:
+ return "XPAK";
+
+ case 0x0A:
+ return "X2";
+
+ case 0x0B:
+ return "DWDM";
+
+ case 0x0C:
+ return "QSFP";
+
+ case 0x0D:
+ return "QSFP+";
+
+ case 0x11:
+ return "QSFP28";
+
+ case 0x12:
+ return "CFP4";
+
+ default:
+ return "ILLEGAL!";
+ }
+}
+
+/*
+ * Disable laser for specific lane or all lanes
+ */
+int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable, int lane_idx)
+{
+ uint8_t value;
+ uint8_t mask;
+ const bool pg_addr = page_addressing(ctx->nim_id);
+
+ if (lane_idx < 0) /* If no lane is specified then all lanes */
+ mask = QSFP_SOFT_TX_ALL_DISABLE_BITS;
+
+ else
+ mask = (uint8_t)(1U << lane_idx);
+
+ if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
+ &value, NIM_READ) != 0) {
+ return -1;
+ }
+
+ if (disable)
+ value |= mask;
+
+ else
+ value &= (uint8_t)(~mask);
+
+ if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
+ &value, NIM_WRITE) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Read vendor information at a certain address. Any trailing whitespace is
+ * removed and a missing string termination in the NIM data is handled.
+ */
+static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data)
+{
+ const bool pg_addr = page_addressing(ctx->nim_id);
+ int i;
+ /* Subtract "1" from max_len that includes a terminating "0" */
+
+ if (nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1), (uint8_t *)p_data,
+ NIM_READ) != 0) {
+ return -1;
+ }
+
+ /* Terminate at first found white space */
+ for (i = 0; i < max_len - 1; i++) {
+ if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || *p_data == '\v' ||
+ *p_data == '\f' || *p_data == '\r') {
+ *p_data = '\0';
+ return 0;
+ }
+
+ p_data++;
+ }
+
+ /*
+ * Add line termination as the very last character, if it was missing in the
+ * NIM data
+ */
+ *p_data = '\0';
+ return 0;
+}
+
+/*
+ * Import length info in various units from NIM module data and convert to meters
+ */
+static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info, uint16_t *p_nim_units)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++)
+ if (*(p_nim_len_info + i) == 255) {
+ ctx->len_info[i] = 65535;
+
+ } else {
+ uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i);
+
+ if (len > 65535)
+ ctx->len_info[i] = 65535;
+
+ else
+ ctx->len_info[i] = (uint16_t)len;
+ }
+}
+
+static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx)
+{
+ const bool pg_addr = page_addressing(ctx->nim_id);
+ uint8_t options;
+ uint8_t value;
+ uint8_t nim_len_info[5];
+ uint16_t nim_units[5] = { 1000, 2, 1, 1, 1 }; /* QSFP MSA units in meters */
+ const char *yes_no[2] = { "No", "Yes" };
+ (void)yes_no;
+ NT_LOG(DBG, ETHDEV, "Instance %d: NIM id: %s (%d)\n", ctx->instance,
+ nim_id_to_text(ctx->nim_id), ctx->nim_id);
+
+ /* Read DMI options */
+ if (nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR, sizeof(options),
+ &options, NIM_READ) != 0) {
+ return -1;
+ }
+
+ ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT;
+ NT_LOG(DBG, ETHDEV, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)\n", ctx->instance,
+ yes_no[ctx->avg_pwr]);
+
+ qsfp_read_vendor_info(ctx);
+ NT_LOG(DBG, PMD,
+ "Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)\n",
+ ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no, ctx->date, ctx->rev);
+
+ if (nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info),
+ nim_len_info, NIM_READ) != 0) {
+ return -1;
+ }
+
+ /*
+ * Returns supported length information in meters for various fibers as 5 indivi-
+ * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper]
+ * If no length information is available for a certain entry, the returned value
+ * will be zero. This will be the case for SFP modules - EBW entry.
+ * If the MSBit is set the returned value in the lower 31 bits indicates that the
+ * supported length is greater than this.
+ */
+
+ nim_import_len_info(ctx, nim_len_info, nim_units);
+
+ /* Read required power level */
+ if (nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER, sizeof(value), &value,
+ NIM_READ) != 0) {
+ return -1;
+ }
+
+ /*
+ * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43:
+ * If power class >= 5 setHighPower must be called for the module to be fully
+ * functional
+ */
+ if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) {
+ /* NIM in power class 1 - 4 */
+ ctx->pwr_level_req = (uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) + 1);
+
+ } else {
+ /* NIM in power class 5 - 7 */
+ ctx->pwr_level_req = (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4);
+ }
+
+ return 0;
+}
+
+/*
+ * Read the vendor product number and from this determine which QSFP DMI options
+ * that are present. This list also covers QSFP28 modules.
+ * This function should be used if automatic detection does not work.
+ */
+static bool qsfpplus_get_qsfp_options_from_pn(nim_i2c_ctx_p ctx)
+{
+ if (strcmp(ctx->prod_no, "FTL410QE1C") == 0) {
+ /* FINISAR FTL410QE1C, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_TX_BIAS) | (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "FTL410QE2C") == 0) {
+ /* FINISAR FTL410QE2C, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY);
+
+ } else if (strcmp(ctx->prod_no, "FTL4C1QE1C") == 0) {
+ /* FINISAR FTL4C1QE1C, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79E4Z") == 0) {
+ /*
+ * AFBR-79E4Z: The digital diagnostic accuracy is not guaranteed so only
+ * the mandatory temperature sensor is made available (although it will
+ * also be inaccurate)
+ */
+ /* AVAGO 79E4Z, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79E4Z-D") == 0) {
+ /* AVAGO 79E4Z-D, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79EQDZ") == 0) {
+ /* AVAGO 79EQDZ, QSFP+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79EBRZ") == 0) {
+ /*
+ * Avago RxOnly BiDi NIM
+ * No sensors available not even the normally mandatory temp sensor and this
+ * is ok since the temp sensor is not mandatory on active optical modules
+ */
+ /* SFF-8436_rev4.1, p67 */
+ ctx->options = (1 << NIM_OPTION_RX_ONLY);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79EBPZ-NU1") == 0) {
+ /*
+ * Avago RxTx BiDi NIM
+ * No sensors available not even the normally mandatory temp sensor and this
+ * is ok since the temp sensor is not mandatory on active optical modules
+ */
+ ctx->options = 0;
+
+ } else if (strcmp(ctx->prod_no, "AFBR-79EBPZ") == 0) {
+ /*
+ * Avago RxTx BiDi NIM
+ * No sensors available not even the normally mandatory temp sensor and this
+ * is ok since the temp sensor is not mandatory on active optical modules
+ */
+ ctx->options = 0;
+
+ } else if (strcmp(ctx->prod_no, "AFBR-89CDDZ") == 0) {
+ /* AVAGO 89CDDZ, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-89BDDZ") == 0) {
+ /* AVAGO 89BDDZ, QSFP28, BiDi */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "AFBR-89BRDZ") == 0) {
+ /*
+ * AVAGO 89BRDZ, QSFP28, BiDi, RxOnly
+ * but sensors have been set as above except for Tx sensors
+ */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_RX_ONLY);
+ /*
+ * According to mail correspondence AFBR-89BRDZ is a RxOnly version of
+ * AFBR-89BDDZ with lasers default off.
+ * The lasers can be turned on however but should probably not because the
+ * receivers might be degraded, and this is the cause for selling them as RxOnly.
+ */
+
+ } else if (strcmp(ctx->prod_no, "SQF1000L4LNGG01P") == 0) {
+ /* Sumitomo SQF1000L4LNGG01P, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "SQF1000L4LNGG01B") == 0) {
+ /* Sumitomo SQF1000L4LNGG01B, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "SQF1001L4LNGG01P") == 0) {
+ /* Sumitomo SQF1001L4LNGG01P, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "SQF1001L4LNGG01B") == 0) {
+ /* Sumitomo SQF1001L4LNGG01B, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "SQF1002L4LNGG01B") == 0) {
+ /* Sumitomo SQF1002L4LNGG01B, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "FIM37700/171") == 0) {
+ /* Fujitsu FIM37700/171, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "FIM37700/172") == 0) {
+ /* Fujitsu FIM37700/172, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "TR-FC85S-NVS") == 0) {
+ /* InnoLight TR-FC85S-NVS, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "TR-FC13L-NVS") == 0) {
+ /* InnoLight TR-FC13L-NVS, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "FTLC9551REPM") == 0) {
+ /* Finisar FTLC9551REPM, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else if (strcmp(ctx->prod_no, "FTLC9558REPM") == 0) {
+ /* Finisar FTLC9558REPM, QSFP28 */
+ ctx->options = (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |
+ (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |
+ (1 << NIM_OPTION_TX_POWER);
+
+ } else {
+ /*
+ * DO NOTE: The temperature sensor is not mandatory on active/passive copper
+ * and active optical modules
+ */
+ ctx->options = (1 << NIM_OPTION_TEMP);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Try to figure out if a sensor is present by reading its value(s) and its limits.
+ * This is a highly impirical way that cannot be guaranteed to give the correct
+ * result but it was a wish not to be dependent on a PN table based solution.
+ */
+static void qsfpplus_find_qsfp_sensor_option(nim_i2c_ctx_p ctx,
+ uint16_t value_addr,
+ uint8_t lane_count,
+ uint16_t limit_addr,
+ bool two_compl,
+ uint32_t sensor_option)
+{
+ uint8_t data[8];
+ int i, j;
+ int value;
+ int value_list[4];
+ int limit;
+ int limit_list[4];
+ bool present;
+
+ /* Read current value(s) */
+ read_data_lin(ctx, value_addr, (uint16_t)(lane_count * 2), data);
+
+ for (j = 0; j < lane_count; j++) {
+ value = 0;
+
+ for (i = 0; i < 2; i++) {
+ value = value << 8;
+ value += data[2 * j + i];
+ }
+
+ if (two_compl && value >= 0x8000)
+ value = value - 0x10000;
+
+ value_list[j] = value;
+ }
+
+ /* Read limits Warning high/low Alarm high/low 4 values each two bytes */
+ read_data_lin(ctx, limit_addr, 8, data);
+
+ for (j = 0; j < 4; j++) {
+ limit = 0;
+
+ for (i = 0; i < 2; i++) {
+ limit = limit << 8;
+ limit += data[2 * j + i];
+ }
+
+ if (two_compl && limit >= 0x8000)
+ limit = limit - 0x10000;
+
+ limit_list[j] = limit;
+ }
+
+ /* Find out if limits contradicts each other */
+ int alarm_high = limit_list[0];
+ int alarm_low = limit_list[1];
+ int warn_high = limit_list[2];
+ int warn_low = limit_list[3];
+
+ bool alarm_limits = false; /* Are they present - that is both not zero */
+ bool warn_limits = false;
+ bool limit_conflict = false;
+
+ if (alarm_high != 0 || alarm_low != 0) {
+ alarm_limits = true;
+
+ if (alarm_high <= alarm_low)
+ limit_conflict = true;
+ }
+
+ if (warn_high != 0 || warn_low != 0) {
+ warn_limits = true;
+
+ /* Warning limits must be least restrictive */
+ if (warn_high <= warn_low)
+ limit_conflict = true;
+
+ else if (warn_high > alarm_high || warn_low < alarm_low)
+ limit_conflict = true;
+ }
+
+ /* Try to deduce if the sensor is present or not */
+ present = false;
+
+ if (limit_conflict) {
+ present = false;
+
+ } else if (warn_limits || alarm_limits) {
+ /* Is one or both present and not contradictory */
+ present = true;
+
+ } else {
+ /*
+ * All limits are zero - look at the sensor value
+ * If one sensor is non-zero the sensor is set to be present
+ */
+ for (j = 0; j < lane_count; j++) {
+ if (value_list[j] != 0) {
+ present = true;
+ break;
+ }
+ }
+
+ /*
+ * If all limits and values are zero then present will be false here. In this
+ * case it is assumed that the sensor is not present:
+ * Experience indicates that for QSFP+ modules RxPwr will be non-zero even with
+ * no optical input. QSFP28 modules however can easily have RxPwr equal to zero
+ * with no optical input.
+ * For all investigated modules it was found that if RxPwr is implemented then
+ * the limits are also set. This is not always the case with TxBias and TxPwr
+ * but here the measured values will be non-zero when the laser is on what it
+ * will be just after initialization since it has no external hardware disable.
+ */
+ }
+
+ if (present)
+ ctx->options |= (1U << sensor_option);
+}
+
+/*
+ * Find active QSFP sensors.
+ */
+static void qsfpplus_get_qsfp_options_from_data(nim_i2c_ctx_p ctx)
+{
+ ctx->options = 0;
+
+ qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TEMP_LIN_ADDR, 1, QSFP_TEMP_THRESH_LIN_ADDR,
+ true, NIM_OPTION_TEMP);
+
+ qsfpplus_find_qsfp_sensor_option(ctx, QSFP_VOLT_LIN_ADDR, 1, QSFP_VOLT_THRESH_LIN_ADDR,
+ false, NIM_OPTION_SUPPLY);
+
+ qsfpplus_find_qsfp_sensor_option(ctx, QSFP_RX_PWR_LIN_ADDR, 4, QSFP_RX_PWR_THRESH_LIN_ADDR,
+ false, NIM_OPTION_RX_POWER);
+
+ qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_PWR_LIN_ADDR, 4, QSFP_TX_PWR_THRESH_LIN_ADDR,
+ false, NIM_OPTION_TX_POWER);
+
+ qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_BIAS_LIN_ADDR, 4, QSFP_BIAS_THRESH_LIN_ADDR,
+ false, NIM_OPTION_TX_BIAS);
+}
+
+static void qsfp28_find_port_params(nim_i2c_ctx_p ctx)
+{
+ uint8_t fiber_chan_speed;
+
+ /* Table 6-17 SFF-8636 */
+ read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed);
+
+ if (fiber_chan_speed & (1 << 7)) {
+ /* SFF-8024, Rev 4.7, Table 4-4 */
+ uint8_t extended_specification_compliance_code = 0;
+ read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1,
+ &extended_specification_compliance_code);
+
+ switch (extended_specification_compliance_code) {
+ case 0x02:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_SR4;
+ break;
+
+ case 0x03:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_LR4;
+ break;
+
+ case 0x0B:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L;
+ break;
+
+ case 0x0C:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S;
+ break;
+
+ case 0x0D:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N;
+ break;
+
+ case 0x25:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_DR;
+ break;
+
+ case 0x26:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_FR;
+ break;
+
+ case 0x27:
+ ctx->port_type = NT_PORT_TYPE_QSFP28_LR;
+ break;
+
+ default:
+ ctx->port_type = NT_PORT_TYPE_QSFP28;
+ }
+
+ } else {
+ ctx->port_type = NT_PORT_TYPE_QSFP28;
+ }
+}
+
+/*
+ * If true the user must actively select the desired rate. If false the module
+ * however can still support several rates without the user is required to select
+ * one of them. Supported rates must then be deduced from the product number.
+ * SFF-8636, Rev 2.10a:
+ * p40: 6.2.7 Rate Select
+ * p85: A.2 Rate Select
+ */
+static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx)
+{
+ const uint8_t ext_rate_select_compl_reg_addr = 141;
+ const uint8_t options_reg_addr = 195;
+ const uint8_t enh_options_reg_addr = 221;
+
+ uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01; /* bit: 5 */
+
+ if (rate_select_ena == 0)
+ return false;
+
+ uint8_t rate_select_type =
+ (read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03; /* bit 3..2 */
+
+ if (rate_select_type != 2) {
+ NT_LOG(DBG, PMD, "NIM has unhandled rate select type (%d)", rate_select_type);
+ return false;
+ }
+
+ uint8_t ext_rate_select_ver =
+ read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03; /* bit 1..0 */
+
+ if (ext_rate_select_ver != 0x02) {
+ NT_LOG(DBG, PMD, "NIM has unhandled extended rate select version (%d)",
+ ext_rate_select_ver);
+ return false;
+ }
+
+ return true; /* When true selectRate() can be used */
+}
+
+static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx)
+{
+ if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || ctx->port_type == NT_PORT_TYPE_QSFP28_DR ||
+ ctx->port_type == NT_PORT_TYPE_QSFP28_LR) {
+ if (ctx->lane_idx < 0)
+ ctx->speed_mask = NT_LINK_SPEED_100G;
+
+ else
+ /* PAM-4 modules can only run on all lanes together */
+ ctx->speed_mask = 0;
+
+ } else {
+ if (ctx->lane_idx < 0)
+ ctx->speed_mask = NT_LINK_SPEED_100G;
+
+ else
+ ctx->speed_mask = NT_LINK_SPEED_25G;
+
+ if (qsfp28_is_rate_selection_enabled(ctx)) {
+ /*
+ * It is assumed that if the module supports dual rates then the other rate
+ * is 10G per lane or 40G for all lanes.
+ */
+ if (ctx->lane_idx < 0)
+ ctx->speed_mask |= NT_LINK_SPEED_40G;
+
+ else
+ ctx->speed_mask = NT_LINK_SPEED_10G;
+ }
+ }
+}
+
+static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx)
+{
+ uint8_t device_tech;
+ read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), &device_tech);
+
+ switch (device_tech & 0xF0) {
+ case 0xA0: /* Copper cable unequalized */
+ case 0xB0: /* Copper cable passive equalized */
+ ctx->port_type = NT_PORT_TYPE_QSFP_PASSIVE_DAC;
+ break;
+
+ case 0xC0: /* Copper cable, near and far end limiting active equalizers */
+ case 0xD0: /* Copper cable, far end limiting active equalizers */
+ case 0xE0: /* Copper cable, near end limiting active equalizers */
+ case 0xF0: /* Copper cable, linear active equalizers */
+ ctx->port_type = NT_PORT_TYPE_QSFP_ACTIVE_DAC;
+ break;
+
+ default:/* Optical */
+ ctx->port_type = NT_PORT_TYPE_QSFP_PLUS;
+ break;
+ }
+}
+
+static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx)
+{
+ ctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G : (NT_LINK_SPEED_10G);
+}
+
+static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx)
+{
+ assert(lane_idx < 4);
+ ctx->specific_u.qsfp.qsfp28 = false;
+ ctx->lane_idx = lane_idx;
+ ctx->lane_count = 4;
+}
+
+static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
+{
+ qsfpplus_construct(ctx, lane_idx);
+ int res = qsfpplus_read_basic_data(ctx);
+
+ if (!res) {
+ qsfpplus_find_port_params(ctx);
+
+ /*
+ * If not on the known modules list try to figure out which sensors that are
+ * present
+ */
+ if (!qsfpplus_get_qsfp_options_from_pn(ctx)) {
+ NT_LOG(DBG, NTHW, "NIM options not known in advance - trying to detect");
+ qsfpplus_get_qsfp_options_from_data(ctx);
+ }
+
+ /*
+ * Read if TX_DISABLE has been implemented
+ * For passive optical modules this is required while it for copper and active
+ * optical modules is optional. Under all circumstances register 195.4 will
+ * indicate, if TX_DISABLE has been implemented in register 86.0-3
+ */
+ uint8_t value;
+ read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value);
+
+ ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0;
+
+ if (ctx->tx_disable)
+ ctx->options |= (1 << NIM_OPTION_TX_DISABLE);
+
+ /*
+ * Previously - considering AFBR-89BRDZ - code tried to establish if a module was
+ * RxOnly by testing the state of the lasers after reset. Lasers were for this
+ * module default disabled.
+ * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was
+ * decided that this option should not be detected automatically but from PN
+ */
+ ctx->specific_u.qsfp.rx_only = (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0;
+ qsfpplus_set_speed_mask(ctx);
+ }
+
+ return res;
+}
+
+static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx)
+{
+ uint8_t data;
+ bool init_complete_flag_present = false;
+
+ /*
+ * Revision compliance
+ * 7: SFF-8636 Rev 2.5, 2.6 and 2.7
+ * 8: SFF-8636 Rev 2.8, 2.9 and 2.10
+ */
+ read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance),
+ &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
+ NT_LOG(DBG, NTHW, "NIM RevCompliance = %d",
+ ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
+
+ /* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */
+ if (ctx->lane_idx > 0)
+ return;
+
+ if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) {
+ /* Check if init complete flag is implemented */
+ read_data_lin(ctx, 221, sizeof(data), &data);
+ init_complete_flag_present = (data & (1 << 4)) != 0;
+ }
+
+ NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", init_complete_flag_present);
+
+ /*
+ * If the init complete flag is not present then wait 500ms that together with 500ms
+ * after reset (in the adapter code) should be enough to read data from upper pages
+ * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been
+ * prone to this when trying to read sensor options using getQsfpOptionsFromData()
+ * Probably because access to the paged address space is required.
+ */
+ if (!init_complete_flag_present) {
+ nt_os_wait_usec(500000);
+ return;
+ }
+
+ /* Otherwise wait for the init complete flag to be set */
+ int count = 0;
+
+ while (true) {
+ if (count > 10) { /* 1 s timeout */
+ NT_LOG(WRN, NTHW, "Timeout waiting for module ready");
+ break;
+ }
+
+ read_data_lin(ctx, 6, sizeof(data), &data);
+
+ if (data & 0x01) {
+ NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100);
+ break;
+ }
+
+ nt_os_wait_usec(100000);/* 100 ms */
+ count++;
+ }
+}
+
+static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx)
+{
+ const char *const nim_list[] = {
+ "AFBR-89BDDZ", /* Avago BiDi */
+ "AFBR-89BRDZ", /* Avago BiDi, RxOnly */
+ "FTLC4352RKPL", /* Finisar QSFP28-LR */
+ "FTLC4352RHPL", /* Finisar QSFP28-DR */
+ "FTLC4352RJPL", /* Finisar QSFP28-FR */
+ "SFBR-89BDDZ-CS4", /* Foxconn, QSFP28 100G/40G BiDi */
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) {
+ if (ctx->prod_no == nim_list[i]) {
+ ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
+ ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true;
+ NT_LOG(DBG, NTHW, "Found FEC info via PN list");
+ return;
+ }
+ }
+
+ /*
+ * For modules not in the list find FEC info via registers
+ * Read if the module has controllable FEC
+ * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing)
+ * (Page 03h, Bytes 224-229)
+ */
+ uint8_t data;
+ uint16_t addr = 227 + 3 * 128;
+ read_data_lin(ctx, addr, sizeof(data), &data);
+
+ /* Check if the module has FEC support that can be controlled */
+ ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0;
+ ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0;
+
+ if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)
+ ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
+
+ if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)
+ ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC);
+}
+
+static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
+{
+ int res = qsfpplus_preinit(ctx, lane_idx);
+
+ if (!res) {
+ qsfp28_wait_for_ready_after_reset(ctx);
+ memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0,
+ sizeof(ctx->specific_u.qsfp.specific_u.qsfp28));
+ ctx->specific_u.qsfp.qsfp28 = true;
+ qsfp28_find_port_params(ctx);
+ qsfp28_get_fec_options(ctx);
+ qsfp28_set_speed_mask(ctx);
+ }
+
+ return res;
+}
+
+static void qsfp_plus_nim_add_all_sensors(uint8_t m_port_no, nim_i2c_ctx_t *ctx,
+ struct nim_sensor_group **nim_sensors_ptr,
+ uint16_t *nim_sensors_cnt)
+{
+ const struct nim_sensors_ops *nim_sensors_ops = get_nim_sensors_ops();
+
+ if (nim_sensors_ops == NULL) {
+ NT_LOG(INF, ETHDEV, "Nim sensors module not included\n");
+ return;
+ }
+
+ struct qsfp_ops *qsfp_ops = get_qsfp_ops();
+
+ if (qsfp_ops == NULL) {
+ NT_LOG(INF, ETHDEV, "QSFP module not included\n");
+ return;
+ }
+
+ struct nt_adapter_sensor_description *qsfp_sensor_level0 =
+ nim_sensors_ops->get_qsfp_sensor_level0();
+
+ struct nt_adapter_sensor_description *qsfp_sensor_level1 =
+ nim_sensors_ops->get_qsfp_sensor_level1();
+
+ struct nim_sensor_group *sensor = NULL;
+
+ if (ctx == NULL || nim_sensors_ptr == NULL) {
+ NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__);
+ return;
+ }
+
+ /*
+ * If the user has not provided a name for the temperature sensor then apply
+ * one automatically
+ */
+ if (strlen(qsfp_sensor_level0[0].name) == 0) {
+ if (ctx->specific_u.qsfp.qsfp28)
+ snprintf(qsfp_sensor_level0[0].name, sizeof(qsfp_sensor_level0[0].name),
+ "%s", "QSFP28");
+
+ else
+ snprintf(qsfp_sensor_level0[0].name, sizeof(qsfp_sensor_level0[0].name),
+ "%s", "QSFP+");
+ }
+
+ /* temperature sensor */
+ nim_sensors_ptr[m_port_no] =
+ allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_PORT,
+ &qsfp_sensor_level0[0]);
+ sensor = nim_sensors_ptr[m_port_no];
+ sensor->read = qsfp_ops->nim_read_qsfp_temp;
+ (*nim_sensors_cnt)++;
+
+ /* voltage */
+ sensor->next = allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[0]);
+ sensor = sensor->next;
+ sensor->read = qsfp_ops->nim_read_qsfp_voltage;
+ (*nim_sensors_cnt)++;
+
+ /* bias current sensors */
+ sensor->next = allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[1]);
+ sensor = sensor->next;
+ sensor->read = qsfp_ops->nim_read_qsfp_bias_current;
+ (*nim_sensors_cnt)++;
+
+ for (uint8_t i = 2; i < 5; i++) {
+ sensor->next =
+ allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[i]);
+ sensor = sensor->next;
+ sensor->read = NULL;
+ (*nim_sensors_cnt)++;
+ }
+
+ /* tx power */
+ sensor->next = allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[5]);
+ sensor = sensor->next;
+ sensor->read = qsfp_ops->nim_read_qsfp_tx_power;
+ (*nim_sensors_cnt)++;
+
+ for (uint8_t i = 6; i < 9; i++) {
+ sensor->next =
+ allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[i]);
+ sensor = sensor->next;
+ sensor->read = NULL;
+ (*nim_sensors_cnt)++;
+ }
+
+ /* rx power */
+ sensor->next = allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[9]);
+ sensor = sensor->next;
+ sensor->read = qsfp_ops->nim_read_qsfp_rx_power;
+ (*nim_sensors_cnt)++;
+
+ for (uint8_t i = 10; i < 13; i++) {
+ sensor->next =
+ allocate_nim_sensor_group(m_port_no, ctx, NT_SENSOR_SOURCE_LEVEL1_PORT,
+ &qsfp_sensor_level1[i]);
+ sensor = sensor->next;
+ sensor->read = NULL;
+ (*nim_sensors_cnt)++;
+ }
+}
+
+struct nim_sensor_group *allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx,
+ enum nt_sensor_source_e ssrc,
+ struct nt_adapter_sensor_description *sd)
+{
+ const struct nim_sensors_ops *nim_sensor_ops = get_nim_sensors_ops();
+
+ if (nim_sensor_ops == NULL) {
+ NT_LOG(INF, ETHDEV, "Sensors module not included\n");
+ return NULL;
+ }
+
+ struct nim_sensor_group *sg = malloc(sizeof(struct nim_sensor_group));
+
+ if (sg == NULL) {
+ NT_LOG(ERR, ETHDEV, "%s: sensor group is NULL", __func__);
+ return NULL;
+ }
+
+ sg->sensor = nim_sensor_ops->allocate_sensor_by_description(port, ssrc, sd);
+ sg->ctx = ctx;
+ sg->next = NULL;
+ return sg;
+}
+
+int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port,
+ struct nim_sensor_group **nim_sensors_ptr, uint16_t *nim_sensors_cnt)
+{
+ int res = i2c_nim_common_construct(ctx);
+
+ switch (translate_nimid(ctx)) {
+ case NT_NIM_SFP_SFP_PLUS:
+ break;
+
+ case NT_NIM_QSFP_PLUS:
+ qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
+ qsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr, nim_sensors_cnt);
+ break;
+
+ case NT_NIM_QSFP28:
+ qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
+ qsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr, nim_sensors_cnt);
+ break;
+
+ default:
+ res = 1;
+ NT_LOG(ERR, NTHW, "NIM type %s is not supported.\n", nim_id_to_text(ctx->nim_id));
+ }
+
+ return res;
+}
+
+static struct sfp_ops *sfp_ops;
+
+void register_sfp_ops(struct sfp_ops *ops)
+{
+ sfp_ops = ops;
+}
+
+struct sfp_ops *get_sfp_ops(void)
+{
+ return sfp_ops;
+}
+
+static struct qsfp_ops *qsfp_ops;
+
+void register_qsfp_ops(struct qsfp_ops *ops)
+{
+ qsfp_ops = ops;
+}
+
+struct qsfp_ops *get_qsfp_ops(void)
+{
+ return qsfp_ops;
+}
diff --git a/drivers/net/ntnic/nim/i2c_nim.h b/drivers/net/ntnic/nim/i2c_nim.h
new file mode 100644
index 0000000000..cbeb3852fd
--- /dev/null
+++ b/drivers/net/ntnic/nim/i2c_nim.h
@@ -0,0 +1,77 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef I2C_NIM_H_
+#define I2C_NIM_H_
+
+#include "nthw_drv.h"
+#include "nim_defines.h"
+#include "nt_link_speed.h"
+#include "ntnic_nim.h"
+
+#include "ntnic_sensor.h"
+
+typedef struct sfp_nim_state {
+ uint8_t br; /* bit rate, units of 100 MBits/sec */
+} sfp_nim_state_t, *sfp_nim_state_p;
+
+struct nim_sensor_group *allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx,
+ enum nt_sensor_source_e ssrc,
+ struct nt_adapter_sensor_description *sd);
+
+/*
+ * Utility functions
+ */
+
+nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx);
+
+/*
+ * Builds an nim state for the port implied by `ctx`, returns zero
+ * if successful, and non-zero otherwise. SFP and QSFP nims are supported
+ */
+int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state);
+
+/*
+ * Returns a type name such as "SFP/SFP+" for a given NIM type identifier,
+ * or the string "ILLEGAL!".
+ */
+const char *nim_id_to_text(uint8_t nim_id);
+
+int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_t *ctx, bool disable, int lane_idx);
+
+/*
+ * This function tries to classify NIM based on it's ID and some register reads
+ * and collects information into ctx structure. The @extra parameter could contain
+ * the initialization argument for specific type of NIMS.
+ */
+int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port,
+ struct nim_sensor_group **nim_sensors_ptr,
+ uint16_t *nim_sensors_cnt);
+
+int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data);
+
+struct sfp_ops {
+ void (*nim_read_sfp_temp)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_sfp_voltage)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_sfp_bias_current)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_sfp_tx_power)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_sfp_rx_power)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+};
+
+struct qsfp_ops {
+ void (*nim_read_qsfp_temp)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_qsfp_voltage)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_qsfp_bias_current)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_qsfp_tx_power)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+ void (*nim_read_qsfp_rx_power)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);
+};
+
+void register_qsfp_ops(struct qsfp_ops *ops);
+struct qsfp_ops *get_qsfp_ops(void);
+
+void register_sfp_ops(struct sfp_ops *ops);
+struct sfp_ops *get_sfp_ops(void);
+
+#endif /* I2C_NIM_H_ */
diff --git a/drivers/net/ntnic/nim/include/qsfp_registers.h b/drivers/net/ntnic/nim/include/qsfp_registers.h
new file mode 100644
index 0000000000..d935b0eb07
--- /dev/null
+++ b/drivers/net/ntnic/nim/include/qsfp_registers.h
@@ -0,0 +1,58 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _QSFP_REGISTERS_H
+#define _QSFP_REGISTERS_H
+
+/*
+ * QSFP Registers
+ */
+#define QSFP_INT_STATUS_RX_LOS_ADDR 3
+#define QSFP_TEMP_LIN_ADDR 22
+#define QSFP_VOLT_LIN_ADDR 26
+#define QSFP_RX_PWR_LIN_ADDR 34 /* uint16_t [0..3] */
+#define QSFP_TX_BIAS_LIN_ADDR 42/* uint16_t [0..3] */
+#define QSFP_TX_PWR_LIN_ADDR 50 /* uint16_t [0..3] */
+
+#define QSFP_CONTROL_STATUS_LIN_ADDR 86
+#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F
+
+#define QSFP_EXTENDED_IDENTIFIER 129
+#define QSFP_POWER_CLASS_BITS_1_4 0xC0
+#define QSFP_POWER_CLASS_BITS_5_7 0x03
+
+#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */
+#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */
+#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */
+#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */
+#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */
+#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */
+#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */
+
+#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */
+#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */
+
+#define QSFP_OPTION3_LIN_ADDR 195
+#define QSFP_OPTION3_TX_DISABLE_BIT (1 << 4)
+
+#define QSFP_DMI_OPTION_LIN_ADDR 220
+#define QSFP_DMI_AVG_PWR_BIT (1 << 3)
+
+#define QSFP_TEMP_THRESH_LIN_ADDR (128 + (3 * 128)) /* Page 3 */
+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */
+
+#define QSFP_VOLT_THRESH_LIN_ADDR (144 + (3 * 128)) /* Page 3 */
+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */
+
+#define QSFP_RX_PWR_THRESH_LIN_ADDR (176 + (3 * 128)) /* Page 3 */
+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */
+
+#define QSFP_BIAS_THRESH_LIN_ADDR (184 + (3 * 128)) /* Page 3 */
+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */
+
+#define QSFP_TX_PWR_THRESH_LIN_ADDR (192 + (3 * 128)) /* Page 3 */
+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */
+
+#endif /* _QSFP_REGISTERS_H */
diff --git a/drivers/net/ntnic/nim/nim_defines.h b/drivers/net/ntnic/nim/nim_defines.h
new file mode 100644
index 0000000000..4abe115dc9
--- /dev/null
+++ b/drivers/net/ntnic/nim/nim_defines.h
@@ -0,0 +1,69 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NIM_DEFINES_H_
+#define NIM_DEFINES_H_
+
+#define NIM_IDENTIFIER_ADDR 0 /* 1 byte */
+
+#define SFP_BIT_RATE_ADDR 12 /* 1 byte */
+#define SFP_VENDOR_NAME_ADDR 20 /* 16bytes */
+#define SFP_VENDOR_PN_ADDR 40 /* 16bytes */
+#define SFP_VENDOR_REV_ADDR 56 /* 4bytes */
+#define SFP_VENDOR_SN_ADDR 68 /* 16bytes */
+#define SFP_VENDOR_DATE_ADDR 84 /* 8bytes */
+
+#define SFP_CONTROL_STATUS_LIN_ADDR (110U + 256U) /* 0xA2 */
+#define SFP_SOFT_TX_DISABLE_BIT (1U << 6)
+
+#define QSFP_EXTENDED_IDENTIFIER 129
+#define QSFP_SUP_LEN_INFO_ADDR 142 /* 5bytes */
+#define QSFP_TRANSMITTER_TYPE_ADDR 147 /* 1byte */
+#define QSFP_VENDOR_NAME_ADDR 148 /* 16bytes */
+#define QSFP_VENDOR_PN_ADDR 168 /* 16bytes */
+#define QSFP_VENDOR_REV_ADDR 184/* 2bytes */
+#define QSFP_VENDOR_SN_ADDR 196 /* 16bytes */
+#define QSFP_VENDOR_DATE_ADDR 212 /* 8bytes */
+
+/* I2C addresses */
+#define NIM_I2C_0XA0 0xA0 /* Basic I2C address */
+#define NIM_I2C_0XA2 0xA2 /* Diagnostic monitoring */
+#define NIM_I2C_0XAC 0xAC /* Address of integrated PHY */
+
+typedef enum {
+ NIM_OPTION_TEMP = 0,
+ NIM_OPTION_SUPPLY,
+ NIM_OPTION_RX_POWER,
+ NIM_OPTION_TX_BIAS,
+ NIM_OPTION_TX_POWER,
+ NIM_OPTION_TX_DISABLE,
+ /* Indicates that the module should be checked for the two next FEC types */
+ NIM_OPTION_FEC,
+ NIM_OPTION_MEDIA_SIDE_FEC,
+ NIM_OPTION_HOST_SIDE_FEC,
+ NIM_OPTION_RX_ONLY
+} nim_option_t;
+
+enum nt_nim_identifier_e {
+ NT_NIM_UNKNOWN = 0x00, /* Nim type is unknown */
+ NT_NIM_GBIC = 0x01, /* Nim type = GBIC */
+ NT_NIM_FIXED = 0x02, /* Nim type = FIXED */
+ NT_NIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */
+ NT_NIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */
+ NT_NIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */
+ NT_NIM_XFP = 0x06, /* Nim type = XFP */
+ NT_NIM_XFF = 0x07, /* Nim type = XFF */
+ NT_NIM_XFP_E = 0x08, /* Nim type = XFP-E */
+ NT_NIM_XPAK = 0x09, /* Nim type = XPAK */
+ NT_NIM_X2 = 0x0A, /* Nim type = X2 */
+ NT_NIM_DWDM = 0x0B, /* Nim type = DWDM */
+ NT_NIM_QSFP = 0x0C, /* Nim type = QSFP */
+ NT_NIM_QSFP_PLUS = 0x0D,/* Nim type = QSFP+ */
+ NT_NIM_QSFP28 = 0x11, /* Nim type = QSFP28 */
+ NT_NIM_CFP4 = 0x12, /* Nim type = CFP4 */
+};
+typedef enum nt_nim_identifier_e nt_nim_identifier_t;
+
+#endif /* NIM_DEFINES_H_ */
diff --git a/drivers/net/ntnic/nim/nt_link_speed.c b/drivers/net/ntnic/nim/nt_link_speed.c
new file mode 100644
index 0000000000..3cc5f50e49
--- /dev/null
+++ b/drivers/net/ntnic/nim/nt_link_speed.c
@@ -0,0 +1,115 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nt_link_speed.h"
+#include "nt4ga_link.h"
+
+const char *nt_translate_link_speed(nt_link_speed_t link_speed)
+{
+ switch (link_speed) {
+ case NT_LINK_SPEED_UNKNOWN:
+ return "NotAvail";
+
+ case NT_LINK_SPEED_10M:
+ return "10M";
+
+ case NT_LINK_SPEED_100M:
+ return "100M";
+
+ case NT_LINK_SPEED_1G:
+ return "1G";
+
+ case NT_LINK_SPEED_10G:
+ return "10G";
+
+ case NT_LINK_SPEED_25G:
+ return "25G";
+
+ case NT_LINK_SPEED_40G:
+ return "40G";
+
+ case NT_LINK_SPEED_50G:
+ return "50G";
+
+ case NT_LINK_SPEED_100G:
+ return "100G";
+
+ default:
+ /* DEBUG assert: remind developer that a switch/case entry is needed here.... */
+ assert(false);
+ return "Unhandled";
+ }
+}
+
+uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed)
+{
+ uint64_t n_link_speed = 0ULL;
+
+ switch (e_link_speed) {
+ case NT_LINK_SPEED_UNKNOWN:
+ n_link_speed = 0UL;
+ break;
+
+ case NT_LINK_SPEED_10M:
+ n_link_speed = (10ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_100M:
+ n_link_speed = (100ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_1G:
+ n_link_speed = (1ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_10G:
+ n_link_speed = (10ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_25G:
+ n_link_speed = (25ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_40G:
+ n_link_speed = (40ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_50G:
+ n_link_speed = (50ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ case NT_LINK_SPEED_100G:
+ n_link_speed = (100ULL * 1000ULL * 1000ULL * 1000ULL);
+ break;
+
+ default:
+ /* DEBUG assert: remind developer that a switch/case entry is needed here.... */
+ assert(false);
+ n_link_speed = 0UL;
+ break;
+ }
+
+ return n_link_speed;
+}
+
+uint64_t nt_get_max_link_speed(uint32_t link_speed_mask)
+{
+ uint64_t n_link_speed = 0UL;
+
+ for (int i = 0; i < 32; i++) {
+ if ((1U << i) & link_speed_mask) {
+ uint64_t link_speed = nt_get_link_speed(1 << i);
+
+ if (link_speed > n_link_speed)
+ n_link_speed = link_speed;
+ }
+ }
+
+ return n_link_speed;
+}
diff --git a/drivers/net/ntnic/nim/nt_link_speed.h b/drivers/net/ntnic/nim/nt_link_speed.h
new file mode 100644
index 0000000000..6e1b682fe8
--- /dev/null
+++ b/drivers/net/ntnic/nim/nt_link_speed.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NT_LINK_SPEED_H_
+#define NT_LINK_SPEED_H_
+
+#include <stdint.h>
+#include "nt4ga_link.h"
+
+const char *nt_translate_link_speed(nt_link_speed_t link_speed);
+uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed);
+uint64_t nt_get_max_link_speed(uint32_t link_speed_mask);
+
+#endif /* NT_LINK_SPEED_H_ */
--
2.44.0
next prev parent reply other threads:[~2024-05-30 14:52 UTC|newest]
Thread overview: 101+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-30 14:48 [PATCH v1 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-05-30 14:48 ` [PATCH v1 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 07/17] net/ntnic: add API " Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 13/17] net/ntnic: add adapter initialization Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-05-30 14:49 ` Serhii Iliushyk [this message]
2024-05-31 15:47 ` [PATCH v2 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 07/17] net/ntnic: add API " Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 13/17] net/ntnic: add adapter initialization Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 17/17] net/ntnic: add NIM module Serhii Iliushyk
2024-06-03 16:17 ` [PATCH v3 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-06-03 16:17 ` [PATCH v3 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 07/17] net/ntnic: add API " Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 13/17] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-03 16:18 ` [PATCH v3 17/17] net/ntnic: add NIM module Serhii Iliushyk
2024-06-04 10:29 ` [PATCH v3 01/17] net/ntnic: Add registers for NapaTech SmartNiC Mykola Kostenok
2024-06-07 13:03 ` Serhii Iliushyk
2024-06-12 8:50 ` Ferruh Yigit
2024-06-12 8:55 ` Ferruh Yigit
2024-06-26 19:55 ` [PATCH v4 01/23] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 02/23] net/ntnic: add logging implementation Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 03/23] net/ntnic: add minimal initialization for PCI device Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 04/23] net/ntnic: add NT utilities implementation Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 05/23] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 06/23] net/ntnic: add NT NIC driver dependencies Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 07/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 08/23] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 09/23] net/ntnic: add registers and FPGA model for NapaTech NIC Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 10/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 11/23] net/ntnic: add FPGA initialization functionality Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 12/23] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 13/23] net/ntnic: add reset module for " Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 14/23] net/ntnic: add clock profiles " Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 15/23] net/ntnic: add MAC and packet features Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 16/23] net/ntnic: add link management module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 17/23] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 18/23] net/ntnic: add NIM module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 19/23] net/ntnic: add QSFP support Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 20/23] net/ntnic: add QSFP28 support Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 21/23] net/ntnic: add GPIO PHY module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 22/23] net/ntnic: add MAC PCS register interface module Serhii Iliushyk
2024-06-26 19:55 ` [PATCH v4 23/23] net/ntnic: add GMF (Generic MAC Feeder) module Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 01/23] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 02/23] net/ntnic: add logging implementation Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 03/23] net/ntnic: add minimal initialization for PCI device Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 04/23] net/ntnic: add NT utilities implementation Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 05/23] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 06/23] net/ntnic: add NT NIC driver dependencies Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 07/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 08/23] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 09/23] net/ntnic: add registers and FPGA model for NapaTech NIC Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 10/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 11/23] net/ntnic: add FPGA initialization functionality Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 12/23] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 13/23] net/ntnic: add reset module for " Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 14/23] net/ntnic: add clock profiles " Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 15/23] net/ntnic: add MAC and packet features Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 16/23] net/ntnic: add link management module Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 17/23] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-27 7:38 ` [PATCH v5 18/23] net/ntnic: add NIM module Serhii Iliushyk
2024-06-27 7:39 ` [PATCH v5 19/23] net/ntnic: add QSFP support Serhii Iliushyk
2024-06-27 7:39 ` [PATCH v5 20/23] net/ntnic: add QSFP28 support Serhii Iliushyk
2024-06-27 7:39 ` [PATCH v5 21/23] net/ntnic: add GPIO PHY module Serhii Iliushyk
2024-06-27 7:39 ` [PATCH v5 22/23] net/ntnic: add MAC PCS register interface module Serhii Iliushyk
2024-06-27 7:39 ` [PATCH v5 23/23] net/ntnic: add GMF (Generic MAC Feeder) module Serhii Iliushyk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240530144929.4127931-17-sil-plv@napatech.com \
--to=sil-plv@napatech.com \
--cc=andrew.rybchenko@oktetlabs.ru \
--cc=ckm@napatech.com \
--cc=dev@dpdk.org \
--cc=ferruh.yigit@amd.com \
--cc=mko-plv@napatech.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).