Skip to content

Commit

Permalink
net: hibmcge: Add support for mac link exception handling feature
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jijie Shao authored and Paolo Abeni committed Mar 4, 2025
1 parent fd394a3 commit e030663
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 0 deletions.
5 changes: 5 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -252,6 +254,8 @@ struct hbg_stats {

u64 tx_timeout_cnt;
u64 tx_dma_err_cnt;

u64 np_link_fail_cnt;
};

struct hbg_priv {
Expand All @@ -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
2 changes: 2 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 10 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit e030663

Please sign in to comment.