net: hibmcge: Add support for mac link exception handling feature
authorJijie Shao <shaojijie@huawei.com>
Fri, 28 Feb 2025 11:54:09 +0000 (19:54 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 4 Mar 2025 12:45:33 +0000 (13:45 +0100)
If the rate changed frequently, the PHY link ok,
but the MAC link maybe fails.
As a result, the network port is unavailable.

According to the documents of the chip,
core_reset needs to do to fix the fault.

In hw_adjus_link(), the core_reset is added to try to
ensure that MAC link status is normal.
In addition, MAC link failure detection is added.
If the MAC link fails after core_reset, driver invokes
the phy_stop() and phy_start() to re-link.

Due to phydev->lock, re-link cannot be triggered
in adjust_link(). Therefore, this operation
is invoked in a scheduled task.

Signed-off-by: Jijie Shao <shaojijie@huawei.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c
drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h
drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h

index 4e4d33d2832a7fa7326c5a6ec9a30699ec26249e..8b943912184bcc0ee320d3861d951a9e0df06efb 100644 (file)
@@ -37,6 +37,7 @@ enum hbg_nic_state {
        HBG_NIC_STATE_RESETTING,
        HBG_NIC_STATE_RESET_FAIL,
        HBG_NIC_STATE_NEED_RESET, /* trigger a reset in scheduled task */
+       HBG_NIC_STATE_NP_LINK_FAIL,
 };
 
 enum hbg_reset_type {
@@ -82,6 +83,7 @@ enum hbg_hw_event_type {
        HBG_HW_EVENT_NONE = 0,
        HBG_HW_EVENT_INIT, /* driver is loading */
        HBG_HW_EVENT_RESET,
+       HBG_HW_EVENT_CORE_RESET,
 };
 
 struct hbg_dev_specs {
@@ -252,6 +254,8 @@ struct hbg_stats {
 
        u64 tx_timeout_cnt;
        u64 tx_dma_err_cnt;
+
+       u64 np_link_fail_cnt;
 };
 
 struct hbg_priv {
@@ -272,5 +276,6 @@ struct hbg_priv {
 };
 
 void hbg_err_reset_task_schedule(struct hbg_priv *priv);
+void hbg_np_link_fail_task_schedule(struct hbg_priv *priv);
 
 #endif
index 55ce90b4319aa7ea6cd1b6c9b721040c27ffe662..5e0ba4d5b08d28d472ed3a4a8bc47dd6e15f700d 100644 (file)
@@ -117,6 +117,8 @@ static int hbg_dbg_nic_state(struct seq_file *s, void *unused)
                   reset_type_str[priv->reset_type]);
        seq_printf(s, "need reset state: %s\n",
                   state_str_true_false(priv, HBG_NIC_STATE_NEED_RESET));
+       seq_printf(s, "np_link fail state: %s\n",
+                  state_str_true_false(priv, HBG_NIC_STATE_NP_LINK_FAIL));
 
        return 0;
 }
index e7798f213645021f69facde649e709e088f86ea0..74a18033b44412aae29d53519db87db5d6ec42cd 100644 (file)
@@ -213,10 +213,20 @@ void hbg_hw_fill_buffer(struct hbg_priv *priv, u32 buffer_dma_addr)
 
 void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
 {
+       hbg_hw_mac_enable(priv, HBG_STATUS_DISABLE);
+
        hbg_reg_write_field(priv, HBG_REG_PORT_MODE_ADDR,
                            HBG_REG_PORT_MODE_M, speed);
        hbg_reg_write_field(priv, HBG_REG_DUPLEX_TYPE_ADDR,
                            HBG_REG_DUPLEX_B, duplex);
+
+       hbg_hw_event_notify(priv, HBG_HW_EVENT_CORE_RESET);
+
+       hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
+
+       if (!hbg_reg_read_field(priv, HBG_REG_AN_NEG_STATE_ADDR,
+                               HBG_REG_AN_NEG_STATE_NP_LINK_OK_B))
+               hbg_np_link_fail_task_schedule(priv);
 }
 
 /* only support uc filter */
index 40f62db33feda792bbcc6e65f7b72c31958772a8..3b7955f57b4d6973c4badaac16ecd2ef518546e6 100644 (file)
@@ -286,6 +286,9 @@ static void hbg_service_task(struct work_struct *work)
        if (test_and_clear_bit(HBG_NIC_STATE_NEED_RESET, &priv->state))
                hbg_err_reset(priv);
 
+       if (test_and_clear_bit(HBG_NIC_STATE_NP_LINK_FAIL, &priv->state))
+               hbg_fix_np_link_fail(priv);
+
        /* The type of statistics register is u32,
         * To prevent the statistics register from overflowing,
         * the driver dumps the statistics every 30 seconds.
@@ -301,6 +304,12 @@ void hbg_err_reset_task_schedule(struct hbg_priv *priv)
        schedule_delayed_work(&priv->service_task, 0);
 }
 
+void hbg_np_link_fail_task_schedule(struct hbg_priv *priv)
+{
+       set_bit(HBG_NIC_STATE_NP_LINK_FAIL, &priv->state);
+       schedule_delayed_work(&priv->service_task, 0);
+}
+
 static void hbg_cancel_delayed_work_sync(void *data)
 {
        cancel_delayed_work_sync(data);
index db6bc4cfb971f2cf55c9001403b71a5b138544aa..f29a937ad0879f9ceac8363c2e075d1d00c8c39b 100644 (file)
@@ -17,6 +17,8 @@
 #define HBG_MDIO_OP_TIMEOUT_US         (1 * 1000 * 1000)
 #define HBG_MDIO_OP_INTERVAL_US                (5 * 1000)
 
+#define HBG_NP_LINK_FAIL_RETRY_TIMES   5
+
 static void hbg_mdio_set_command(struct hbg_mac *mac, u32 cmd)
 {
        hbg_reg_write(HBG_MAC_GET_PRIV(mac), HBG_REG_MDIO_COMMAND_ADDR, cmd);
@@ -127,6 +129,26 @@ static void hbg_flowctrl_cfg(struct hbg_priv *priv)
        hbg_hw_set_pause_enable(priv, tx_pause, rx_pause);
 }
 
+void hbg_fix_np_link_fail(struct hbg_priv *priv)
+{
+       struct device *dev = &priv->pdev->dev;
+
+       if (priv->stats.np_link_fail_cnt >= HBG_NP_LINK_FAIL_RETRY_TIMES) {
+               dev_err(dev, "failed to fix the MAC link status\n");
+               priv->stats.np_link_fail_cnt = 0;
+               return;
+       }
+
+       priv->stats.np_link_fail_cnt++;
+       dev_err(dev, "failed to link between MAC and PHY, try to fix...\n");
+
+       /* Replace phy_reset() with phy_stop() and phy_start(),
+        * as suggested by Andrew.
+        */
+       hbg_phy_stop(priv);
+       hbg_phy_start(priv);
+}
+
 static void hbg_phy_adjust_link(struct net_device *netdev)
 {
        struct hbg_priv *priv = netdev_priv(netdev);
index febd02a309c791f463cb0560464048cbc4b637fc..f3771c1bbd3470d64e37b0ccb2fad69595ae7712 100644 (file)
@@ -9,4 +9,6 @@
 int hbg_mdio_init(struct hbg_priv *priv);
 void hbg_phy_start(struct hbg_priv *priv);
 void hbg_phy_stop(struct hbg_priv *priv);
+void hbg_fix_np_link_fail(struct hbg_priv *priv);
+
 #endif
index c45450ab608c91a96cc59e3b9b3cc01269fc44c1..23c55a24b2848cfec4888856097be7f4399e7a6d 100644 (file)
@@ -54,6 +54,7 @@
 #define HBG_REG_PAUSE_ENABLE_RX_B              BIT(0)
 #define HBG_REG_PAUSE_ENABLE_TX_B              BIT(1)
 #define HBG_REG_AN_NEG_STATE_ADDR              (HBG_REG_SGMII_BASE + 0x0058)
+#define HBG_REG_AN_NEG_STATE_NP_LINK_OK_B      BIT(15)
 #define HBG_REG_TRANSMIT_CTRL_ADDR             (HBG_REG_SGMII_BASE + 0x0060)
 #define HBG_REG_TRANSMIT_CTRL_PAD_EN_B         BIT(7)
 #define HBG_REG_TRANSMIT_CTRL_CRC_ADD_B                BIT(6)