[PATCH net-next 5/7] net: hibmcge: Add mac link exception handling feature in this module
From: Jijie Shao <shaojijie@huawei.com>
Date: 2025-02-13 04:03:23
Also in:
lkml
Subsystem:
hisilicon network hibmcge driver, hisilicon network subsystem driver, networking drivers, the rest · Maintainers:
Jijie Shao, Jian Shen, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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, the PHY will reset and re-link up to five times. Signed-off-by: Jijie Shao <shaojijie@huawei.com> --- .../ethernet/hisilicon/hibmcge/hbg_common.h | 4 ++- .../ethernet/hisilicon/hibmcge/hbg_debugfs.c | 2 ++ .../net/ethernet/hisilicon/hibmcge/hbg_err.c | 26 +++++++++++++++-- .../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 29 ++++++++++++++++++- .../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 2 +- .../net/ethernet/hisilicon/hibmcge/hbg_mdio.c | 13 ++++++++- .../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 1 + 7 files changed, 70 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index f45e899c62d8..e942a1e6f859 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h@@ -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,7 +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, HBG_HW_EVENT_SERDES_LOOPBACK_ENABLE = 4, HBG_HW_EVENT_SERDES_LOOPBACK_DISABLE = 5, };
@@ -257,6 +258,7 @@ struct hbg_stats { u64 tx_dma_err_cnt; u64 self_test_rx_pkt_cnt; + u64 np_link_fail_cnt; }; typedef void (*self_test_pkt_recv)(struct net_device *ndev,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c
index 55ce90b4319a..5e0ba4d5b08d 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c@@ -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; }
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
index 4e8cb66f601c..01c4b246d040 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c@@ -46,6 +46,16 @@ int hbg_rebuild(struct hbg_priv *priv) return 0; } +int hbg_reset_phy(struct hbg_priv *priv) +{ + struct phy_device *phydev = priv->mac.phydev; + + if (phydev->drv->soft_reset) + return phydev->drv->soft_reset(phydev); + + return genphy_soft_reset(phydev); +} + static int hbg_reset_prepare(struct hbg_priv *priv, enum hbg_reset_type type) { int ret;
@@ -61,12 +71,22 @@ static int hbg_reset_prepare(struct hbg_priv *priv, enum hbg_reset_type type) priv->reset_type = type; set_bit(HBG_NIC_STATE_RESETTING, &priv->state); clear_bit(HBG_NIC_STATE_RESET_FAIL, &priv->state); - ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_RESET); + + ret = hbg_reset_phy(priv); if (ret) { - set_bit(HBG_NIC_STATE_RESET_FAIL, &priv->state); - clear_bit(HBG_NIC_STATE_RESETTING, &priv->state); + dev_err(&priv->pdev->dev, "failed to reset phy\n"); + goto reset_fail; } + ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_RESET); + if (ret) + goto reset_fail; + + return 0; + +reset_fail: + set_bit(HBG_NIC_STATE_RESET_FAIL, &priv->state); + clear_bit(HBG_NIC_STATE_RESETTING, &priv->state); return ret; }
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index d978535e06a0..cb145f524e15 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c@@ -17,6 +17,7 @@ */ #define HBG_ENDIAN_CTRL_LE_DATA_BE 0x0 #define HBG_PCU_FRAME_LEN_PLUS 4 +#define HBG_LINK_FAIL_RETRY_TIMES 5 static bool hbg_hw_spec_is_valid(struct hbg_priv *priv) {
@@ -217,12 +218,38 @@ void hbg_hw_fill_buffer(struct hbg_priv *priv, u32 buffer_dma_addr) hbg_reg_write(priv, HBG_REG_RX_CFF_ADDR_ADDR, buffer_dma_addr); } -void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex) +int hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex) { + struct hbg_stats *stats = &priv->stats; + int ret; + + clear_bit(HBG_NIC_STATE_NP_LINK_FAIL, &priv->state); + 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); + + ret = hbg_hw_event_notify(priv, HBG_HW_EVENT_CORE_RESET); + if (ret) + return ret; + + 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)) { + set_bit(HBG_NIC_STATE_NP_LINK_FAIL, &priv->state); + + stats->np_link_fail_cnt++; + if (!(stats->np_link_fail_cnt % HBG_LINK_FAIL_RETRY_TIMES)) + return -EFAULT; + + return -ENOLINK; + } + + stats->np_link_fail_cnt = 0; + return 0; } /* only support uc filter */
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index fc216fcfae06..4c9bef0348b4 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h@@ -44,7 +44,7 @@ static inline void hbg_reg_write64(struct hbg_priv *priv, u32 addr, u64 value) int hbg_hw_event_notify(struct hbg_priv *priv, enum hbg_hw_event_type event_type); int hbg_hw_init(struct hbg_priv *priv); -void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex); +int hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex); u32 hbg_hw_get_irq_status(struct hbg_priv *priv); void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask); bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask);
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
index db6bc4cfb971..8de6d57bd5f3 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c@@ -3,6 +3,7 @@ #include <linux/phy.h> #include "hbg_common.h" +#include "hbg_err.h" #include "hbg_hw.h" #include "hbg_mdio.h" #include "hbg_reg.h"
@@ -132,6 +133,7 @@ static void hbg_phy_adjust_link(struct net_device *netdev) struct hbg_priv *priv = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; u32 speed; + int ret; if (phydev->link != priv->mac.link_status) { if (phydev->link) {
@@ -152,7 +154,16 @@ static void hbg_phy_adjust_link(struct net_device *netdev) priv->mac.speed = speed; priv->mac.duplex = phydev->duplex; priv->mac.autoneg = phydev->autoneg; - hbg_hw_adjust_link(priv, speed, phydev->duplex); + ret = hbg_hw_adjust_link(priv, speed, phydev->duplex); + if (ret == -ENOLINK) { + dev_err(&priv->pdev->dev, + "failed to link between MAC and PHY\n"); + hbg_reset_phy(priv); + } else if (ret == -EFAULT) { + dev_err(&priv->pdev->dev, + "failed to fix the MAC link status\n"); + } + hbg_flowctrl_cfg(priv); }
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index fe146c2c5e80..9f9346b7f1be 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h@@ -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)
--
2.33.0