[PATCH v10 20/21] net/ntnic: add GPIO communication for NIMs
Serhii Iliushyk
sil-plv at napatech.com
Wed Jul 17 15:33:07 CEST 2024
For NIM reset sequence GPIO communication is used.
After this commit the NIMs supported by ntnic
is able to start up and be controlled fully by the adapter.
Signed-off-by: Serhii Iliushyk <sil-plv at napatech.com>
---
v5
* Fix Typo/Spelling
v10
* Use 8 spaces as indentation in meson
---
drivers/net/ntnic/include/nt4ga_link.h | 1 +
.../link_mgmt/link_100g/nt4ga_link_100g.c | 71 ++++++++-
drivers/net/ntnic/meson.build | 1 +
.../net/ntnic/nthw/core/include/nthw_core.h | 1 +
.../ntnic/nthw/core/include/nthw_gpio_phy.h | 48 ++++++
drivers/net/ntnic/nthw/core/nthw_gpio_phy.c | 145 ++++++++++++++++++
6 files changed, 263 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
create mode 100644 drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
diff --git a/drivers/net/ntnic/include/nt4ga_link.h b/drivers/net/ntnic/include/nt4ga_link.h
index 0851057f81..5a16afea2a 100644
--- a/drivers/net/ntnic/include/nt4ga_link.h
+++ b/drivers/net/ntnic/include/nt4ga_link.h
@@ -78,6 +78,7 @@ typedef struct port_action_s {
typedef struct adapter_100g_s {
nim_i2c_ctx_t nim_ctx[NUM_ADAPTER_PORTS_MAX]; /* Should be the first field */
+ nthw_gpio_phy_t gpio_phy[NUM_ADAPTER_PORTS_MAX];
} adapter_100g_t;
typedef union adapter_var_s {
diff --git a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
index 69d0a5d24a..ed0b89d417 100644
--- a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
+++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
@@ -10,13 +10,24 @@
#include "i2c_nim.h"
#include "ntnic_mod_reg.h"
+/*
+ * Check whether a NIM module is present
+ */
+static bool _nim_is_present(nthw_gpio_phy_t *gpio_phy, uint8_t if_no)
+{
+ assert(if_no < NUM_ADAPTER_PORTS_MAX);
+
+ return nthw_gpio_phy_is_module_present(gpio_phy, if_no);
+}
+
/*
* Initialize NIM, Code based on nt200e3_2_ptp.cpp: MyPort::createNim()
*/
-static int _create_nim(adapter_info_t *drv, int port)
+static int _create_nim(adapter_info_t *drv, int port, bool enable)
{
int res = 0;
const uint8_t valid_nim_id = 17U;
+ nthw_gpio_phy_t *gpio_phy;
nim_i2c_ctx_t *nim_ctx;
sfp_nim_state_t nim;
nt4ga_link_t *link_info = &drv->nt4ga_link;
@@ -24,14 +35,43 @@ static int _create_nim(adapter_info_t *drv, int port)
assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
assert(link_info->variables_initialized);
+ gpio_phy = &link_info->u.var100g.gpio_phy[port];
nim_ctx = &link_info->u.var100g.nim_ctx[port];
+ /*
+ * Check NIM is present before doing GPIO PHY reset.
+ */
+ if (!_nim_is_present(gpio_phy, (uint8_t)port)) {
+ NT_LOG(INF, NTNIC, "%s: NIM module is absent\n", drv->mp_port_id_str[port]);
+ return 0;
+ }
+
+ /*
+ * Perform PHY reset.
+ */
+ NT_LOG(DBG, NTNIC, "%s: Performing NIM reset\n", drv->mp_port_id_str[port]);
+ nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, true);
+ nt_os_wait_usec(100000);/* pause 0.1s */
+ nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, false);
+
/*
* 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 */
+ if (!_nim_is_present(gpio_phy, (uint8_t)port)) {
+ NT_LOG(DBG, NTNIC, "%s: NIM module is no longer absent!\n",
+ drv->mp_port_id_str[port]);
+ return -1;
+ }
+
+ if (!_nim_is_present(gpio_phy, (uint8_t)port)) {
+ NT_LOG(DBG, NTNIC, "%s: NIM module is no longer absent!\n",
+ drv->mp_port_id_str[port]);
+ return -1;
+ }
+
res = construct_and_preinit_nim(nim_ctx, NULL);
if (res)
@@ -57,6 +97,15 @@ static int _create_nim(adapter_info_t *drv, int port)
return -1;
}
+ if (enable) {
+ NT_LOG(DBG, NTNIC, "%s: De-asserting low power\n", drv->mp_port_id_str[port]);
+ nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, false);
+
+ } else {
+ NT_LOG(DBG, NTNIC, "%s: Asserting low power\n", drv->mp_port_id_str[port]);
+ nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, true);
+ }
+
return res;
}
@@ -89,7 +138,7 @@ static int _port_init(adapter_info_t *drv, int port)
/* Phase 3. Link state machine steps */
/* 3.1) Create NIM, ::createNim() */
- res = _create_nim(drv, port);
+ res = _create_nim(drv, port, true);
if (res) {
NT_LOG(WRN, NTNIC, "%s: NIM initialization failed\n", drv->mp_port_id_str[port]);
@@ -115,6 +164,7 @@ static int _common_ptp_nim_state_machine(void *data)
uint32_t last_lpbk_mode[NUM_ADAPTER_PORTS_MAX];
link_state_t *link_state;
+ nthw_gpio_phy_t *gpio_phy;
if (!fpga) {
NT_LOG(ERR, NTNIC, "%s: fpga is NULL\n", drv->mp_adapter_id_str);
@@ -123,6 +173,7 @@ static int _common_ptp_nim_state_machine(void *data)
assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);
link_state = link_info->link_state;
+ gpio_phy = link_info->u.var100g.gpio_phy;
monitor_task_is_running[adapter_no] = 1;
memset(last_lpbk_mode, 0, sizeof(last_lpbk_mode));
@@ -150,7 +201,7 @@ static int _common_ptp_nim_state_machine(void *data)
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);
+ (void)_create_nim(drv, i, false);
NT_LOG(DBG, NTNIC, "%s: Port %i is disabled\n",
drv->mp_port_id_str[i], i);
continue;
@@ -167,7 +218,13 @@ static int _common_ptp_nim_state_machine(void *data)
if (link_info->port_action[i].port_lpbk_mode != last_lpbk_mode[i]) {
/* Loopback mode has changed. Do something */
- _port_init(drv, i);
+ if (!_nim_is_present(&gpio_phy[i], (uint8_t)i)) {
+ /*
+ * If there is no Nim present, we need to initialize the
+ * port anyway
+ */
+ _port_init(drv, i);
+ }
NT_LOG(INF, NTNIC, "%s: Loopback mode changed=%u\n",
drv->mp_port_id_str[i],
@@ -224,6 +281,7 @@ static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nth
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;
+ nthw_gpio_phy_t *gpio_phy = p_adapter_info->nt4ga_link.u.var100g.gpio_phy;
int i;
for (i = 0; i < nb_ports; i++) {
@@ -239,6 +297,11 @@ static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nth
nim_ctx[i].devaddr = 0x50; /* 0xA0 / 2 */
nim_ctx[i].regaddr = 0U;
nim_ctx[i].type = I2C_HWIIC;
+
+ res = nthw_gpio_phy_init(&gpio_phy[i], fpga, 0 /* Only one instance */);
+
+ if (res != 0)
+ break;
}
if (res == 0) {
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index 5d044153e3..6dd972cf7d 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -34,6 +34,7 @@ sources = files(
'nthw/core/nt200a0x/reset/nthw_fpga_rst9563.c',
'nthw/core/nt200a0x/reset/nthw_fpga_rst_nt200a0x.c',
'nthw/core/nthw_fpga.c',
+ 'nthw/core/nthw_gpio_phy.c',
'nthw/core/nthw_hif.c',
'nthw/core/nthw_i2cm.c',
'nthw/core/nthw_iic.c',
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h b/drivers/net/ntnic/nthw/core/include/nthw_core.h
index 8d9d78b86d..5cce56e13f 100644
--- a/drivers/net/ntnic/nthw/core/include/nthw_core.h
+++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h
@@ -17,6 +17,7 @@
#include "nthw_iic.h"
#include "nthw_i2cm.h"
+#include "nthw_gpio_phy.h"
#include "nthw_sdc.h"
#include "nthw_si5340.h"
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
new file mode 100644
index 0000000000..d4641cdb5d
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
@@ -0,0 +1,48 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NTHW_GPIO_PHY_H_
+#define NTHW_GPIO_PHY_H_
+
+#define GPIO_PHY_INTERFACES (2)
+
+typedef struct {
+ nthw_field_t *cfg_fld_lp_mode; /* Cfg Low Power Mode */
+ nthw_field_t *cfg_int; /* Cfg Port Interrupt */
+ nthw_field_t *cfg_reset;/* Cfg Reset */
+ nthw_field_t *cfg_mod_prs; /* Cfg Module Present */
+ nthw_field_t *cfg_pll_int; /* Cfg PLL Interrupt */
+ nthw_field_t *cfg_port_rxlos; /* Emulate Cfg Port RXLOS */
+
+ nthw_field_t *gpio_fld_lp_mode; /* Gpio Low Power Mode */
+ nthw_field_t *gpio_int; /* Gpio Port Interrupt */
+ nthw_field_t *gpio_reset; /* Gpio Reset */
+ nthw_field_t *gpio_mod_prs; /* Gpio Module Present */
+ nthw_field_t *gpio_pll_int; /* Gpio PLL Interrupt */
+ nthw_field_t *gpio_port_rxlos; /* Emulate Gpio Port RXLOS */
+} gpio_phy_fields_t;
+
+struct nthw_gpio_phy {
+ nthw_fpga_t *mp_fpga;
+ nthw_module_t *mp_mod_gpio_phy;
+ int mn_instance;
+
+ /* Registers */
+ nthw_register_t *mp_reg_config;
+ nthw_register_t *mp_reg_gpio;
+
+ /* Fields */
+ gpio_phy_fields_t mpa_fields[GPIO_PHY_INTERFACES];
+};
+
+typedef struct nthw_gpio_phy nthw_gpio_phy_t;
+
+int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no);
+void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool enable);
+void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable);
+
+#endif /* NTHW_GPIO_PHY_H_ */
diff --git a/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
new file mode 100644
index 0000000000..754a8ca5a4
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
@@ -0,0 +1,145 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nt_util.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "nthw_gpio_phy.h"
+
+int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+ nthw_module_t *p_mod = nthw_fpga_query_module(p_fpga, MOD_GPIO_PHY, n_instance);
+
+ if (p == NULL)
+ return p_mod == NULL ? -1 : 0;
+
+ if (p_mod == NULL) {
+ NT_LOG(ERR, NTHW, "%s: GPIO_PHY %d: no such instance\n",
+ p_fpga->p_fpga_info->mp_adapter_id_str, n_instance);
+ return -1;
+ }
+
+ p->mp_fpga = p_fpga;
+ p->mn_instance = n_instance;
+ p->mp_mod_gpio_phy = p_mod;
+
+ /* Registers */
+ p->mp_reg_config = nthw_module_get_register(p->mp_mod_gpio_phy, GPIO_PHY_CFG);
+ p->mp_reg_gpio = nthw_module_get_register(p->mp_mod_gpio_phy, GPIO_PHY_GPIO);
+
+ /* PORT-0, config fields */
+ p->mpa_fields[0].cfg_fld_lp_mode =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_LPMODE);
+ p->mpa_fields[0].cfg_int =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_INT_B);
+ p->mpa_fields[0].cfg_reset =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_RESET_B);
+ p->mpa_fields[0].cfg_mod_prs =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_MODPRS_B);
+
+ /* PORT-0, Non-mandatory fields (query_field) */
+ p->mpa_fields[0].cfg_pll_int =
+ nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_PLL_INTR);
+ p->mpa_fields[0].cfg_port_rxlos =
+ nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_E_PORT0_RXLOS);
+
+ /* PORT-1, config fields */
+ p->mpa_fields[1].cfg_fld_lp_mode =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_LPMODE);
+ p->mpa_fields[1].cfg_int =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_INT_B);
+ p->mpa_fields[1].cfg_reset =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_RESET_B);
+ p->mpa_fields[1].cfg_mod_prs =
+ nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_MODPRS_B);
+
+ /* PORT-1, Non-mandatory fields (query_field) */
+ p->mpa_fields[1].cfg_pll_int =
+ nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_PLL_INTR);
+ p->mpa_fields[1].cfg_port_rxlos =
+ nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_E_PORT1_RXLOS);
+
+ /* PORT-0, gpio fields */
+ p->mpa_fields[0].gpio_fld_lp_mode =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_LPMODE);
+ p->mpa_fields[0].gpio_int =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_INT_B);
+ p->mpa_fields[0].gpio_reset =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_RESET_B);
+ p->mpa_fields[0].gpio_mod_prs =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_MODPRS_B);
+
+ /* PORT-0, Non-mandatory fields (query_field) */
+ p->mpa_fields[0].gpio_pll_int =
+ nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_PLL_INTR);
+ p->mpa_fields[0].gpio_port_rxlos =
+ nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_E_PORT0_RXLOS);
+
+ /* PORT-1, gpio fields */
+ p->mpa_fields[1].gpio_fld_lp_mode =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_LPMODE);
+ p->mpa_fields[1].gpio_int =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_INT_B);
+ p->mpa_fields[1].gpio_reset =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_RESET_B);
+ p->mpa_fields[1].gpio_mod_prs =
+ nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_MODPRS_B);
+
+ /* PORT-1, Non-mandatory fields (query_field) */
+ p->mpa_fields[1].gpio_pll_int =
+ nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_PLL_INTR);
+ p->mpa_fields[1].gpio_port_rxlos =
+ nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_E_PORT1_RXLOS);
+
+ nthw_register_update(p->mp_reg_config);
+
+ return 0;
+}
+
+bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no)
+{
+ if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+ assert(false);
+ return false;
+ }
+
+ /* NOTE: This is a negated GPIO PIN "MODPRS_B" */
+ return nthw_field_get_updated(p->mpa_fields[if_no].gpio_mod_prs) == 0U ? true : false;
+}
+
+void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool enable)
+{
+ if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+ assert(false);
+ return;
+ }
+
+ if (enable)
+ nthw_field_set_flush(p->mpa_fields[if_no].gpio_fld_lp_mode);
+
+ else
+ nthw_field_clr_flush(p->mpa_fields[if_no].gpio_fld_lp_mode);
+
+ nthw_field_clr_flush(p->mpa_fields[if_no].cfg_fld_lp_mode); /* enable output */
+}
+
+void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable)
+{
+ if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+ assert(false);
+ return;
+ }
+
+ if (enable)
+ nthw_field_clr_flush(p->mpa_fields[if_no].gpio_reset);
+
+ else
+ nthw_field_set_flush(p->mpa_fields[if_no].gpio_reset);
+
+ nthw_field_clr_flush(p->mpa_fields[if_no].cfg_reset); /* enable output */
+}
--
2.43.0
More information about the dev
mailing list