mirror of
https://github.com/torvalds/linux.git
synced 2025-08-15 22:21:42 +02:00
net: hibmcge: Add support for mac link exception handling feature
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>
This commit is contained in:
parent
fd394a334b
commit
e0306637e8
7 changed files with 51 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue