Commit ea0fbd05 authored by Gatis Peisenieks's avatar Gatis Peisenieks Committed by David S. Miller
Browse files

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: default avatarGatis Peisenieks <gatis@mikrotik.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b0390009
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -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,
+1 −0
Original line number Diff line number Diff line
@@ -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);
+7 −11
Original line number Diff line number Diff line
@@ -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);