smsc75xx: Add workaround for gigabit link up hardware errata.
[ 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>
This commit is contained in:
parent
6e7084e2c3
commit
5996929435
@ -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]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user