[V13,2/3] net/hns3: support FEC
diff mbox series

Message ID 1601023147-57193-3-git-send-email-humin29@huawei.com
State Superseded
Delegated to: Ferruh Yigit
Headers show
Series
  • add FEC support
Related show

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Min Hu (Connor) Sept. 25, 2020, 8:39 a.m. UTC
Forward error correction (FEC) is a bit error correction mode.
It adds error correction information to data packets at the
transmit end, and uses the error correction information to correct
the bit errors generated during data packet transmission at the
receive end. This improves signal quality but also brings a delay
to signals. This function can be enabled or disabled as required.

This patch adds FEC support for ethdev.Introduce ethdev
operations which support query and config FEC information in
hardware.

Signed-off-by: Min Hu (Connor) <humin29@huawei.com>
Reviewed-by: Wei Hu (Xavier) <xavier.huwei@huawei.com>
Reviewed-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Chengchang Tang <tangchengchang@huawei.com>
---
v12->v13:
change fec get capa interface.

---
v11->v12:
fix coding warning.

v10->v11:
allow to report capabilities per link speed.
specify what should be reported if link is down
when get FEC.
change mode to capa bitmask.

---
v9->v10:
use RTE_ETH_FEC_MODE_CAPA_MASK(x) which is just defined.

---
v7->v8:
FEC mode order defined in hns3 hardware is inconsistend with
that defined in the ethdev library. So the sequence needs
to be converted.

---
v5->v6:
fixed code styles according to DPDK coding style.
added _eth prefix.

---
v4->v5:
Data type for fec_cap is changed from uint8_t
to uint32_t for possible future expansion.

---
v2->v3:
adjust the return value of function.

---
 doc/guides/rel_notes/release_20_11.rst |   5 +
 drivers/net/hns3/hns3_cmd.h            |  19 +-
 drivers/net/hns3/hns3_ethdev.c         | 356 +++++++++++++++++++++++++++++++++
 drivers/net/hns3/hns3_ethdev.h         |   1 +
 4 files changed, 380 insertions(+), 1 deletion(-)

Patch
diff mbox series

diff --git a/doc/guides/rel_notes/release_20_11.rst b/doc/guides/rel_notes/release_20_11.rst
index 1f04bd5..eec6930 100644
--- a/doc/guides/rel_notes/release_20_11.rst
+++ b/doc/guides/rel_notes/release_20_11.rst
@@ -83,6 +83,11 @@  New Features
   Added the FEC API which provides functions for query FEC capabilities and
   current FEC mode from device. Also, API for configuring FEC mode is also provided.
 
+* **Added hns3 FEC PMD, for supporting query and config FEC mode.**
+
+  Added the FEC PMD which provides functions for query FEC capabilities and
+  current FEC mode from device. Also, PMD for configuring FEC mode is also provided.
+
 
 Removed Items
 -------------
