From 1a547f79f8d9b412dd4d643107b134e653500865 Mon Sep 17 00:00:00 2001
From: Huisong Li <lihuisong@huawei.com>
Date: Mon, 22 May 2023 21:17:37 +0800
Subject: net/hns3: add flow control autoneg for fiber port

[ upstream commit ab0ddd9a0ad5adcc96eabd1fe49edbd2809f202e ]

Support flow control autoneg for fiber and backplane port.
And it depends on the capability of firmware.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
---
 drivers/net/hns3/hns3_cmd.c    | 30 +++++++++++++++++-
 drivers/net/hns3/hns3_cmd.h    | 20 +++++++++++-
 drivers/net/hns3/hns3_dump.c   |  1 +
 drivers/net/hns3/hns3_ethdev.c | 58 ++++++++--------------------------
 drivers/net/hns3/hns3_ethdev.h |  1 +
 5 files changed, 63 insertions(+), 47 deletions(-)

diff --git a/drivers/net/hns3/hns3_cmd.c b/drivers/net/hns3/hns3_cmd.c
index d530650452..d0a3853656 100644
--- a/drivers/net/hns3/hns3_cmd.c
+++ b/drivers/net/hns3/hns3_cmd.c
@@ -428,7 +428,8 @@ hns3_get_caps_name(uint32_t caps_id)
 		{ HNS3_CAPS_UDP_TUNNEL_CSUM_B, "udp_tunnel_csum" },
 		{ HNS3_CAPS_RAS_IMP_B,         "ras_imp"         },
 		{ HNS3_CAPS_RXD_ADV_LAYOUT_B,  "rxd_adv_layout"  },
-		{ HNS3_CAPS_TM_B,              "tm_capability"   }
+		{ HNS3_CAPS_TM_B,              "tm_capability"   },
+		{ HNS3_CAPS_FC_AUTO_B,         "fc_autoneg"      }
 	};
 	uint32_t i;
 
@@ -510,6 +511,8 @@ hns3_parse_capability(struct hns3_hw *hw,
 		hns3_set_bit(hw->capability, HNS3_DEV_SUPPORT_RAS_IMP_B, 1);
 	if (hns3_get_bit(caps, HNS3_CAPS_TM_B))
 		hns3_set_bit(hw->capability, HNS3_DEV_SUPPORT_TM_B, 1);
+	if (hns3_get_bit(caps, HNS3_CAPS_FC_AUTO_B))
+		hns3_set_bit(hw->capability, HNS3_DEV_SUPPORT_FC_AUTO_B, 1);
 }
 
 static uint32_t
@@ -613,9 +616,31 @@ hns3_update_dev_lsc_cap(struct hns3_hw *hw, int fw_compact_cmd_result)
 	}
 }
 
+static void
+hns3_set_fc_autoneg_cap(struct hns3_adapter *hns, int fw_compact_cmd_result)
+{
+	struct hns3_hw *hw = &hns->hw;
+	struct hns3_mac *mac = &hw->mac;
+
+	if (mac->media_type == HNS3_MEDIA_TYPE_COPPER) {
+		hns->pf.support_fc_autoneg = true;
+		return;
+	}
+
+	/*
+	 * Flow control auto-negotiation requires the cooperation of the driver
+	 * and firmware.
+	 */
+	hns->pf.support_fc_autoneg = (hns3_dev_get_support(hw, FC_AUTO) &&
+					fw_compact_cmd_result == 0) ?
+					true : false;
+}
+
 static int
 hns3_apply_fw_compat_cmd_result(struct hns3_hw *hw, int result)
 {
+	struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw);
+
 	if (result != 0 && hns3_dev_get_support(hw, COPPER)) {
 		hns3_err(hw, "firmware fails to initialize the PHY, ret = %d.",
 			 result);
@@ -623,6 +648,7 @@ hns3_apply_fw_compat_cmd_result(struct hns3_hw *hw, int result)
 	}
 
 	hns3_update_dev_lsc_cap(hw, result);
+	hns3_set_fc_autoneg_cap(hns, result);
 
 	return 0;
 }
@@ -642,6 +668,8 @@ hns3_firmware_compat_config(struct hns3_hw *hw, bool is_init)
 		hns3_set_bit(compat, HNS3_NCSI_ERROR_REPORT_EN_B, 0);
 		if (hns3_dev_get_support(hw, COPPER))
 			hns3_set_bit(compat, HNS3_FIRMWARE_PHY_DRIVER_EN_B, 1);
+		if (hns3_dev_get_support(hw, FC_AUTO))
+			hns3_set_bit(compat, HNS3_MAC_FC_AUTONEG_EN_B, 1);
 	}
 	req->compat = rte_cpu_to_le_32(compat);
 
diff --git a/drivers/net/hns3/hns3_cmd.h b/drivers/net/hns3/hns3_cmd.h
index 4abe0f1d13..d78c1b401e 100644
--- a/drivers/net/hns3/hns3_cmd.h
+++ b/drivers/net/hns3/hns3_cmd.h
@@ -323,6 +323,7 @@ enum HNS3_CAPS_BITS {
 	HNS3_CAPS_RAS_IMP_B,
 	HNS3_CAPS_RXD_ADV_LAYOUT_B = 15,
 	HNS3_CAPS_TM_B = 19,
+	HNS3_CAPS_FC_AUTO_B = 30,
 };
 
 /* Capabilities of VF dependent on the PF */
