phy_stop() powers down the PHY when the interface is closed. The driver
leaves MAC receive/transmit enabled on link-down and enables them again
during initialization before resuming the PHY. The MTX-1 can hang when
the MAC runs while its MII clocks are absent or still recovering.
Start the PHY before initializing the MAC, but leave receive/transmit
stopped while the PHY reports no link. The duplex update enables them
when the link comes up. Stop the MAC in the link-down callback before
phylib suspends the PHY, and stop the PHY if MAC initialization fails.
Fixes: be9dad1f9f26 ("net: phy: suspend phydev when going to HALTED")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/ethernet/amd/au1000_eth.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
index 8c2dbf72652e..c616ce616541 100644
--- a/drivers/net/ethernet/amd/au1000_eth.c
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -458,7 +458,8 @@ au1000_adjust_link(struct net_device *dev)
/* link state changed */
if (!phydev->link) {
- /* link went down */
+ /* Stop the MAC before the PHY can remove its clocks. */
+ au1000_hard_stop(dev);
aup->old_speed = 0;
aup->old_duplex = -1;
}@@ -727,7 +728,7 @@ static int au1000_init(struct net_device *dev)
wmb(); /* drain writebuffer */
- control = MAC_RX_ENABLE | MAC_TX_ENABLE;
+ control = 0;
#ifndef CONFIG_CPU_LITTLE_ENDIAN
control |= MAC_BIG_ENDIAN;
#endif
@@ -740,6 +741,10 @@ static int au1000_init(struct net_device *dev)
control |= MAC_FULL_DUPLEX;
}
+ /* The PHY may still be restoring its MII clocks after resume. */
+ if (!dev->phydev || dev->phydev->link)
+ control |= MAC_RX_ENABLE | MAC_TX_ENABLE;
+
writel(control, &aup->mac->control);
writel(0x8100, &aup->mac->vlan1_tag); /* activate vlan support */
wmb(); /* drain writebuffer */
@@ -928,16 +933,18 @@ static int au1000_open(struct net_device *dev)
return retval;
}
+ if (dev->phydev)
+ phy_start(dev->phydev);
+
retval = au1000_init(dev);
if (retval) {
netdev_err(dev, "error in au1000_init\n");
+ if (dev->phydev)
+ phy_stop(dev->phydev);
free_irq(dev->irq, dev);
return retval;
}
- if (dev->phydev)
- phy_start(dev->phydev);
-
netif_start_queue(dev);
netif_dbg(aup, drv, dev, "open: Initialization done.\n");--
2.53.0