[PATCH v1 03/17] net/hinic3: add FEC mode get/set and capability support
Jiacheng Ye
yejiacheng4 at h-partners.com
Sat Oct 10 16:44:54 CEST 2026
High-speed links need a configurable FEC mode to interoperate with the
peer, so add FEC get/set/capability callbacks.
Signed-off-by: Jiacheng Ye <yejiacheng4 at h-partners.com>
---
doc/guides/rel_notes/release_26_11.rst | 1 +
drivers/net/hinic3/base/hinic3_hwdev.h | 1 +
drivers/net/hinic3/base/hinic3_nic_cfg.c | 110 +++++++++++++++++++++
drivers/net/hinic3/base/hinic3_nic_cfg.h | 42 ++++++++
drivers/net/hinic3/base/hinic3_nic_event.c | 1 +
drivers/net/hinic3/hinic3_ethdev.c | 92 +++++++++++++++++
drivers/net/hinic3/hinic3_ethdev.h | 1 +
7 files changed, 248 insertions(+)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 0c02cbf67f..31109a16a7 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -95,6 +95,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_hwdev.h b/drivers/net/hinic3/base/hinic3_hwdev.h
index c6661aa1a6..5601f93190 100644
--- a/drivers/net/hinic3/base/hinic3_hwdev.h
+++ b/drivers/net/hinic3/base/hinic3_hwdev.h
@@ -158,6 +158,7 @@ struct hinic3_hwdev {
uint16_t max_vfs;
uint16_t link_status;
+ uint32_t speed;
uint64_t features[HINIC3_MAX_FEATURE_QWORD];
};
diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.c b/drivers/net/hinic3/base/hinic3_nic_cfg.c
index 48dee29750..c67779349f 100644
--- a/drivers/net/hinic3/base/hinic3_nic_cfg.c
+++ b/drivers/net/hinic3/base/hinic3_nic_cfg.c
@@ -1752,6 +1752,116 @@ hinic3_set_link_status_follow(struct hinic3_hwdev *hwdev,
return follow.head.status;
}
+static void
+hinic3_fec_param_covert(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), HINIC3_FEC_MODE_NONE},
+ {HINIC3_PORT_FEC_RSFEC, RTE_BIT32(HINIC3_PORT_FEC_RSFEC), HINIC3_FEC_MODE_RS},
+ {HINIC3_PORT_FEC_BASEFEC, RTE_BIT32(HINIC3_PORT_FEC_BASEFEC),
+ HINIC3_FEC_MODE_BASER},
+ {HINIC3_PORT_FEC_NOFEC, RTE_BIT32(HINIC3_PORT_FEC_NOFEC), HINIC3_FEC_MODE_OFF},
+ {HINIC3_PORT_FEC_LLRSFEC, RTE_BIT32(HINIC3_PORT_FEC_LLRSFEC), HINIC3_FEC_MODE_LLRS},
+ {HINIC3_PORT_FEC_AUTO, RTE_BIT32(HINIC3_PORT_FEC_AUTO), HINIC3_FEC_MODE_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_covert(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_covert(HINIC3_FEC_MODE_OPCODE_GET,
+ RTE_BIT32(fec_msg.advertised_fec),
+ advertised_fec);
+
+ if (supported_fec != NULL)
+ hinic3_fec_param_covert(HINIC3_FEC_MODE_OPCODE_GET,
+ fec_msg.supported_fec,
+ supported_fec);
+
+ return 0;
+}
+
uint8_t
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 5f08d49a72..42a7b7010d 100644
--- a/drivers/net/hinic3/base/hinic3_nic_cfg.h
+++ b/drivers/net/hinic3/base/hinic3_nic_cfg.h
@@ -967,6 +967,43 @@ 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,
+};
+
+enum hinic3_fec_mode {
+ HINIC3_FEC_MODE_NONE = 0,
+ HINIC3_FEC_MODE_OFF = 1,
+ HINIC3_FEC_MODE_AUTO = 2,
+ HINIC3_FEC_MODE_BASER = 4,
+ HINIC3_FEC_MODE_RS = 8,
+ HINIC3_FEC_MODE_LLRS = 16,
+};
+
+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 +1620,9 @@ int hinic3_get_cir_drop(void *hwdev, struct hinic3_cir_drop *stats);
uint8_t 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/base/hinic3_nic_event.c b/drivers/net/hinic3/base/hinic3_nic_event.c
index 981a5f5083..be8a1fc576 100644
--- a/drivers/net/hinic3/base/hinic3_nic_event.c
+++ b/drivers/net/hinic3/base/hinic3_nic_event.c
@@ -57,6 +57,7 @@ hinic3_get_link_port_info(struct hinic3_hwdev *hwdev, uint8_t link_state,
port_speed[port_info.speed % LINK_SPEED_LEVELS];
link->link_duplex = port_info.duplex;
link->link_autoneg = port_info.autoneg_state;
+ hwdev->speed = link->link_speed;
}
}
}
diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c
index ecf7df3ec0..6d789565d5 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -3335,6 +3335,95 @@ 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 == HINIC3_FEC_MODE_LLRS ||
+ fec_param == HINIC3_FEC_MODE_RS ||
+ fec_param == HINIC3_FEC_MODE_BASER ||
+ fec_param == HINIC3_FEC_MODE_OFF) {
+ 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 valid, 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;
+ }
+
+ nic_dev->fec_mode = fec_capa;
+
+ 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, 0);
+ 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, 0, &supported_fec);
+ if (err) {
+ PMD_DRV_LOG(ERR, "Failed to get fec capability, err: %d.", err);
+ return err;
+ }
+
+ speed_fec_capa->speed = nic_dev->hwdev->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,
@@ -3381,6 +3470,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 = {
diff --git a/drivers/net/hinic3/hinic3_ethdev.h b/drivers/net/hinic3/hinic3_ethdev.h
index 6e21672a4d..c1bba21486 100644
--- a/drivers/net/hinic3/hinic3_ethdev.h
+++ b/drivers/net/hinic3/hinic3_ethdev.h
@@ -152,6 +152,7 @@ struct hinic3_nic_dev {
struct hinic3_nic_cmdq_ops *cmdq_ops;
struct hinic3_nic_rx_ops *rx_ops;
struct hinic3_nic_tx_ops *tx_ops;
+ uint32_t fec_mode; /**< Current FEC mode for ethdev. */
};
--
2.33.0
More information about the dev
mailing list