From: Danylo Vodopianov <dvo-...@napatech.com> Implement PHY line loopback configuration for Agilex FPGA
Signed-off-by: Danylo Vodopianov <dvo-...@napatech.com> --- .../link_agx_100g/nt4ga_agx_link_100g.c | 111 ++++++++++++++++++ .../ntnic/nthw/core/include/nthw_phy_tile.h | 1 + drivers/net/ntnic/nthw/core/nthw_phy_tile.c | 28 +++++ 3 files changed, 140 insertions(+) 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 a6455ee598..f3862b7fe3 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 @@ -14,6 +14,11 @@ #include "nthw_gfg.h" #include "nthw_phy_tile.h" +typedef enum { + LOOPBACK_HOST_NONE, + LOOPBACK_HOST +} loopback_host_t; + static int nt4ga_agx_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nthw_fpga_t *fpga); @@ -29,6 +34,65 @@ void link_agx_100g_init(void) register_agx_100g_link_ops(&link_agx_100g_ops); } +/* + * Phy handling + */ + +static void phy_rx_path_rst(adapter_info_t *drv, int port, bool reset) +{ + nthw_phy_tile_t *p = drv->fpga_info.mp_nthw_agx.p_phy_tile; + NT_LOG(DBG, NTNIC, "Port %d: %s", port, reset ? "assert" : "deassert"); + nthw_phy_tile_set_rx_reset(p, port, reset); +} + +static void phy_tx_path_rst(adapter_info_t *drv, int port, bool reset) +{ + nthw_phy_tile_t *p = drv->fpga_info.mp_nthw_agx.p_phy_tile; + NT_LOG(DBG, NTNIC, "Port %d: %s", port, reset ? "assert" : "deassert"); + nthw_phy_tile_set_tx_reset(p, port, reset); +} + +static void phy_reset_rx(adapter_info_t *drv, int port) +{ + phy_rx_path_rst(drv, port, true); + nt_os_wait_usec(10000); /* 10ms */ + phy_rx_path_rst(drv, port, false); + nt_os_wait_usec(10000); /* 10ms */ +} + +static void phy_reset_tx(adapter_info_t *drv, int port) +{ + phy_tx_path_rst(drv, port, true); + nt_os_wait_usec(10000); /* 10ms */ + phy_tx_path_rst(drv, port, false); + nt_os_wait_usec(10000); /* 10ms */ +} + +static int phy_set_host_loopback(adapter_info_t *drv, int port, loopback_host_t loopback) +{ + nthw_phy_tile_t *p = drv->fpga_info.mp_nthw_agx.p_phy_tile; + + switch (loopback) { + case LOOPBACK_HOST_NONE: + for (uint8_t lane = 0; lane < 4; lane++) + nthw_phy_tile_set_host_loopback(p, port, lane, false); + + break; + + case LOOPBACK_HOST: + for (uint8_t lane = 0; lane < 4; lane++) + nthw_phy_tile_set_host_loopback(p, port, lane, true); + + break; + + default: + NT_LOG(ERR, NTNIC, "Port %d: Unhandled loopback value (%d)", port, loopback); + return -1; + } + + return 0; +} + /* * Utility functions */ @@ -81,6 +145,40 @@ static int swap_tx_rx_polarity(adapter_info_t *drv, int port, bool swap) return -1; } +static void +set_loopback(struct adapter_info_s *p_adapter_info, int port, uint32_t mode, uint32_t last_mode) +{ + switch (mode) { + case 1: + NT_LOG(INF, NTNIC, "%s: Applying host loopback", + p_adapter_info->mp_port_id_str[port]); + phy_set_host_loopback(p_adapter_info, port, LOOPBACK_HOST); + swap_tx_rx_polarity(p_adapter_info, port, false); + break; + + default: + switch (last_mode) { + case 1: + NT_LOG(INF, NTNIC, "%s: Removing host loopback", + p_adapter_info->mp_port_id_str[port]); + phy_set_host_loopback(p_adapter_info, port, LOOPBACK_HOST_NONE); + swap_tx_rx_polarity(p_adapter_info, port, true); + break; + + default: + /* do nothing */ + break; + } + + break; + } + + /* After changing the loopback the system must be properly reset */ + phy_reset_rx(p_adapter_info, port); + phy_reset_tx(p_adapter_info, port); + nt_os_wait_usec(10000); /* 10ms - arbitrary choice */ +} + /* * Link state machine */ @@ -146,6 +244,19 @@ static void *_common_ptp_nim_state_machine(void *data) if (is_port_disabled) continue; + if (link_info->port_action[i].port_lpbk_mode != last_lpbk_mode[i]) { + set_loopback(drv, + i, + link_info->port_action[i].port_lpbk_mode, + last_lpbk_mode[i]); + + 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; + } + link_state[i].link_disabled = is_port_disabled; if (!link_state[i].nim_present) { diff --git a/drivers/net/ntnic/nthw/core/include/nthw_phy_tile.h b/drivers/net/ntnic/nthw/core/include/nthw_phy_tile.h index 930c897046..5577f95944 100644 --- a/drivers/net/ntnic/nthw/core/include/nthw_phy_tile.h +++ b/drivers/net/ntnic/nthw/core/include/nthw_phy_tile.h @@ -52,6 +52,7 @@ void nthw_phy_tile_set_tx_pol_inv(nthw_phy_tile_t *p, uint8_t intf_no, uint8_t l void nthw_phy_tile_set_rx_pol_inv(nthw_phy_tile_t *p, uint8_t intf_no, uint8_t lane, bool invert); void nthw_phy_tile_set_host_loopback(nthw_phy_tile_t *p, uint8_t intf_no, uint8_t lane, bool enable); +void nthw_phy_tile_set_tx_reset(nthw_phy_tile_t *p, uint8_t intf_no, bool reset); void nthw_phy_tile_set_rx_reset(nthw_phy_tile_t *p, uint8_t intf_no, bool reset); uint32_t nthw_phy_tile_read_xcvr(nthw_phy_tile_t *p, uint8_t intf_no, uint8_t lane, diff --git a/drivers/net/ntnic/nthw/core/nthw_phy_tile.c b/drivers/net/ntnic/nthw/core/nthw_phy_tile.c index fd7a1e1214..6402e5caf3 100644 --- a/drivers/net/ntnic/nthw/core/nthw_phy_tile.c +++ b/drivers/net/ntnic/nthw/core/nthw_phy_tile.c @@ -64,6 +64,34 @@ void nthw_phy_tile_set_rx_reset(nthw_phy_tile_t *p, uint8_t intf_no, bool reset) } } +void nthw_phy_tile_set_tx_reset(nthw_phy_tile_t *p, uint8_t intf_no, bool reset) +{ + /* Reset is active low */ + nthw_field_get_updated(p->mp_fld_port_config_tx_reset[intf_no]); + + if (reset) { + nthw_field_clr_flush(p->mp_fld_port_config_tx_reset[intf_no]); + + /* Wait for ack */ + if (p->mp_fld_port_status_tx_reset_ackn[intf_no]) { + int32_t count = 1000; + + do { + nt_os_wait_usec(1000); /* 1ms */ + } while (nthw_field_get_updated(p->mp_fld_port_status_tx_reset_ackn + [intf_no]) && (--count > 0)); + + if (count <= 0) { + NT_LOG(ERR, NTHW, "intf_no %u: Time-out waiting for TxResetAck", + intf_no); + } + } + + } else { + nthw_field_set_flush(p->mp_fld_port_config_tx_reset[intf_no]); + } +} + uint32_t nthw_phy_tile_read_xcvr(nthw_phy_tile_t *p, uint8_t intf_no, uint8_t lane, uint32_t address) { -- 2.45.0