diff options
author | Gatis Peisenieks <gatis@mikrotik.com> | 2021-05-13 14:43:26 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2021-05-14 01:48:10 +0300 |
commit | ea0fbd05d7bd3298290d3579a837311ee5ceaf18 (patch) | |
tree | 562621c7525aece0f397a1a3d3d898e9606a8a3b | |
parent | b0390009502b0c635f9ddc26a16025268f6c6211 (diff) | |
download | linux-ea0fbd05d7bd3298290d3579a837311ee5ceaf18.tar.xz |
atl1c: improve link detection reliability on Mikrotik 10/25G NIC
Mikrotik 10/25G NIC emulates the MDIO accesses, but the emulation is
not 100% reliable - the MDIO ops occasionally can timeout.
This adds a reliable way of detecting link on Mikrotik 10/25G NIC.
Signed-off-by: Gatis Peisenieks <gatis@mikrotik.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/atheros/atl1c/atl1c_hw.c | 26 | ||||
-rw-r--r-- | drivers/net/ethernet/atheros/atl1c/atl1c_hw.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 18 |
3 files changed, 27 insertions, 18 deletions
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c index ddb9442416cd..7dff20350865 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c @@ -636,6 +636,23 @@ int atl1c_phy_init(struct atl1c_hw *hw) return 0; } +bool atl1c_get_link_status(struct atl1c_hw *hw) +{ + u16 phy_data; + + if (hw->nic_type == athr_mt) { + u32 spd; + + AT_READ_REG(hw, REG_MT_SPEED, &spd); + return !!spd; + } + + /* MII_BMSR must be read twice */ + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + return !!(phy_data & BMSR_LSTATUS); +} + /* * Detects the current speed and duplex settings of the hardware. * @@ -695,15 +712,12 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) int ret = 0; u16 autoneg_advertised = ADVERTISED_10baseT_Half; u16 save_autoneg_advertised; - u16 phy_data; u16 mii_lpa_data; u16 speed = SPEED_0; u16 duplex = FULL_DUPLEX; int i; - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); - if (phy_data & BMSR_LSTATUS) { + if (atl1c_get_link_status(hw)) { atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data); if (mii_lpa_data & LPA_10FULL) autoneg_advertised = ADVERTISED_10baseT_Full; @@ -726,9 +740,7 @@ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) if (mii_lpa_data) { for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { mdelay(100); - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); - if (phy_data & BMSR_LSTATUS) { + if (atl1c_get_link_status(hw)) { if (atl1c_get_speed_and_duplex(hw, &speed, &duplex) != 0) dev_dbg(&pdev->dev, diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h index 73cbc049a63e..c263b326cec5 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.h @@ -26,6 +26,7 @@ void atl1c_phy_disable(struct atl1c_hw *hw); void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr); int atl1c_phy_reset(struct atl1c_hw *hw); int atl1c_read_mac_addr(struct atl1c_hw *hw); +bool atl1c_get_link_status(struct atl1c_hw *hw); int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex); u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr); void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value); diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index 9693da5028cf..740127a6a21d 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -232,15 +232,14 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter) struct pci_dev *pdev = adapter->pdev; int err; unsigned long flags; - u16 speed, duplex, phy_data; + u16 speed, duplex; + bool link; spin_lock_irqsave(&adapter->mdio_lock, flags); - /* MII_BMSR must read twise */ - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); - atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); + link = atl1c_get_link_status(hw); spin_unlock_irqrestore(&adapter->mdio_lock, flags); - if ((phy_data & BMSR_LSTATUS) == 0) { + if (!link) { /* link down */ netif_carrier_off(netdev); hw->hibernate = true; @@ -284,16 +283,13 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; - u16 phy_data; - u16 link_up; + bool link; spin_lock(&adapter->mdio_lock); - atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); - atl1c_read_phy_reg(&adapter->hw, MII_BMSR, &phy_data); + link = atl1c_get_link_status(&adapter->hw); spin_unlock(&adapter->mdio_lock); - link_up = phy_data & BMSR_LSTATUS; /* notify upper layer link down ASAP */ - if (!link_up) { + if (!link) { if (netif_carrier_ok(netdev)) { /* old link state: Up */ netif_carrier_off(netdev); |