diff --git a/drivers/net/hns3/hns3_cmd.h b/drivers/net/hns3/hns3_cmd.h
index 87d6053..a1e9604 100644
--- a/drivers/net/hns3/hns3_cmd.h
+++ b/drivers/net/hns3/hns3_cmd.h
@@ -100,6 +100,7 @@  enum hns3_opcode_type {
 	HNS3_OPC_QUERY_LINK_STATUS      = 0x0307,
 	HNS3_OPC_CONFIG_MAX_FRM_SIZE    = 0x0308,
 	HNS3_OPC_CONFIG_SPEED_DUP       = 0x0309,
+	HNS3_OPC_CONFIG_FEC_MODE        = 0x031A,
 
 	/* PFC/Pause commands */
 	HNS3_OPC_CFG_MAC_PAUSE_EN       = 0x0701,
@@ -684,9 +685,25 @@  struct hns3_config_auto_neg_cmd {
 	uint8_t   rsv[20];
 };
 
+#define HNS3_MAC_CFG_FEC_AUTO_EN_B	0
+#define HNS3_MAC_CFG_FEC_MODE_S		1
+#define HNS3_MAC_CFG_FEC_MODE_M	GENMASK(3, 1)
+#define HNS3_MAC_FEC_OFF		0
+#define HNS3_MAC_FEC_BASER		1
+#define HNS3_MAC_FEC_RS			2
+
 struct hns3_sfp_speed_cmd {
 	uint32_t  sfp_speed;
-	uint32_t  rsv[5];
+	uint8_t   query_type; /* 0: sfp speed, 1: active fec */
+	uint8_t   active_fec; /* current FEC mode */
+	uint16_t  rsv1;
+	uint32_t  rsv2[4];
+};
+
+/* Configure FEC mode, opcode:0x031A */
+struct hns3_config_fec_cmd {
+	uint8_t fec_mode;
+	uint8_t rsv[23];
 };
 
 #define HNS3_MAC_MGR_MASK_VLAN_B		BIT(0)
diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c
index 73d5042..d3650bc 100644
--- a/drivers/net/hns3/hns3_ethdev.c
+++ b/drivers/net/hns3/hns3_ethdev.c
@@ -63,6 +63,11 @@ 
 #define HNS3_RESET_WAIT_MS	100
 #define HNS3_RESET_WAIT_CNT	200
 
+/* FEC mode order defined in HNS3 hardware */
+#define HNS3_HW_FEC_MODE_NOFEC  0
+#define HNS3_HW_FEC_MODE_BASER  1
+#define HNS3_HW_FEC_MODE_RS     2
+
 enum hns3_evt_cause {
 	HNS3_VECTOR0_EVENT_RST,
 	HNS3_VECTOR0_EVENT_MBX,
@@ -70,6 +75,34 @@  enum hns3_evt_cause {
 	HNS3_VECTOR0_EVENT_OTHER,
 };
 
+static const struct rte_eth_fec_capa speed_fec_capa_tbl[] = {
+	{ ETH_SPEED_NUM_10G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(BASER) },
+
+	{ ETH_SPEED_NUM_25G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(BASER) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(RS) },
+
+	{ ETH_SPEED_NUM_40G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(BASER) },
+
+	{ ETH_SPEED_NUM_50G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(BASER) |
+			     RTE_ETH_FEC_MODE_CAPA_MASK(RS) },
+
+	{ ETH_SPEED_NUM_100G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			      RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			      RTE_ETH_FEC_MODE_CAPA_MASK(RS) },
+
+	{ ETH_SPEED_NUM_200G, RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC) |
+			      RTE_ETH_FEC_MODE_CAPA_MASK(AUTO) |
+			      RTE_ETH_FEC_MODE_CAPA_MASK(RS) }
+};
+
 static enum hns3_reset_level hns3_get_reset_level(struct hns3_adapter *hns,
 						 uint64_t *levels);
 static int hns3_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
@@ -81,6 +114,8 @@  static int hns3_add_mc_addr(struct hns3_hw *hw,
 			    struct rte_ether_addr *mac_addr);
 static int hns3_remove_mc_addr(struct hns3_hw *hw,
 			    struct rte_ether_addr *mac_addr);
+static int hns3_restore_fec(struct hns3_hw *hw);
+static int hns3_query_dev_fec_info(struct rte_eth_dev *dev);
 
 static void
 hns3_pf_disable_irq0(struct hns3_hw *hw)
@@ -2889,6 +2924,13 @@  hns3_get_capability(struct hns3_hw *hw)
 	    device_id == HNS3_DEV_ID_200G_RDMA)
 		hns3_set_bit(hw->capability, HNS3_DEV_SUPPORT_DCB_B, 1);
 
+	ret = hns3_query_dev_fec_info(eth_dev);
+	if (ret) {
+		PMD_INIT_LOG(ERR,
+			     "failed to query FEC information, ret = %d", ret);
+		return ret;
+	}
+
 	/* Get PCI revision id */
 	ret = rte_pci_read_config(pci_dev, &revision, HNS3_PCI_REVISION_ID_LEN,
 				  HNS3_PCI_REVISION_ID);
@@ -5446,6 +5488,10 @@  hns3_restore_conf(struct hns3_adapter *hns)
 	if (ret)
 		goto err_promisc;
 