@@ -666,6 +667,9 @@ enum hns3_promisc_type {
 #define HNS3_LINK_EVENT_REPORT_EN_B	0
 #define HNS3_NCSI_ERROR_REPORT_EN_B	1
 #define HNS3_FIRMWARE_PHY_DRIVER_EN_B	2
+
+#define HNS3_MAC_FC_AUTONEG_EN_B	6
+
 struct hns3_firmware_compat_cmd {
 	uint32_t compat;
 	uint8_t rsv[20];
@@ -680,6 +684,7 @@ struct hns3_firmware_compat_cmd {
 #define HNS3_PHY_LINK_MODE_AUTONEG_BIT		BIT(6)
 #define HNS3_PHY_LINK_MODE_PAUSE_BIT		BIT(13)
 #define HNS3_PHY_LINK_MODE_ASYM_PAUSE_BIT	BIT(14)
+#define HNS3_PHY_LINK_MODE_PAUSE_S		13
 
 #define HNS3_PHY_PARAM_CFG_BD_NUM	2
 struct hns3_phy_params_bd0_cmd {
@@ -789,6 +794,17 @@ struct hns3_sfp_type {
 #define HNS3_FIBER_LINK_SPEED_10M_BIT		BIT(7)
 #define HNS3_FIBER_LINK_SPEED_200G_BIT		BIT(8)
 
+/* Flags for pause status field */
+#define HNS3_FIBER_LOCAL_PAUSE_BIT	BIT(0)
+#define HNS3_FIBER_LOCAL_ASYM_PAUSE_BIT	BIT(1)
+#define HNS3_FIBER_LP_PAUSE_BIT		BIT(2)
+#define HNS3_FIBER_LP_ASYM_PAUSE_BIT	BIT(3)
+#define HNS3_FIBER_LOCAL_PAUSE_MASK	(HNS3_FIBER_LOCAL_PAUSE_BIT | \
+					 HNS3_FIBER_LOCAL_ASYM_PAUSE_BIT)
+#define HNS3_FIBER_LP_PAUSE_MASK	(HNS3_FIBER_LP_PAUSE_BIT | \
+					 HNS3_FIBER_LP_ASYM_PAUSE_BIT)
+#define HNS3_FIBER_LP_PAUSE_S		2
+
 struct hns3_sfp_info_cmd {
 	uint32_t sfp_speed;
 	uint8_t query_type; /* 0: sfp speed, 1: active */
@@ -798,7 +814,9 @@ struct hns3_sfp_info_cmd {
 	uint8_t autoneg_ability;
 	uint32_t supported_speed; /* speed supported by current media */
 	uint32_t module_type;
-	uint8_t rsv1[8];
+	uint8_t rsv[2];
+	uint8_t pause_status;
+	uint8_t rsv1[5];
 };
 
 #define HNS3_MAC_CFG_FEC_AUTO_EN_B	0
diff --git a/drivers/net/hns3/hns3_dump.c b/drivers/net/hns3/hns3_dump.c
index 7daa54ec87..7ecfca8497 100644
--- a/drivers/net/hns3/hns3_dump.c
+++ b/drivers/net/hns3/hns3_dump.c
@@ -104,6 +104,7 @@ hns3_get_dev_feature_capability(FILE *file, struct hns3_hw *hw)
 		{HNS3_DEV_SUPPORT_RAS_IMP_B, "RAS IMP"},
 		{HNS3_DEV_SUPPORT_TM_B, "TM"},
 		{HNS3_DEV_SUPPORT_VF_VLAN_FLT_MOD_B, "VF VLAN FILTER MOD"},
+		{HNS3_DEV_SUPPORT_FC_AUTO_B, "FC AUTO"}
 	};
 	uint32_t i;
 
diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c
index e01d5f76db..8a7f6cc7be 100644
--- a/drivers/net/hns3/hns3_ethdev.c
+++ b/drivers/net/hns3/hns3_ethdev.c
@@ -3975,6 +3975,7 @@ static int
 hns3_get_sfp_info(struct hns3_hw *hw, struct hns3_mac *mac_info)
 {
 	struct hns3_sfp_info_cmd *resp;
+	uint32_t local_pause, lp_pause;
 	struct hns3_cmd_desc desc;
 	int ret;
 
@@ -4011,6 +4012,13 @@ hns3_get_sfp_info(struct hns3_hw *hw, struct hns3_mac *mac_info)
 		mac_info->support_autoneg = resp->autoneg_ability;
 		mac_info->link_autoneg = (resp->autoneg == 0) ? RTE_ETH_LINK_FIXED
 					: RTE_ETH_LINK_AUTONEG;
+		local_pause = resp->pause_status & HNS3_FIBER_LOCAL_PAUSE_MASK;
+		lp_pause = (resp->pause_status & HNS3_FIBER_LP_PAUSE_MASK) >>
+						HNS3_FIBER_LP_PAUSE_S;
+		mac_info->advertising =
+				local_pause << HNS3_PHY_LINK_MODE_PAUSE_S;
+		mac_info->lp_advertising =
+				lp_pause << HNS3_PHY_LINK_MODE_PAUSE_S;
 	} else {
 		mac_info->query_type = HNS3_DEFAULT_QUERY;
 	}
@@ -4093,6 +4101,8 @@ hns3_update_fiber_link_info(struct hns3_hw *hw)
 		mac->supported_speed = mac_info.supported_speed;
 		mac->support_autoneg = mac_info.support_autoneg;
 		mac->link_autoneg = mac_info.link_autoneg;
+		mac->advertising = mac_info.advertising;
+		mac->lp_advertising = mac_info.lp_advertising;
 
 		return 0;
 	}
@@ -4495,24 +4505,6 @@ hns3_get_port_supported_speed(struct rte_eth_dev *eth_dev)
 	return 0;
 }
 
-static void
-hns3_get_fc_autoneg_capability(struct hns3_adapter *hns)
-{
-	struct hns3_mac *mac = &hns->hw.mac;
-
-	if (mac->media_type == HNS3_MEDIA_TYPE_COPPER) {
-		hns->pf.support_fc_autoneg = true;
-		return;
-	}
-
-	/*
-	 * Flow control auto-negotiation requires the cooperation of the driver
-	 * and firmware. Currently, the optical port does not support flow
-	 * control auto-negotiation.
-	 */
-	hns->pf.support_fc_autoneg = false;
-}
-
 static int
 hns3_init_pf(struct rte_eth_dev *eth_dev)
 {
@@ -4615,8 +4607,6 @@ hns3_init_pf(struct rte_eth_dev *eth_dev)
 		goto err_supported_speed;
 	}
 
-	hns3_get_fc_autoneg_capability(hns);
-
 	hns3_tm_conf_init(eth_dev);
 
 	return 0;
@@ -5181,8 +5171,7 @@ hns3_dev_close(struct rte_eth_dev *eth_dev)
 }
 
 static void
