smsc75xx: Add workaround for gigabit link up hardware errata.

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: d0cad87170 ("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>
This commit is contained in:
Yuiko Oshino 2018-07-03 11:21:46 -04:00 committed by David S. Miller
parent a659254755
commit d461e3da90

View file

@ -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(struct usbnet *dev)
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);
@ -987,6 +993,62 @@ static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
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]);