| From foo@baz Wed Aug 22 09:42:09 CEST 2018 |
| From: Yuiko Oshino <yuiko.oshino@microchip.com> |
| Date: Tue, 3 Jul 2018 11:21:46 -0400 |
| Subject: smsc75xx: Add workaround for gigabit link up hardware errata. |
| |
| From: Yuiko Oshino <yuiko.oshino@microchip.com> |
| |
| [ Upstream commit d461e3da905332189aad546b2ad9adbe6071c7cc ] |
| |
| In certain conditions, the device may not be able to link in gigabit mode. This software workaround ensures that the device will not enter the failure state. |
| |
| Fixes: d0cad871703b898a442e4049c532ec39168e5b57 ("SMSC75XX USB 2.0 Gigabit Ethernet Devices") |
| Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/usb/smsc75xx.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 62 insertions(+) |
| |
| --- a/drivers/net/usb/smsc75xx.c |
| +++ b/drivers/net/usb/smsc75xx.c |
| @@ -82,6 +82,9 @@ static bool turbo_mode = true; |
| module_param(turbo_mode, bool, 0644); |
| MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); |
| |
| +static int smsc75xx_link_ok_nopm(struct usbnet *dev); |
| +static int smsc75xx_phy_gig_workaround(struct usbnet *dev); |
| + |
| static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, |
| u32 *data, int in_pm) |
| { |
| @@ -852,6 +855,9 @@ static int smsc75xx_phy_initialize(struc |
| return -EIO; |
| } |
| |
| + /* phy workaround for gig link */ |
| + smsc75xx_phy_gig_workaround(dev); |
| + |
| smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, |
| ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | |
| ADVERTISE_PAUSE_ASYM); |
| @@ -990,6 +996,62 @@ static int smsc75xx_wait_ready(struct us |
| return -EIO; |
| } |
| |
| +static int smsc75xx_phy_gig_workaround(struct usbnet *dev) |
| +{ |
| + struct mii_if_info *mii = &dev->mii; |
| + int ret = 0, timeout = 0; |
| + u32 buf, link_up = 0; |
| + |
| + /* Set the phy in Gig loopback */ |
| + smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040); |
| + |
| + /* Wait for the link up */ |
| + do { |
| + link_up = smsc75xx_link_ok_nopm(dev); |
| + usleep_range(10000, 20000); |
| + timeout++; |
| + } while ((!link_up) && (timeout < 1000)); |
| + |
| + if (timeout >= 1000) { |
| + netdev_warn(dev->net, "Timeout waiting for PHY link up\n"); |
| + return -EIO; |
| + } |
| + |
| + /* phy reset */ |
| + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); |
| + if (ret < 0) { |
| + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); |
| + return ret; |
| + } |
| + |
| + buf |= PMT_CTL_PHY_RST; |
| + |
| + ret = smsc75xx_write_reg(dev, PMT_CTL, buf); |
| + if (ret < 0) { |
| + netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); |
| + return ret; |
| + } |
| + |
| + timeout = 0; |
| + do { |
| + usleep_range(10000, 20000); |
| + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); |
| + if (ret < 0) { |
| + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", |
| + ret); |
| + return ret; |
| + } |
| + timeout++; |
| + } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); |
| + |
| + if (timeout >= 100) { |
| + netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); |
| + return -EIO; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| static int smsc75xx_reset(struct usbnet *dev) |
| { |
| struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); |