+	ret = hns3_restore_fec(hw);
+	if (ret)
+		goto err_promisc;
+
 	if (hns->hw.adapter_state == HNS3_NIC_STARTED) {
 		ret = hns3_do_start(hns, false);
 		if (ret)
@@ -5524,6 +5570,313 @@  hns3_reset_service(void *param)
 		hns3_msix_process(hns, reset_level);
 }
 
+static unsigned int
+hns3_get_speed_capa_num(uint16_t device_id)
+{
+	unsigned int num;
+
+	switch (device_id) {
+	case HNS3_DEV_ID_25GE:
+	case HNS3_DEV_ID_25GE_RDMA:
+		num = 2;
+		break;
+	case HNS3_DEV_ID_100G_RDMA_MACSEC:
+	case HNS3_DEV_ID_200G_RDMA:
+		num = 1;
+		break;
+	default:
+		num = 0;
+		break;
+	}
+
+	return num;
+}
+
+static int
+hns3_get_speed_fec_capa(struct rte_eth_fec_capa *speed_fec_capa,
+			uint16_t device_id)
+{
+	switch (device_id) {
+	case HNS3_DEV_ID_25GE:
+	/* fallthrough */
+	case HNS3_DEV_ID_25GE_RDMA:
+		speed_fec_capa[0].speed = speed_fec_capa_tbl[1].speed;
+		speed_fec_capa[0].capa = speed_fec_capa_tbl[1].capa;
+
+		/* In HNS3 device, the 25G NIC is compatible with 10G rate */
+		speed_fec_capa[1].speed = speed_fec_capa_tbl[0].speed;
+		speed_fec_capa[1].capa = speed_fec_capa_tbl[0].capa;
+		break;
+	case HNS3_DEV_ID_100G_RDMA_MACSEC:
+		speed_fec_capa[0].speed = speed_fec_capa_tbl[4].speed;
+		speed_fec_capa[0].capa = speed_fec_capa_tbl[4].capa;
+		break;
+	case HNS3_DEV_ID_200G_RDMA:
+		speed_fec_capa[0].speed = speed_fec_capa_tbl[5].speed;
+		speed_fec_capa[0].capa = speed_fec_capa_tbl[5].capa;
+		break;
+	default:
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static int
+hns3_fec_get_capability(struct rte_eth_dev *dev,
+			struct rte_eth_fec_capa *speed_fec_capa,
+			unsigned int num)
+{
+	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+	uint16_t device_id = pci_dev->id.device_id;
+	unsigned int capa_num;
+	int ret;
+
+	capa_num = hns3_get_speed_capa_num(device_id);
+	if (capa_num == 0) {
+		hns3_err(hw, "device(0x%x) is not supported by hns3 PMD",
+			 device_id);
+		return -ENOTSUP;
+	}
+
+	if (speed_fec_capa == NULL || num < capa_num)
+		return capa_num;
+
+	ret = hns3_get_speed_fec_capa(speed_fec_capa, device_id);
+	if (ret)
+		return -ENOTSUP;
+
+	return capa_num;
+}
+
+static int
+get_current_fec_auto_state(struct hns3_hw *hw, uint8_t *state)
+{
+	struct hns3_config_fec_cmd *req;
+	struct hns3_cmd_desc desc;
+	int ret;
+
+	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CONFIG_FEC_MODE, true);
+	req = (struct hns3_config_fec_cmd *)desc.data;
+	ret = hns3_cmd_send(hw, &desc, 1);
+	if (ret) {
+		hns3_err(hw, "get current fec auto state failed, ret = %d",
+			 ret);
+		return ret;
+	}
+
+	*state = req->fec_mode & (1U << HNS3_MAC_CFG_FEC_AUTO_EN_B);
+	return 0;
+}
+
+static int
+hns3_fec_get(struct rte_eth_dev *dev, uint32_t *mode)
+{
+#define QUERY_ACTIVE_SPEED	1
+	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct hns3_sfp_speed_cmd *resp;
+	uint32_t tmp_mode;
+	uint8_t auto_state;
+	struct hns3_cmd_desc desc;
+	int ret;
+
+	/*
+	 * If link is down and AUTO is enabled, AUTO is returned, otherwise,
+	 * configured FEC mode is returned.
+	 * If link is up, current FEC mode is returned.
+	 */
+	if (hw->mac.link_status == ETH_LINK_DOWN) {
+		ret = get_current_fec_auto_state(hw, &auto_state);
+		if (ret)
+			return ret;
+
+		if (auto_state == 0x1) {
+			*mode = RTE_ETH_FEC_MODE_CAPA_MASK(AUTO);
+			return 0;
+		}
+	}
+
+	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_SFP_GET_SPEED, true);
+	resp = (struct hns3_sfp_speed_cmd *)desc.data;
+	resp->query_type = QUERY_ACTIVE_SPEED;
+
+	ret = hns3_cmd_send(hw, &desc, 1);
+	if (ret == -EOPNOTSUPP) {
+		hns3_err(hw, "IMP do not support get FEC, ret = %d", ret);
+		return ret;
+	} else if (ret) {
+		hns3_err(hw, "get FEC failed, ret = %d", ret);
+		return ret;
+	}
+
+	/*
+	 * FEC mode order defined in hns3 hardware is inconsistend with
+	 * that defined in the ethdev library. So the sequence needs
+	 * to be converted.
+	 */
+	switch (resp->active_fec) {
+	case HNS3_HW_FEC_MODE_NOFEC:
+		tmp_mode = RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC);
+		break;
+	case HNS3_HW_FEC_MODE_BASER:
+		tmp_mode = RTE_ETH_FEC_MODE_CAPA_MASK(BASER);
+		break;
+	case HNS3_HW_FEC_MODE_RS:
+		tmp_mode = RTE_ETH_FEC_MODE_CAPA_MASK(RS);
+		break;
+	default:
+		tmp_mode = RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC);
+		break;
+	}
+
+	*mode = tmp_mode;
+	return 0;
+}
+
+static int
+hns3_set_fec_hw(struct hns3_hw *hw, uint32_t mode)
+{
+	struct hns3_config_fec_cmd *req;
+	struct hns3_cmd_desc desc;
+	int ret;
+
+	hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CONFIG_FEC_MODE, false);
+
+	req = (struct hns3_config_fec_cmd *)desc.data;
+	switch (mode) {
+	case RTE_ETH_FEC_MODE_CAPA_MASK(NOFEC):
+		hns3_set_field(req->fec_mode, HNS3_MAC_CFG_FEC_MODE_M,
+				HNS3_MAC_CFG_FEC_MODE_S, HNS3_MAC_FEC_OFF);
+		break;
+	case RTE_ETH_FEC_MODE_CAPA_MASK(BASER):
+		hns3_set_field(req->fec_mode, HNS3_MAC_CFG_FEC_MODE_M,
+				HNS3_MAC_CFG_FEC_MODE_S, HNS3_MAC_FEC_BASER);
+		break;
+	case RTE_ETH_FEC_MODE_CAPA_MASK(RS):
+		hns3_set_field(req->fec_mode, HNS3_MAC_CFG_FEC_MODE_M,
+				HNS3_MAC_CFG_FEC_MODE_S, HNS3_MAC_FEC_RS);
+		break;
+	case RTE_ETH_FEC_MODE_CAPA_MASK(AUTO):
+		hns3_set_bit(req->fec_mode, HNS3_MAC_CFG_FEC_AUTO_EN_B, 1);
+		break;
+	default:
+		return 0;
+	}
+	ret = hns3_cmd_send(hw, &desc, 1);
+	if (ret)
+		hns3_err(hw, "set fec mode failed, ret = %d", ret);
+
+	return ret;
+}
+
+static uint32_t
+get_current_speed_fec_cap(struct hns3_hw *hw, struct rte_eth_fec_capa *fec_capa)
+{
+	struct hns3_mac *mac = &hw->mac;
+	uint32_t cur_capa;
+
+	switch (mac->link_speed) {
+	case ETH_SPEED_NUM_10G:
+		cur_capa = fec_capa[1].capa;
+		break;
+	case ETH_SPEED_NUM_25G:
+	case ETH_SPEED_NUM_100G:
+	case ETH_SPEED_NUM_200G:
+		cur_capa = fec_capa[0].capa;
+		break;
+	default:
+		cur_capa = 0;
+		break;
+	}
+
+	return cur_capa;
+}
+
+static bool
+is_fec_mode_one_bit_set(uint32_t mode)
+{
+	int cnt = 0;
+	uint8_t i;
+
+	for (i = 0; i < sizeof(mode); i++)
+		if (mode >> i & 0x1)
+			cnt++;
+
+	return cnt == 1 ? true : false;
+}
+
+static int
+hns3_fec_set(struct rte_eth_dev *dev, uint32_t mode)
+{
+#define FEC_CAPA_NUM 2
+	struct hns3_adapter *hns = dev->data->dev_private;
+	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(hns);
+	struct hns3_pf *pf = &hns->pf;
+
+	struct rte_eth_fec_capa fec_capa[FEC_CAPA_NUM];
+	uint32_t cur_capa;
+	uint32_t num = FEC_CAPA_NUM;
+	int ret;
+
+	ret = hns3_fec_get_capability(dev, fec_capa, num);
+	if (ret < 0)
+		return ret;
+
+	/* HNS3 PMD driver only support one bit set mode, e.g. 0x1, 0x4 */
+	if (!is_fec_mode_one_bit_set(mode))
+		hns3_err(hw, "FEC mode(0x%x) not supported in HNS3 PMD,"
+			     "FEC mode should be only one bit set", mode);
+
+	/*
+	 * Check whether the configured mode is within the FEC capability.
+	 * If not, the configured mode will not be supported.
+	 */
+	cur_capa = get_current_speed_fec_cap(hw, fec_capa);
+	if (!(cur_capa & mode)) {
+		hns3_err(hw, "unsupported FEC mode = 0x%x", mode);
+		return -EINVAL;
+	}
+
+	ret = hns3_set_fec_hw(hw, mode);
+	if (ret)
+		return ret;
+
+	pf->fec_mode = mode;
+	return 0;
+}
+
+static int
+hns3_restore_fec(struct hns3_hw *hw)
+{
+	struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
+	struct hns3_pf *pf = &hns->pf;
+	uint32_t mode = pf->fec_mode;
+	int ret;
+
+	ret = hns3_set_fec_hw(hw, mode);
+	if (ret)
+		hns3_err(hw, "restore fec mode(0x%x) failed, ret = %d",
+			 mode, ret);
+
+	return ret;
+}
+
+static int
+hns3_query_dev_fec_info(struct rte_eth_dev *dev)
+{
+	struct hns3_adapter *hns = dev->data->dev_private;
+	struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(hns);
+	struct hns3_pf *pf = &hns->pf;
+	int ret;
+
+	ret = hns3_fec_get(dev, &pf->fec_mode);
+	if (ret)
+		hns3_err(hw, "query device FEC info failed, ret = %d", ret);
+
+	return ret;
+}
+
 static const struct eth_dev_ops hns3_eth_dev_ops = {
 	.dev_configure      = hns3_dev_configure,
 	.dev_start          = hns3_dev_start,
@@ -5573,6 +5926,9 @@  static const struct eth_dev_ops hns3_eth_dev_ops = {
 	.get_reg                = hns3_get_regs,
 	.get_dcb_info           = hns3_get_dcb_info,
 	.dev_supported_ptypes_get = hns3_dev_supported_ptypes_get,
+	.fec_get_capability     = hns3_fec_get_capability,
+	.fec_get                = hns3_fec_get,
+	.fec_set                = hns3_fec_set,
 };
 
 static const struct hns3_reset_ops hns3_reset_ops = {
diff --git a/drivers/net/hns3/hns3_ethdev.h b/drivers/net/hns3/hns3_ethdev.h
index fd6a9f9..393b91f 100644
--- a/drivers/net/hns3/hns3_ethdev.h
+++ b/drivers/net/hns3/hns3_ethdev.h
@@ -620,6 +620,7 @@  struct hns3_pf {
 	struct hns3_err_msix_intr_stats abn_int_stats;
 
 	bool support_sfp_query;
+	uint32_t fec_mode; /* current FEC mode for ethdev */
 
 	struct hns3_vtag_cfg vtag_config;
 	LIST_HEAD(vlan_tbl, hns3_user_vlan_table) vlan_list;