Commit adf0759b authored by Jijie Shao's avatar Jijie Shao Committed by Hao Chen
Browse files

net: hibmcge: Add support for mac link exception handling feature

maillist inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IBU56J
CVE: NA

Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=e0306637e85d



----------------------------------------------------------------------

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: default avatarJijie Shao <shaojijie@huawei.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarHao Chen <chenhao418@huawei.com>
parent ba19a87b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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;
}
+10 −0
Original line number Diff line number Diff line
@@ -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 */
+9 −0
Original line number Diff line number Diff line
@@ -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);
+22 −0
Original line number Diff line number Diff line
@@ -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);
Loading