From: Danylo Vodopianov <dvo-...@napatech.com> Initialize NIM and 100G port for Agilex FPGA
Signed-off-by: Danylo Vodopianov <dvo-...@napatech.com> --- .../link_agx_100g/nt4ga_agx_link_100g.c | 143 ++++++++++++++++++ drivers/net/ntnic/meson.build | 1 + .../net/ntnic/nthw/core/include/nthw_gmf.h | 2 + .../ntnic/nthw/core/include/nthw_pcal6416a.h | 10 ++ drivers/net/ntnic/nthw/core/nthw_gmf.c | 41 +++++ drivers/net/ntnic/nthw/core/nthw_pcal6416a.c | 41 +++++ 6 files changed, 238 insertions(+) create mode 100644 drivers/net/ntnic/nthw/core/nthw_pcal6416a.c diff --git a/drivers/net/ntnic/link_mgmt/link_agx_100g/nt4ga_agx_link_100g.c b/drivers/net/ntnic/link_mgmt/link_agx_100g/nt4ga_agx_link_100g.c index 9551dfdfdc..6593c90ca5 100644 --- a/drivers/net/ntnic/link_mgmt/link_agx_100g/nt4ga_agx_link_100g.c +++ b/drivers/net/ntnic/link_mgmt/link_agx_100g/nt4ga_agx_link_100g.c @@ -197,6 +197,26 @@ static int phy_set_line_loopback(adapter_info_t *drv, int port, loopback_line_t return 0; } +/* + * Nim handling + */ + +static bool nim_is_present(nim_i2c_ctx_p ctx, uint8_t nim_idx) +{ + assert(nim_idx < NUM_ADAPTER_PORTS_MAX); + + nthw_pcal6416a_t *p = ctx->hwagx.p_io_nim; + uint8_t data = 0; + + if (nim_idx == 0) + nthw_pcal6416a_read(p, 3, &data); + + else if (nim_idx == 1) + nthw_pcal6416a_read(p, 7, &data); + + return data == 0; +} + /* * Utility functions */ @@ -295,6 +315,119 @@ set_loopback(struct adapter_info_s *p_adapter_info, int port, uint32_t mode, uin nt_os_wait_usec(10000); /* 10ms - arbitrary choice */ } +/* + * Initialize NIM, Code based on nt400d1x.cpp: MyPort::createNim() + */ + +static int create_nim(adapter_info_t *drv, int port, bool enable) +{ + int res = 0; + const uint8_t valid_nim_id = NT_NIM_QSFP28; + sfp_nim_state_t nim; + nt4ga_link_t *link_info = &drv->nt4ga_link; + nim_i2c_ctx_t *nim_ctx = &link_info->u.nim_ctx[port]; + + assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX); + assert(link_info->variables_initialized); + + if (!enable) { + phy_reset_rx(drv, port); + phy_reset_tx(drv, 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, NULL); + + if (res) + return res; + + res = nim_state_build(nim_ctx, &nim); + + if (res) + return res; + + /* Set FEC to be enabled by default */ + nim_ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true; + + NT_LOG(DBG, NTHW, "%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'", + drv->mp_port_id_str[port], nim_ctx->nim_id, nim_id_to_text(nim_ctx->nim_id), nim.br, + nim_ctx->vendor_name, nim_ctx->prod_no, nim_ctx->serial_no); + + /* + * 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", + 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", + drv->mp_port_id_str[port], nim_id_to_text(valid_nim_id)); + return -1; + } + + return res; +} + +/* + * Initialize one 100 Gbps port. + */ +static int _port_init(adapter_info_t *p_info, nthw_fpga_t *fpga, int port) +{ + uint8_t adapter_no = p_info->adapter_no; + int res; + + nt4ga_link_t *link_info = &p_info->nt4ga_link; + nthw_gfg_t *p_gfg = &link_info->u.var_a100g.gfg[adapter_no]; + nthw_phy_tile_t *p_phy_tile = p_info->fpga_info.mp_nthw_agx.p_phy_tile; + nthw_rpf_t *p_rpf = p_info->fpga_info.mp_nthw_agx.p_rpf; + + assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX); + assert(link_info->variables_initialized); + + 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; + + nthw_gfg_stop(p_gfg, port); + + for (uint8_t lane = 0; lane < 4; lane++) + nthw_phy_tile_set_host_loopback(p_phy_tile, port, lane, false); + + swap_tx_rx_polarity(p_info, port, true); + nthw_rpf_set_ts_at_eof(p_rpf, true); + + NT_LOG(DBG, NTNIC, "%s: Setting up port %d", p_info->mp_port_id_str[port], port); + + phy_reset_rx(p_info, port); + + if (nthw_gmf_init(NULL, fpga, port) == 0) { + nthw_gmf_t gmf; + + if (nthw_gmf_init(&gmf, fpga, port) == 0) + nthw_gmf_set_enable_tsi(&gmf, true, 0, 0, false); + } + + nthw_rpf_unblock(p_rpf); + + res = create_nim(p_info, port, true); + + if (res) { + NT_LOG(WRN, NTNIC, "%s: NIM initialization failed", + p_info->mp_port_id_str[port]); + return res; + } + + NT_LOG(DBG, NTNIC, "%s: NIM initialized", p_info->mp_port_id_str[port]); + + phy_reset_rx(p_info, port); + return res; +} + /* * Link state machine */ @@ -310,6 +443,7 @@ static void *_common_ptp_nim_state_machine(void *data) /* link_state_t new_link_state; */ link_state_t *link_state = link_info->link_state; + nim_i2c_ctx_t *nim_ctx = link_info->u.var_a100g.nim_ctx; if (!fpga) { NT_LOG(ERR, NTNIC, "%s: fpga is NULL", drv->mp_adapter_id_str); @@ -361,6 +495,15 @@ static void *_common_ptp_nim_state_machine(void *data) continue; if (link_info->port_action[i].port_lpbk_mode != last_lpbk_mode[i]) { + /* Loopback mode has changed. Do something */ + if (!nim_is_present(&nim_ctx[i], i)) { + /* + * If there is no Nim present, we need to initialize the + * port anyway + */ + _port_init(drv, fpga, i); + } + set_loopback(drv, i, link_info->port_action[i].port_lpbk_mode, diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build index 271115cdd3..b1505c5549 100644 --- a/drivers/net/ntnic/meson.build +++ b/drivers/net/ntnic/meson.build @@ -55,6 +55,7 @@ sources = files( 'nthw/core/nthw_iic.c', 'nthw/core/nthw_mac_pcs.c', 'nthw/core/nthw_pcie3.c', + 'nthw/core/nthw_pcal6416a.c', 'nthw/core/nthw_phy_tile.c', 'nthw/core/nthw_si5332_si5156.c', 'nthw/core/nthw_rpf.c', diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gmf.h b/drivers/net/ntnic/nthw/core/include/nthw_gmf.h index cc5be85154..c8beb2117c 100644 --- a/drivers/net/ntnic/nthw/core/include/nthw_gmf.h +++ b/drivers/net/ntnic/nthw/core/include/nthw_gmf.h @@ -58,6 +58,8 @@ struct nthw_gmf { typedef struct nthw_gmf nthw_gmf_t; int nthw_gmf_init(nthw_gmf_t *p, nthw_fpga_t *p_fpga, int n_instance); +void nthw_gmf_set_enable_tsi(nthw_gmf_t *p, bool enable, int tsi_dynamic_offset, + int tsi_static_offset, bool tsi_always); void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable); diff --git a/drivers/net/ntnic/nthw/core/include/nthw_pcal6416a.h b/drivers/net/ntnic/nthw/core/include/nthw_pcal6416a.h index 636d2beb85..5ef14a0bc9 100644 --- a/drivers/net/ntnic/nthw/core/include/nthw_pcal6416a.h +++ b/drivers/net/ntnic/nthw/core/include/nthw_pcal6416a.h @@ -7,13 +7,23 @@ #include <stdint.h> +#include "nthw_i2cm.h" +#include "nthw_si5332_si5156.h" + /* * PCAL6416A I/O expander class */ struct nthw_pcal6416a { + nthw_i2cm_t *mp_nt_i2cm; + uint8_t m_dev_address; + nthw_pca9849_t *mp_ca9849; + uint8_t m_mux_channel; + uint8_t m_config_data[2]; }; typedef struct nthw_pcal6416a nthw_pcal6416a_t; +void nthw_pcal6416a_read(nthw_pcal6416a_t *p, uint8_t pin, uint8_t *value); + #endif /* __NTHW_PCAL6416A_H__ */ diff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.c b/drivers/net/ntnic/nthw/core/nthw_gmf.c index 16a4c288bd..9fb708bc50 100644 --- a/drivers/net/ntnic/nthw/core/nthw_gmf.c +++ b/drivers/net/ntnic/nthw/core/nthw_gmf.c @@ -131,3 +131,44 @@ void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable) if (!p->m_administrative_block) nthw_field_set_val_flush32(p->mp_ctrl_enable, enable ? 1 : 0); } + +void nthw_gmf_set_enable_tsi(nthw_gmf_t *p, bool enable, int tsi_dynamic_offset, + int tsi_static_offset, bool tsi_always) +{ + if (!p->m_administrative_block) { + nthw_field_update_register(p->mp_ctrl_enable); + + if (p->mp_ctrl_ts_inject_always) { + /* + * Do not force timestamp Inject- let the TBH control this now + * Later we could consider an ini-setting for controlling this + */ + nthw_field_set_val_flush32(p->mp_ctrl_ts_inject_always, + tsi_always ? 1 : 0); + } + + if (p->mp_ctrl_fcs_always) { + /* + * Do not force FSC calculation - let the TBH control this + * Later we could consider an ini-setting for controlling this + */ + nthw_field_set_val_flush32(p->mp_ctrl_fcs_always, 0); + } + + if (p->mp_ts_inject) { + nthw_register_update(p->mp_ts_inject); + + if (p->mp_ts_inject_pos) { + nthw_field_set_val_flush32(p->mp_ts_inject_pos, + (uint32_t)tsi_dynamic_offset); + } + + if (p->mp_ts_inject_offset) { + nthw_field_set_val_flush32(p->mp_ts_inject_offset, + (uint32_t)tsi_static_offset); + } + } + + nthw_field_set_val_flush32(p->mp_ctrl_enable, enable ? 1 : 0); + } +} diff --git a/drivers/net/ntnic/nthw/core/nthw_pcal6416a.c b/drivers/net/ntnic/nthw/core/nthw_pcal6416a.c new file mode 100644 index 0000000000..37b6e7ec57 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_pcal6416a.c @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ +#include <pthread.h> + +#include "nt_util.h" +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_pcal6416a.h" + +static const uint8_t read_port[2] = { 0x00, 0x01 }; + +/* + * PCAL6416A I/O expander class + */ + +void nthw_pcal6416a_read(nthw_pcal6416a_t *p, uint8_t pin, uint8_t *value) +{ + uint8_t port; + uint8_t data; + + rte_spinlock_lock(&p->mp_nt_i2cm->i2cmmutex); + nthw_pca9849_set_channel(p->mp_ca9849, p->m_mux_channel); + + if (pin < 8) { + port = 0; + + } else { + port = 1; + pin = (uint8_t)(pin - 8); + } + + nthw_i2cm_read(p->mp_nt_i2cm, p->m_dev_address, read_port[port], &data); + + *value = (data >> pin) & 0x1; + rte_spinlock_unlock(&p->mp_nt_i2cm->i2cmmutex); +} -- 2.45.0