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 <[email protected]>
---
 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


Reply via email to