High-speed links need a configurable FEC mode to interoperate with the peer, so add FEC get/set/capability callbacks. The advertised/supported FEC values are converted between the firmware and DPDK representations.
Signed-off-by: Jiacheng Ye <[email protected]> --- doc/guides/nics/features/hinic3.ini | 1 + doc/guides/rel_notes/release_26_11.rst | 1 + drivers/net/hinic3/base/hinic3_nic_cfg.c | 114 +++++++++++++++++++++++ drivers/net/hinic3/base/hinic3_nic_cfg.h | 33 +++++++ drivers/net/hinic3/hinic3_ethdev.c | 90 ++++++++++++++++++ 5 files changed, 239 insertions(+) diff --git a/doc/guides/nics/features/hinic3.ini b/doc/guides/nics/features/hinic3.ini index 6cdd240a82..8083dc63d1 100644 --- a/doc/guides/nics/features/hinic3.ini +++ b/doc/guides/nics/features/hinic3.ini @@ -26,6 +26,7 @@ Flow control = Y CRC offload = Y VLAN offload = Y QinQ offload = P +FEC = Y L3 checksum offload = Y L4 checksum offload = Y Inner L3 checksum = P diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst index 606a201ae9..06312632d0 100644 --- a/doc/guides/rel_notes/release_26_11.rst +++ b/doc/guides/rel_notes/release_26_11.rst @@ -117,6 +117,7 @@ New Features * **Updated Huawei hinic3 ethernet driver.** * Added CIR drop statistics and VF link aggregation (LAG) support. + * Added FEC mode get and set support. * **Updated Intel iavf driver.** diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.c b/drivers/net/hinic3/base/hinic3_nic_cfg.c index 1c0e6df081..520ee1e2a2 100644 --- a/drivers/net/hinic3/base/hinic3_nic_cfg.c +++ b/drivers/net/hinic3/base/hinic3_nic_cfg.c @@ -1755,6 +1755,120 @@ hinic3_set_link_status_follow(struct hinic3_hwdev *hwdev, return follow.head.status; } +static void +hinic3_fec_param_convert(uint32_t opcode, uint8_t in_fec_param, uint8_t *out_fec_param) +{ + uint8_t fec_value_table_length; + uint8_t i; + + struct hinic3_fec_param_value_map fec_value_table[] = { + {HINIC3_PORT_FEC_NOT_SET, RTE_BIT32(HINIC3_PORT_FEC_NOT_SET), 0}, + {HINIC3_PORT_FEC_RSFEC, RTE_BIT32(HINIC3_PORT_FEC_RSFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(RS)}, + {HINIC3_PORT_FEC_BASEFEC, RTE_BIT32(HINIC3_PORT_FEC_BASEFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(BASER)}, + {HINIC3_PORT_FEC_NOFEC, RTE_BIT32(HINIC3_PORT_FEC_NOFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC)}, + {HINIC3_PORT_FEC_LLRSFEC, RTE_BIT32(HINIC3_PORT_FEC_LLRSFEC), + RTE_ETH_FEC_MODE_CAPA_MASK(LLRS)}, + {HINIC3_PORT_FEC_AUTO, RTE_BIT32(HINIC3_PORT_FEC_AUTO), + RTE_ETH_FEC_MODE_CAPA_MASK(AUTO)} + }; + + *out_fec_param = 0; + fec_value_table_length = + (uint8_t)(sizeof(fec_value_table) / sizeof(struct hinic3_fec_param_value_map)); + + if (opcode == HINIC3_FEC_MODE_OPCODE_SET) { + for (i = 0; i < fec_value_table_length; i++) { + if ((in_fec_param & fec_value_table[i].ethtool_fec_value) != 0) + *out_fec_param = fec_value_table[i].fec_offset; + } + } + + if (opcode == HINIC3_FEC_MODE_OPCODE_GET) { + for (i = 0; i < fec_value_table_length; i++) { + if ((in_fec_param & fec_value_table[i].hinic3_fec_value) != 0) + *out_fec_param |= fec_value_table[i].ethtool_fec_value; + } + } +} + +int +hinic3_set_fec_mode(struct hinic3_hwdev *hwdev, uint8_t fecparam) +{ + struct mag_cmd_cfg_fec_mode fec_msg = { 0 }; + uint16_t out_size = sizeof(fec_msg); + uint8_t advertised_fec = 0; + int err; + + if (!hwdev) + return -EINVAL; + + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_SET, fecparam, &advertised_fec); + fec_msg.opcode = HINIC3_FEC_MODE_OPCODE_SET; + fec_msg.port_id = hinic3_physical_port_id(hwdev); + fec_msg.advertised_fec = advertised_fec; + + err = hinic3_msg_to_mgmt_sync(hwdev, HINIC3_MOD_HILINK, + MAG_CMD_CFG_FEC_MODE, + &fec_msg, sizeof(fec_msg), + &fec_msg, &out_size); + + if (fec_msg.head.status != 0 || err) { + PMD_DRV_LOG(ERR, "Failed to set fec mode failed, err: %d, status: 0x%x, out size: 0x%x", + err, fec_msg.head.status, out_size); + return -EINVAL; + } + + PMD_DRV_LOG(INFO, "Set fec mode success, active fec capa mode: %d", fecparam); + return 0; +} + +int +hinic3_get_fec_mode(struct hinic3_hwdev *hwdev, uint8_t *advertised_fec, + uint8_t *supported_fec) +{ + struct mag_cmd_cfg_fec_mode fec_msg = { 0 }; + uint16_t out_size = sizeof(fec_msg); + int err; + + if (!hwdev) + return -EINVAL; + + if (advertised_fec != NULL) + *advertised_fec = 0; + + if (supported_fec != NULL) + *supported_fec = 0; + + fec_msg.opcode = HINIC3_FEC_MODE_OPCODE_GET; + fec_msg.port_id = hinic3_physical_port_id(hwdev); + + err = hinic3_msg_to_mgmt_sync(hwdev, HINIC3_MOD_HILINK, + MAG_CMD_CFG_FEC_MODE, + &fec_msg, sizeof(fec_msg), + &fec_msg, &out_size); + + if (fec_msg.head.status != 0 || err) { + PMD_DRV_LOG(ERR, "Failed to get fec mode failed, err: %d, status: 0x%x, out size: 0x%x", + err, fec_msg.head.status, out_size); + return -EINVAL; + } + + if (advertised_fec != NULL) + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_GET, + RTE_BIT32(fec_msg.advertised_fec), + advertised_fec); + + if (supported_fec != NULL) + hinic3_fec_param_convert(HINIC3_FEC_MODE_OPCODE_GET, + fec_msg.supported_fec, + supported_fec); + + return 0; +} + bool hinic3_cmd_vf_lag(void *hwdev, uint16_t func_id, uint8_t opcode) { diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.h b/drivers/net/hinic3/base/hinic3_nic_cfg.h index 6fb56fff4f..4d4cb2d8cb 100644 --- a/drivers/net/hinic3/base/hinic3_nic_cfg.h +++ b/drivers/net/hinic3/base/hinic3_nic_cfg.h @@ -967,6 +967,34 @@ int hinic3_msg_to_mgmt_sync(struct hinic3_hwdev *hwdev, enum hinic3_mod_type mod int hinic3_set_ci_table(struct hinic3_hwdev *hwdev, struct hinic3_sq_attr *attr); +enum hinic3_fec_mode_opcode { + HINIC3_FEC_MODE_OPCODE_GET = 0, + HINIC3_FEC_MODE_OPCODE_SET = 1, +}; + +struct mag_cmd_cfg_fec_mode { + struct mgmt_msg_head head; + uint8_t port_id; + uint8_t opcode; + uint8_t advertised_fec; + uint8_t supported_fec; +}; + +enum hinic3_cmd_port_fec { + HINIC3_PORT_FEC_NOT_SET = 0, + HINIC3_PORT_FEC_RSFEC = 1, + HINIC3_PORT_FEC_BASEFEC = 2, + HINIC3_PORT_FEC_NOFEC = 3, + HINIC3_PORT_FEC_LLRSFEC = 4, + HINIC3_PORT_FEC_AUTO = 5, +}; + +struct hinic3_fec_param_value_map { + uint8_t fec_offset; + uint8_t hinic3_fec_value; + uint8_t ethtool_fec_value; +}; + /** * Update MAC address to hardware. * @@ -1583,4 +1611,9 @@ int hinic3_get_cir_drop(void *hwdev, struct hinic3_cir_drop *stats); bool hinic3_cmd_vf_lag(void *hwdev, uint16_t func_id, uint8_t opcode); +int hinic3_set_fec_mode(struct hinic3_hwdev *hwdev, uint8_t fecparam); + +int hinic3_get_fec_mode(struct hinic3_hwdev *hwdev, uint8_t *advertised_fec, + uint8_t *supported_fec); + #endif /* _HINIC3_NIC_CFG_H_ */ diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c index 6dd617f6d1..f09b6248a7 100644 --- a/drivers/net/hinic3/hinic3_ethdev.c +++ b/drivers/net/hinic3/hinic3_ethdev.c @@ -3358,6 +3358,93 @@ hinic3_dev_filter_ctrl(struct rte_eth_dev *dev, const struct rte_flow_ops **arg) return 0; } +static const uint32_t HINIC3_FEC_CAPA_NUM_PER_SPEED = 1; + +static bool +hinic3_fec_param_valid(uint32_t fec_param) +{ + if (fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(LLRS) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(RS) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(BASER) || + fec_param == RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC)) { + return true; + } + + return false; +} + +static int +hinic3_fec_set(struct rte_eth_dev *dev, uint32_t fec_capa) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + int err; + + if (hinic3_fec_param_valid(fec_capa) == false) { + PMD_DRV_LOG(ERR, "Fec param is invalid, failed to set fec param."); + return -EINVAL; + } + + err = hinic3_set_fec_mode(nic_dev->hwdev, (uint8_t)fec_capa); + if (err) { + PMD_DRV_LOG(ERR, "Set fec param failed: %d.", err); + return err; + } + + return 0; +} + +static int +hinic3_fec_get(struct rte_eth_dev *dev, uint32_t *fec_capa) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + uint8_t advertised_fec = 0; + int err; + + err = hinic3_get_fec_mode(nic_dev->hwdev, &advertised_fec, NULL); + if (err) { + PMD_DRV_LOG(ERR, "Get fec parma failed: %d.", err); + return err; + } + + *fec_capa = (uint32_t)advertised_fec; + + return 0; +} + +static int +hinic3_fec_capability_get(struct rte_eth_dev *dev, + struct rte_eth_fec_capa *speed_fec_capa, + unsigned int num) +{ + struct hinic3_nic_dev *nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(dev); + uint8_t supported_fec = 0; + int err; + + if (speed_fec_capa == NULL) + return HINIC3_FEC_CAPA_NUM_PER_SPEED; + + if (num < HINIC3_FEC_CAPA_NUM_PER_SPEED) { + PMD_DRV_LOG(ERR, "Not enough array size(%u) to store FEC capabilities, should not be less than %u.", + num, HINIC3_FEC_CAPA_NUM_PER_SPEED); + return -EINVAL; + } + + err = hinic3_get_fec_mode(nic_dev->hwdev, NULL, &supported_fec); + if (err) { + PMD_DRV_LOG(ERR, "Failed to get fec capability, err: %d.", err); + return err; + } + + speed_fec_capa->speed = dev->data->dev_link.link_speed; + speed_fec_capa->capa = (uint32_t)supported_fec; + + if (speed_fec_capa->speed == RTE_ETH_SPEED_NUM_NONE || + speed_fec_capa->capa == 0) + return -ENOTSUP; + + return HINIC3_FEC_CAPA_NUM_PER_SPEED; +} + static const struct eth_dev_ops hinic3_pmd_ops = { .dev_configure = hinic3_dev_configure, .dev_infos_get = hinic3_dev_infos_get, @@ -3404,6 +3491,9 @@ static const struct eth_dev_ops hinic3_pmd_ops = { .mac_addr_add = hinic3_mac_addr_add, .set_mc_addr_list = hinic3_set_mc_addr_list, .flow_ops_get = hinic3_dev_filter_ctrl, + .fec_get_capability = hinic3_fec_capability_get, + .fec_get = hinic3_fec_get, + .fec_set = hinic3_fec_set, }; static const struct eth_dev_ops hinic3_pmd_vf_ops = { -- 2.33.0

