Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Yuiko Oshino and committed by
David S. Miller
d461e3da a6592547

+62
+62
drivers/net/usb/smsc75xx.c
··· 82 82 module_param(turbo_mode, bool, 0644); 83 83 MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); 84 84 85 + static int smsc75xx_link_ok_nopm(struct usbnet *dev); 86 + static int smsc75xx_phy_gig_workaround(struct usbnet *dev); 87 + 85 88 static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, 86 89 u32 *data, int in_pm) 87 90 { ··· 855 852 return -EIO; 856 853 } 857 854 855 + /* phy workaround for gig link */ 856 + smsc75xx_phy_gig_workaround(dev); 857 + 858 858 smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, 859 859 ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | 860 860 ADVERTISE_PAUSE_ASYM); ··· 991 985 992 986 netdev_warn(dev->net, "timeout waiting for device ready\n"); 993 987 return -EIO; 988 + } 989 + 990 + static int smsc75xx_phy_gig_workaround(struct usbnet *dev) 991 + { 992 + struct mii_if_info *mii = &dev->mii; 993 + int ret = 0, timeout = 0; 994 + u32 buf, link_up = 0; 995 + 996 + /* Set the phy in Gig loopback */ 997 + smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040); 998 + 999 + /* Wait for the link up */ 1000 + do { 1001 + link_up = smsc75xx_link_ok_nopm(dev); 1002 + usleep_range(10000, 20000); 1003 + timeout++; 1004 + } while ((!link_up) && (timeout < 1000)); 1005 + 1006 + if (timeout >= 1000) { 1007 + netdev_warn(dev->net, "Timeout waiting for PHY link up\n"); 1008 + return -EIO; 1009 + } 1010 + 1011 + /* phy reset */ 1012 + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 1013 + if (ret < 0) { 1014 + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); 1015 + return ret; 1016 + } 1017 + 1018 + buf |= PMT_CTL_PHY_RST; 1019 + 1020 + ret = smsc75xx_write_reg(dev, PMT_CTL, buf); 1021 + if (ret < 0) { 1022 + netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); 1023 + return ret; 1024 + } 1025 + 1026 + timeout = 0; 1027 + do { 1028 + usleep_range(10000, 20000); 1029 + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); 1030 + if (ret < 0) { 1031 + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", 1032 + ret); 1033 + return ret; 1034 + } 1035 + timeout++; 1036 + } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); 1037 + 1038 + if (timeout >= 100) { 1039 + netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); 1040 + return -EIO; 1041 + } 1042 + 1043 + return 0; 994 1044 } 995 1045 996 1046 static int smsc75xx_reset(struct usbnet *dev)