-hns3_get_autoneg_rxtx_pause_copper(struct hns3_hw *hw, bool *rx_pause,
-				   bool *tx_pause)
+hns3_get_autoneg_rxtx_pause(struct hns3_hw *hw, bool *rx_pause, bool *tx_pause)
 {
 	struct hns3_mac *mac = &hw->mac;
 	uint32_t advertising = mac->advertising;
@@ -5193,8 +5182,7 @@ hns3_get_autoneg_rxtx_pause_copper(struct hns3_hw *hw, bool *rx_pause,
 	if (advertising & lp_advertising & HNS3_PHY_LINK_MODE_PAUSE_BIT) {
 		*rx_pause = true;
 		*tx_pause = true;
-	} else if (advertising & lp_advertising &
-		   HNS3_PHY_LINK_MODE_ASYM_PAUSE_BIT) {
+	} else if (advertising & lp_advertising & HNS3_PHY_LINK_MODE_ASYM_PAUSE_BIT) {
 		if (advertising & HNS3_PHY_LINK_MODE_PAUSE_BIT)
 			*rx_pause = true;
 		else if (lp_advertising & HNS3_PHY_LINK_MODE_PAUSE_BIT)
@@ -5209,26 +5197,7 @@ hns3_get_autoneg_fc_mode(struct hns3_hw *hw)
 	bool rx_pause = false;
 	bool tx_pause = false;
 
-	switch (hw->mac.media_type) {
-	case HNS3_MEDIA_TYPE_COPPER:
-		hns3_get_autoneg_rxtx_pause_copper(hw, &rx_pause, &tx_pause);
-		break;
-
-	/*
-	 * Flow control auto-negotiation is not supported for fiber and
-	 * backplane media type.
-	 */
-	case HNS3_MEDIA_TYPE_FIBER:
-	case HNS3_MEDIA_TYPE_BACKPLANE:
-		hns3_err(hw, "autoneg FC mode can't be obtained, but flow control auto-negotiation is enabled.");
-		current_mode = hw->requested_fc_mode;
-		goto out;
-	default:
-		hns3_err(hw, "autoneg FC mode can't be obtained for unknown media type(%u).",
-			 hw->mac.media_type);
-		current_mode = HNS3_FC_NONE;
-		goto out;
-	}
+	hns3_get_autoneg_rxtx_pause(hw, &rx_pause, &tx_pause);
 
 	if (rx_pause && tx_pause)
 		current_mode = HNS3_FC_FULL;
@@ -5239,7 +5208,6 @@ hns3_get_autoneg_fc_mode(struct hns3_hw *hw)
 	else
 		current_mode = HNS3_FC_NONE;
 
-out:
 	return current_mode;
 }
 
diff --git a/drivers/net/hns3/hns3_ethdev.h b/drivers/net/hns3/hns3_ethdev.h
index 8268dba788..88146f5054 100644
--- a/drivers/net/hns3/hns3_ethdev.h
+++ b/drivers/net/hns3/hns3_ethdev.h
@@ -892,6 +892,7 @@ enum hns3_dev_cap {
 	HNS3_DEV_SUPPORT_RAS_IMP_B,
 	HNS3_DEV_SUPPORT_TM_B,
 	HNS3_DEV_SUPPORT_VF_VLAN_FLT_MOD_B,
+	HNS3_DEV_SUPPORT_FC_AUTO_B,
 };
 
 #define hns3_dev_get_support(hw, _name) \
-- 
2.23.0

