@@ -9,6 +9,32 @@
#include "nthw_fpga.h"
#include "ntnic_mod_reg.h"
+/*
+ * Global variables shared by NT adapter types
+ */
+rte_thread_t monitor_tasks[NUM_ADAPTER_MAX];
+volatile int monitor_task_is_running[NUM_ADAPTER_MAX];
+
+/*
+ * Signal-handler to stop all monitor threads
+ */
+static void stop_monitor_tasks(int signum)
+{
+ const size_t N = ARRAY_SIZE(monitor_task_is_running);
+ size_t i;
+
+ /* Stop all monitor tasks */
+ for (i = 0; i < N; i++) {
+ const int is_running = monitor_task_is_running[i];
+ monitor_task_is_running[i] = 0;
+
+ if (signum == -1 && is_running != 0) {
+ rte_thread_join(monitor_tasks[i], NULL);
+ memset(&monitor_tasks[i], 0, sizeof(monitor_tasks[0]));
+ }
+ }
+}
+
static int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *pfh)
{
const char *const p_dev_name = p_adapter_info->p_dev_name;
@@ -35,6 +61,9 @@ static int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *
p_fpga_info->n_fpga_ver_id, p_fpga_info->n_fpga_rev_id, p_fpga_info->n_fpga_ident,
p_fpga_info->n_fpga_build_time);
fprintf(pfh, "%s: FpgaDebugMode=0x%x\n", p_adapter_id_str, p_fpga_info->n_fpga_debug_mode);
+ fprintf(pfh, "%s: Nims=%d PhyPorts=%d PhyQuads=%d RxPorts=%d TxPorts=%d\n",
+ p_adapter_id_str, p_fpga_info->n_nims, p_fpga_info->n_phy_ports,
+ p_fpga_info->n_phy_quads, p_fpga_info->n_rx_ports, p_fpga_info->n_tx_ports);
fprintf(pfh, "%s: Hw=0x%02X_rev%d: %s\n", p_adapter_id_str, p_hw_info->hw_platform_id,
p_fpga_info->nthw_hw_info.hw_id, p_fpga_info->nthw_hw_info.hw_plat_id_str);
fprintf(pfh, "%s: MCU Details:\n", p_adapter_id_str);
@@ -56,6 +85,7 @@ static int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info)
* (nthw_fpga_init())
*/
int n_phy_ports = -1;
+ int n_nim_ports = -1;
int res = -1;
nthw_fpga_t *p_fpga = NULL;
@@ -122,6 +152,8 @@ static int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info)
assert(p_fpga);
n_phy_ports = fpga_info->n_phy_ports;
assert(n_phy_ports >= 1);
+ n_nim_ports = fpga_info->n_nims;
+ assert(n_nim_ports >= 1);
{
int i;
@@ -171,6 +203,8 @@ static int nt4ga_adapter_deinit(struct adapter_info_s *p_adapter_info)
int i;
int res = -1;
+ stop_monitor_tasks(-1);
+
nthw_fpga_shutdown(&p_adapter_info->fpga_info);
/* Rac rab reset flip flop */
@@ -39,5 +39,8 @@ typedef struct adapter_info_s {
int n_tx_host_buffers;
} adapter_info_t;
+extern rte_thread_t monitor_tasks[NUM_ADAPTER_MAX];
+extern volatile int monitor_task_is_running[NUM_ADAPTER_MAX];
+
#endif /* _NT4GA_ADAPTER_H_ */
@@ -7,6 +7,7 @@
#define NT4GA_LINK_H_
#include "ntos_drv.h"
+#include "ntnic_nim.h"
enum nt_link_state_e {
NT_LINK_STATE_UNKNOWN = 0, /* The link state has not been read yet */
@@ -41,6 +42,8 @@ enum nt_link_auto_neg_e {
typedef struct link_state_s {
bool link_disabled;
+ bool nim_present;
+ bool lh_nim_absent;
bool link_up;
enum nt_link_state_e link_state;
enum nt_link_state_e link_state_latched;
@@ -73,12 +76,22 @@ typedef struct port_action_s {
uint32_t port_lpbk_mode;
} port_action_t;
+typedef struct adapter_100g_s {
+ nim_i2c_ctx_t nim_ctx[NUM_ADAPTER_PORTS_MAX]; /* Should be the first field */
+} adapter_100g_t;
+
+typedef union adapter_var_s {
+ nim_i2c_ctx_t nim_ctx[NUM_ADAPTER_PORTS_MAX]; /* First field in all the adapters type */
+ adapter_100g_t var100g;
+} adapter_var_u;
+
typedef struct nt4ga_link_s {
link_state_t link_state[NUM_ADAPTER_PORTS_MAX];
link_info_t link_info[NUM_ADAPTER_PORTS_MAX];
port_action_t port_action[NUM_ADAPTER_PORTS_MAX];
uint32_t speed_capa;
bool variables_initialized;
+ adapter_var_u u;
} nt4ga_link_t;
#endif /* NT4GA_LINK_H_ */
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTNIC_NIM_H_
+#define _NTNIC_NIM_H_
+
+#include <stdint.h>
+
+typedef enum i2c_type {
+ I2C_HWIIC,
+} i2c_type_e;
+
+enum nt_port_type_e {
+ NT_PORT_TYPE_NOT_AVAILABLE = 0, /* The NIM/port type is not available (unknown) */
+ NT_PORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */
+};
+
+typedef enum nt_port_type_e nt_port_type_t, *nt_port_type_p;
+
+typedef struct nim_i2c_ctx {
+ union {
+ nthw_iic_t hwiic; /* depends on *Fpga_t, instance number, and cycle time */
+ struct {
+ nthw_i2cm_t *p_nt_i2cm;
+ int mux_channel;
+ } hwagx;
+ };
+ i2c_type_e type;/* 0 = hwiic (xilinx) - 1 = hwagx (agilex) */
+ uint8_t instance;
+ uint8_t devaddr;
+ uint8_t regaddr;
+ uint8_t nim_id;
+ nt_port_type_t port_type;
+
+ char vendor_name[17];
+ char prod_no[17];
+ char serial_no[17];
+ char date[9];
+ char rev[5];
+ bool avg_pwr;
+ bool content_valid;
+ uint8_t pwr_level_req;
+ uint8_t pwr_level_cur;
+ uint16_t len_info[5];
+ uint32_t speed_mask; /* Speeds supported by the NIM */
+ int8_t lane_idx;
+ uint8_t lane_count;
+ uint32_t options;
+ bool tx_disable;
+ bool dmi_supp;
+
+} nim_i2c_ctx_t, *nim_i2c_ctx_p;
+
+struct nim_sensor_group {
+ struct nim_i2c_ctx *ctx;
+ struct nim_sensor_group *next;
+};
+
+#endif /* _NTNIC_NIM_H_ */
@@ -7,16 +7,202 @@
#include "nt_util.h"
#include "ntlog.h"
+#include "i2c_nim.h"
#include "ntnic_mod_reg.h"
+/*
+ * Initialize NIM, Code based on nt200e3_2_ptp.cpp: MyPort::createNim()
+ */
+static int _create_nim(adapter_info_t *drv, int port)
+{
+ int res = 0;
+ const uint8_t valid_nim_id = 17U;
+ nim_i2c_ctx_t *nim_ctx;
+ nt4ga_link_t *link_info = &drv->nt4ga_link;
+
+ assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
+ assert(link_info->variables_initialized);
+
+ nim_ctx = &link_info->u.var100g.nim_ctx[port];
+
+ /*
+ * Wait a little after a module has been inserted before trying to access I2C
+ * data, otherwise the module will not respond correctly.
+ */
+ nt_os_wait_usec(1000000); /* pause 1.0s */
+
+ res = construct_and_preinit_nim(nim_ctx);
+
+ if (res)
+ return res;
+
+ /*
+ * Does the driver support the NIM module type?
+ */
+ if (nim_ctx->nim_id != valid_nim_id) {
+ NT_LOG(ERR, NTHW, "%s: The driver does not support the NIM module type %s\n",
+ drv->mp_port_id_str[port], nim_id_to_text(nim_ctx->nim_id));
+ NT_LOG(DBG, NTHW, "%s: The driver supports the NIM module type %s\n",
+ drv->mp_port_id_str[port], nim_id_to_text(valid_nim_id));
+ return -1;
+ }
+
+ return res;
+}
+
+/*
+ * Initialize one 100 Gbps port.
+ * The function shall not assume anything about the state of the adapter
+ * and/or port.
+ */
+static int _port_init(adapter_info_t *drv, int port)
+{
+ int res;
+ nt4ga_link_t *link_info = &drv->nt4ga_link;
+
+ assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
+ assert(link_info->variables_initialized);
+
+ /*
+ * Phase 1. Pre-state machine (`port init` functions)
+ * 1.1) nt4ga_adapter::port_init()
+ */
+
+ /* No adapter set-up here, only state variables */
+
+ /* 1.2) MyPort::init() */
+ link_info->link_info[port].link_speed = NT_LINK_SPEED_100G;
+ link_info->link_info[port].link_duplex = NT_LINK_DUPLEX_FULL;
+ link_info->link_info[port].link_auto_neg = NT_LINK_AUTONEG_OFF;
+ link_info->speed_capa |= NT_LINK_SPEED_100G;
+
+ /* Phase 3. Link state machine steps */
+
+ /* 3.1) Create NIM, ::createNim() */
+ res = _create_nim(drv, port);
+
+ if (res) {
+ NT_LOG(WRN, NTNIC, "%s: NIM initialization failed\n", drv->mp_port_id_str[port]);
+ return res;
+ }
+
+ NT_LOG(DBG, NTNIC, "%s: NIM initialized\n", drv->mp_port_id_str[port]);
+
+ return res;
+}
+
+/*
+ * State machine shared between kernel and userland
+ */
+static int _common_ptp_nim_state_machine(void *data)
+{
+ adapter_info_t *drv = (adapter_info_t *)data;
+ fpga_info_t *fpga_info = &drv->fpga_info;
+ nt4ga_link_t *link_info = &drv->nt4ga_link;
+ nthw_fpga_t *fpga = fpga_info->mp_fpga;
+ const int adapter_no = drv->adapter_no;
+ const int nb_ports = fpga_info->n_phy_ports;
+ uint32_t last_lpbk_mode[NUM_ADAPTER_PORTS_MAX];
+
+ link_state_t *link_state;
+
+ if (!fpga) {
+ NT_LOG(ERR, NTNIC, "%s: fpga is NULL\n", drv->mp_adapter_id_str);
+ goto NT4GA_LINK_100G_MON_EXIT;
+ }
+
+ assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);
+ link_state = link_info->link_state;
+
+ monitor_task_is_running[adapter_no] = 1;
+ memset(last_lpbk_mode, 0, sizeof(last_lpbk_mode));
+
+ if (monitor_task_is_running[adapter_no])
+ NT_LOG(DBG, NTNIC, "%s: link state machine running...\n", drv->mp_adapter_id_str);
+
+ while (monitor_task_is_running[adapter_no]) {
+ int i;
+
+ for (i = 0; i < nb_ports; i++) {
+ const bool is_port_disabled = link_info->port_action[i].port_disable;
+ const bool was_port_disabled = link_state[i].link_disabled;
+ const bool disable_port = is_port_disabled && !was_port_disabled;
+ const bool enable_port = !is_port_disabled && was_port_disabled;
+
+ if (!monitor_task_is_running[adapter_no]) /* stop quickly */
+ break;
+
+ /* Has the administrative port state changed? */
+ assert(!(disable_port && enable_port));
+
+ if (disable_port) {
+ memset(&link_state[i], 0, sizeof(link_state[i]));
+ link_info->link_info[i].link_speed = NT_LINK_SPEED_UNKNOWN;
+ link_state[i].link_disabled = true;
+ /* Turn off laser and LED, etc. */
+ (void)_create_nim(drv, i);
+ NT_LOG(DBG, NTNIC, "%s: Port %i is disabled\n",
+ drv->mp_port_id_str[i], i);
+ continue;
+ }
+
+ if (enable_port) {
+ link_state[i].link_disabled = false;
+ NT_LOG(DBG, NTNIC, "%s: Port %i is enabled\n",
+ drv->mp_port_id_str[i], i);
+ }
+
+ if (is_port_disabled)
+ continue;
+
+ if (link_info->port_action[i].port_lpbk_mode != last_lpbk_mode[i]) {
+ /* Loopback mode has changed. Do something */
+ _port_init(drv, i);
+
+ NT_LOG(INF, NTNIC, "%s: Loopback mode changed=%u\n",
+ drv->mp_port_id_str[i],
+ link_info->port_action[i].port_lpbk_mode);
+
+ if (link_info->port_action[i].port_lpbk_mode == 1)
+ link_state[i].link_up = true;
+
+ last_lpbk_mode[i] = link_info->port_action[i].port_lpbk_mode;
+ continue;
+ }
+
+ } /* end-for */
+
+ if (monitor_task_is_running[adapter_no])
+ nt_os_wait_usec(5 * 100000U); /* 5 x 0.1s = 0.5s */
+ }
+
+NT4GA_LINK_100G_MON_EXIT:
+
+ NT_LOG(DBG, NTNIC, "%s: Stopped NT4GA 100 Gbps link monitoring thread.\n",
+ drv->mp_adapter_id_str);
+
+ return 0;
+}
+
+/*
+ * Userland NIM state machine
+ */
+static uint32_t nt4ga_link_100g_mon(void *data)
+{
+ (void)_common_ptp_nim_state_machine(data);
+
+ return 0;
+}
+
/*
* Initialize all ports
* The driver calls this function during initialization (of the driver).
*/
static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nthw_fpga_t *fpga)
{
- (void)fpga;
+ fpga_info_t *fpga_info = &p_adapter_info->fpga_info;
const int adapter_no = p_adapter_info->adapter_no;
+ const int nb_ports = fpga_info->n_phy_ports;
int res = 0;
NT_LOG(DBG, NTNIC, "%s: Initializing ports\n", p_adapter_info->mp_adapter_id_str);
@@ -27,12 +213,38 @@ static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nth
assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);
if (res == 0 && !p_adapter_info->nt4ga_link.variables_initialized) {
+ nim_i2c_ctx_t *nim_ctx = p_adapter_info->nt4ga_link.u.var100g.nim_ctx;
+ int i;
+
+ for (i = 0; i < nb_ports; i++) {
+ /* 2 + adapter port number */
+ const uint8_t instance = (uint8_t)(2U + i);
+
+ res = nthw_iic_init(&nim_ctx[i].hwiic, fpga, instance, 8);
+
+ if (res != 0)
+ break;
+
+ nim_ctx[i].instance = instance;
+ nim_ctx[i].devaddr = 0x50; /* 0xA0 / 2 */
+ nim_ctx[i].regaddr = 0U;
+ nim_ctx[i].type = I2C_HWIIC;
+ }
+
if (res == 0) {
p_adapter_info->nt4ga_link.speed_capa = NT_LINK_SPEED_100G;
p_adapter_info->nt4ga_link.variables_initialized = true;
}
}
+ /* Create state-machine thread */
+ if (res == 0) {
+ if (!monitor_task_is_running[adapter_no]) {
+ res = rte_thread_create(&monitor_tasks[adapter_no], NULL,
+ nt4ga_link_100g_mon, p_adapter_info);
+ }
+ }
+
return res;
}
@@ -12,6 +12,7 @@
#include "ntnic_mod_reg.h"
#include "nt4ga_link.h"
+#include "i2c_nim.h"
/*
* port: speed capabilities
@@ -26,6 +27,16 @@ static uint32_t nt4ga_port_get_link_speed_capabilities(struct adapter_info_s *p,
return nt_link_speed_capa;
}
+/*
+ * port: nim present
+ */
+static bool nt4ga_port_get_nim_present(struct adapter_info_s *p, int port)
+{
+ nt4ga_link_t *const p_link = &p->nt4ga_link;
+ const bool nim_present = p_link->link_state[port].nim_present;
+ return nim_present;
+}
+
/*
* port: link mode
*/
@@ -131,6 +142,8 @@ static uint32_t nt4ga_port_get_loopback_mode(struct adapter_info_s *p, int port)
static const struct port_ops ops = {
+ .get_nim_present = nt4ga_port_get_nim_present,
+
/*
* port:s link mode
*/
@@ -17,6 +17,7 @@ includes = [
include_directories('nthw'),
include_directories('nthw/supported'),
include_directories('nthw/model'),
+ include_directories('nim/'),
]
# all sources
@@ -24,6 +25,7 @@ sources = files(
'adapter/nt4ga_adapter.c',
'link_mgmt/link_100g/nt4ga_link_100g.c',
'link_mgmt/nt4ga_link.c',
+ 'nim/i2c_nim.c',
'nthw/supported/nthw_fpga_9563_055_039_0000.c',
'nthw/supported/nthw_fpga_instances.c',
'nthw/supported/nthw_fpga_mod_str_map.c',
@@ -33,6 +35,7 @@ sources = files(
'nthw/core/nt200a0x/reset/nthw_fpga_rst_nt200a0x.c',
'nthw/core/nthw_fpga.c',
'nthw/core/nthw_hif.c',
+ 'nthw/core/nthw_i2cm.c',
'nthw/core/nthw_iic.c',
'nthw/core/nthw_pcie3.c',
'nthw/core/nthw_sdc.c',
new file mode 100644
@@ -0,0 +1,224 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <string.h>
+
+#include "nthw_drv.h"
+#include "i2c_nim.h"
+#include "ntlog.h"
+#include "nt_util.h"
+#include "ntnic_mod_reg.h"
+#include "nim_defines.h"
+
+#define NIM_READ false
+#define NIM_WRITE true
+#define NIM_PAGE_SEL_REGISTER 127
+#define NIM_I2C_0XA0 0xA0 /* Basic I2C address */
+
+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, NTNIC,
+ "Cannot set up page for linear address %u\n", 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, NTNIC, " Call to nim_read_write_i2c_data failed\n");
+ return -1;
+ }
+
+ p_data += seq_cnt;
+ i = (uint16_t)(i + seq_cnt);
+ lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));
+ }
+
+ return 0;
+}
+
+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;
+}
+
+const char *nim_id_to_text(uint8_t nim_id)
+{
+ switch (nim_id) {
+ case 0x0:
+ return "UNKNOWN";
+
+ default:
+ return "ILLEGAL!";
+ }
+}
+
+int construct_and_preinit_nim(nim_i2c_ctx_p ctx)
+{
+ int res = i2c_nim_common_construct(ctx);
+
+ return res;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef I2C_NIM_H_
+#define I2C_NIM_H_
+
+#include "ntnic_nim.h"
+
+/*
+ * 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);
+
+/*
+ * 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);
+
+#endif /* I2C_NIM_H_ */
new file mode 100644
@@ -0,0 +1,29 @@
+/*
+ * 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 */
+
+/* I2C addresses */
+#define NIM_I2C_0XA0 0xA0 /* Basic I2C address */
+#define NIM_I2C_0XA2 0xA2 /* Diagnostic monitoring */
+
+typedef enum {
+ 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 */
+};
+typedef enum nt_nim_identifier_e nt_nim_identifier_t;
+
+#endif /* NIM_DEFINES_H_ */
@@ -15,6 +15,7 @@
#include "nthw_hif.h"
#include "nthw_pcie3.h"
#include "nthw_iic.h"
+#include "nthw_i2cm.h"
#include "nthw_sdc.h"
new file mode 100644
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_II2CM_H__
+#define __NTHW_II2CM_H__
+
+#include "nthw_fpga_model.h"
+#include "pthread.h"
+
+struct nt_i2cm {
+ nthw_fpga_t *mp_fpga;
+
+ nthw_module_t *m_mod_i2cm;
+
+ int mn_i2c_instance;
+
+ nthw_register_t *mp_reg_prer_low;
+ nthw_field_t *mp_fld_prer_low_prer_low;
+
+ nthw_register_t *mp_reg_prer_high;
+ nthw_field_t *mp_fld_prer_high_prer_high;
+
+ nthw_register_t *mp_reg_ctrl;
+ nthw_field_t *mp_fld_ctrl_ien;
+ nthw_field_t *mp_fld_ctrl_en;
+
+ nthw_register_t *mp_reg_data;
+ nthw_field_t *mp_fld_data_data;
+
+ nthw_register_t *mp_reg_cmd_status;
+ nthw_field_t *mp_fld_cmd_status_cmd_status;
+
+ nthw_register_t *mp_reg_select;
+ nthw_field_t *mp_fld_select_select;
+
+ nthw_register_t *mp_reg_io_exp;
+ nthw_field_t *mp_fld_io_exp_rst;
+ nthw_field_t *mp_fld_io_exp_int_b;
+
+ pthread_mutex_t i2cmmutex;
+};
+
+typedef struct nt_i2cm nthw_i2cm_t;
+
+int nthw_i2cm_read(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t *value);
+int nthw_i2cm_write(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t value);
+
+#endif /* __NTHW_II2CM_H__ */
@@ -19,12 +19,14 @@ int nthw_fpga_get_param_info(struct fpga_info_s *p_fpga_info, nthw_fpga_t *p_fpg
{
mcu_info_t *p_mcu_info = &p_fpga_info->mcu_info;
+ const int n_nims = nthw_fpga_get_product_param(p_fpga, NT_NIMS, -1);
const int n_phy_ports = nthw_fpga_get_product_param(p_fpga, NT_PHY_PORTS, -1);
const int n_phy_quads = nthw_fpga_get_product_param(p_fpga, NT_PHY_QUADS, -1);
const int n_rx_ports = nthw_fpga_get_product_param(p_fpga, NT_RX_PORTS, -1);
const int n_tx_ports = nthw_fpga_get_product_param(p_fpga, NT_TX_PORTS, -1);
const int n_vf_offset = nthw_fpga_get_product_param(p_fpga, NT_HIF_VF_OFFSET, 4);
+ p_fpga_info->n_nims = n_nims;
p_fpga_info->n_phy_ports = n_phy_ports;
p_fpga_info->n_phy_quads = n_phy_quads;
p_fpga_info->n_rx_ports = n_rx_ports;
@@ -236,6 +238,7 @@ int nthw_fpga_init(struct fpga_info_s *p_fpga_info)
nthw_fpga_get_param_info(p_fpga_info, p_fpga);
/* debug: report params */
+ NT_LOG(DBG, NTHW, "%s: NT_NIMS=%d\n", p_adapter_id_str, p_fpga_info->n_nims);
NT_LOG(DBG, NTHW, "%s: NT_PHY_PORTS=%d\n", p_adapter_id_str, p_fpga_info->n_phy_ports);
NT_LOG(DBG, NTHW, "%s: NT_PHY_QUADS=%d\n", p_adapter_id_str, p_fpga_info->n_phy_quads);
NT_LOG(DBG, NTHW, "%s: NT_RX_PORTS=%d\n", p_adapter_id_str, p_fpga_info->n_rx_ports);
new file mode 100644
@@ -0,0 +1,192 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "nt_util.h"
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "nthw_i2cm.h"
+
+#define NT_I2C_CMD_START 0x80
+#define NT_I2C_CMD_STOP 0x40
+#define NT_I2C_CMD_RD 0x20
+#define NT_I2C_CMD_WR 0x10
+#define NT_I2C_CMD_NACK 0x08
+#define NT_I2C_CMD_IRQ_ACK 0x01
+
+#define NT_I2C_STATUS_NACK 0x80
+#define NT_I2C_STATUS_TIP 0x02
+
+#define NT_I2C_TRANSMIT_WR 0x00
+#define NT_I2C_TRANSMIT_RD 0x01
+
+#define NUM_RETRIES 50U
+#define SLEEP_USECS 100U/* 0.1 ms */
+
+
+static bool nthw_i2cm_ready(nthw_i2cm_t *p, bool wait_for_ack)
+{
+ uint32_t flags = NT_I2C_STATUS_TIP | (wait_for_ack ? NT_I2C_STATUS_NACK : 0U);
+
+ for (uint32_t i = 0U; i < NUM_RETRIES; i++) {
+ uint32_t status = nthw_field_get_updated(p->mp_fld_cmd_status_cmd_status);
+ uint32_t ready = (status & flags) == 0U;
+ /* MUST have a short break to avoid time-outs, even if ready == true */
+ nt_os_wait_usec(SLEEP_USECS);
+
+ if (ready)
+ return true;
+ }
+
+ return false;
+}
+
+static int nthw_i2cm_write_internal(nthw_i2cm_t *p, uint8_t value)
+{
+ /* Write data to data register */
+ nthw_field_set_val_flush32(p->mp_fld_data_data, value);
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, true)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out writing data %u", __PRETTY_FUNCTION__, value);
+ return 1;
+ }
+
+ /* Generate stop condition and clear interrupt */
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, true)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", __PRETTY_FUNCTION__);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nthw_i2cm_write_reg_addr_internal(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr,
+ bool send_stop)
+{
+ /* Write device address to data register */
+ nthw_field_set_val_flush32(p->mp_fld_data_data,
+ (uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_WR));
+
+ /* #Set start condition along with secondary I2C dev_addr */
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, true)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out writing device address %u, reg_addr=%u",
+ __PRETTY_FUNCTION__, dev_addr, reg_addr);
+ return 1;
+ }
+
+ /* Writing I2C register address */
+ nthw_field_set_val_flush32(p->mp_fld_data_data, reg_addr);
+
+ if (send_stop) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK | NT_I2C_CMD_STOP);
+
+ } else {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+ }
+
+ if (!nthw_i2cm_ready(p, true)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out writing register address %u", __PRETTY_FUNCTION__,
+ reg_addr);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nthw_i2cm_read_internal(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t *value)
+{
+ /* Write I2C device address - with LSBit set to READ */
+
+ nthw_field_set_val_flush32(p->mp_fld_data_data,
+ (uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_RD));
+ /* #Send START condition along with secondary I2C dev_addr */
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, true)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out rewriting device address %u", __PRETTY_FUNCTION__,
+ dev_addr);
+ return 1;
+ }
+
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_RD | NT_I2C_CMD_NACK | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, false)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out during read operation", __PRETTY_FUNCTION__);
+ return 1;
+ }
+
+ *value = (uint8_t)nthw_field_get_updated(p->mp_fld_data_data);
+
+ /* Generate stop condition and clear interrupt */
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+
+ if (!nthw_i2cm_ready(p, false)) {
+ nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+ NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+ NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", __PRETTY_FUNCTION__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int nthw_i2cm_read(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t *value)
+{
+ int status;
+ status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, false);
+
+ if (status != 0)
+ return status;
+
+ status = nthw_i2cm_read_internal(p, dev_addr, value);
+
+ if (status != 0)
+ return status;
+
+ return 0;
+}
+
+int nthw_i2cm_write(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t value)
+{
+ int status;
+ status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, false);
+
+ if (status != 0)
+ return status;
+
+ status = nthw_i2cm_write_internal(p, value);
+
+ if (status != 0)
+ return status;
+
+ return 0;
+}
@@ -52,6 +52,7 @@ typedef struct fpga_info_s {
int n_fpga_debug_mode;
+ int n_nims;
int n_phy_ports;
int n_phy_quads;
int n_rx_ports;
@@ -22,6 +22,8 @@ const struct link_ops_s *get_100g_link_ops(void);
void link_100g_init(void);
struct port_ops {
+ bool (*get_nim_present)(struct adapter_info_s *p, int port);
+
/*
* port:s link mode
*/
@@ -60,6 +62,11 @@ struct port_ops {
uint32_t (*get_link_speed_capabilities)(struct adapter_info_s *p, int port);
+ /*
+ * port: nim capabilities
+ */
+ nim_i2c_ctx_t (*get_nim_capabilities)(struct adapter_info_s *p, int port);
+
/*
* port: tx power
*/