An exception is sometimes seen when the link speed is changed from auto-negotiation to a fixed speed, or vice versa. The exception occurs when the MAC is reset (due to the link speed change) at the same time as the PHY state machine is accessing a PHY register.
Add serialisation to the MAC reset to eliminate the race condition. Signed-off-by: John Efstathiades <john.efstathia...@pebblebay.com> --- drivers/net/usb/lan78xx.c | 54 ++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 9bd21d17d6f1..0a6f4765f595 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1342,6 +1342,52 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev); +static int lan78xx_mac_reset(struct lan78xx_net *dev) +{ + u32 val; + int ret; + unsigned long start_time = jiffies; + + mutex_lock(&dev->phy_mutex); + + /* confirm MII not busy */ + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + + ret = lan78xx_read_reg(dev, MAC_CR, &val); + if (unlikely(ret < 0)) { + ret = -EIO; + goto done; + } + + val |= MAC_CR_RST_; + ret = lan78xx_write_reg(dev, MAC_CR, val); + if (unlikely(ret < 0)) { + ret = -EIO; + goto done; + } + + /* poll for completion */ + + do { + ret = lan78xx_read_reg(dev, MAC_CR, &val); + if (unlikely(ret < 0)) { + ret = -EIO; + break; + } + if (!(val & MAC_CR_RST_)) { + ret = 0; + break; + } + } while (!time_after(jiffies, start_time + HZ)); + +done: + mutex_unlock(&dev->phy_mutex); + + return ret; +} + static int lan78xx_link_reset(struct lan78xx_net *dev) { struct phy_device *phydev = dev->net->phydev; @@ -1360,12 +1406,8 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) dev->link_on = false; /* reset MAC */ - ret = lan78xx_read_reg(dev, MAC_CR, &buf); - if (unlikely(ret < 0)) - return -EIO; - buf |= MAC_CR_RST_; - ret = lan78xx_write_reg(dev, MAC_CR, buf); - if (unlikely(ret < 0)) + ret = lan78xx_mac_reset(dev); + if (unlikely(ret != 0)) return -EIO; del_timer(&dev->stat_monitor); -- 2.17.1