diff options
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 7a6a1fe79309..05553d252446 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c | |||
@@ -82,6 +82,9 @@ static bool turbo_mode = true; | |||
82 | module_param(turbo_mode, bool, 0644); | 82 | module_param(turbo_mode, bool, 0644); |
83 | MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); | 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 | static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, | 88 | static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, |
86 | u32 *data, int in_pm) | 89 | u32 *data, int in_pm) |
87 | { | 90 | { |
@@ -852,6 +855,9 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) | |||
852 | return -EIO; | 855 | return -EIO; |
853 | } | 856 | } |
854 | 857 | ||
858 | /* phy workaround for gig link */ | ||
859 | smsc75xx_phy_gig_workaround(dev); | ||
860 | |||
855 | smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, | 861 | smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, |
856 | ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | | 862 | ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | |
857 | ADVERTISE_PAUSE_ASYM); | 863 | ADVERTISE_PAUSE_ASYM); |
@@ -987,6 +993,62 @@ static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) | |||
987 | return -EIO; | 993 | return -EIO; |
988 | } | 994 | } |
989 | 995 | ||
996 | static int smsc75xx_phy_gig_workaround(struct usbnet *dev) | ||
997 | { | ||
998 | struct mii_if_info *mii = &dev->mii; | ||
999 | int ret = 0, timeout = 0; | ||
1000 | u32 buf, link_up = 0; | ||
1001 | |||
1002 | /* Set the phy in Gig loopback */ | ||
1003 | smsc75xx_mdio_write(dev->net, mii->phy_id, MII_BMCR, 0x4040); | ||
1004 | |||
1005 | /* Wait for the link up */ | ||
1006 | do { | ||
1007 | link_up = smsc75xx_link_ok_nopm(dev); | ||
1008 | usleep_range(10000, 20000); | ||
1009 | timeout++; | ||
1010 | } while ((!link_up) && (timeout < 1000)); | ||
1011 | |||
1012 | if (timeout >= 1000) { | ||
1013 | netdev_warn(dev->net, "Timeout waiting for PHY link up\n"); | ||
1014 | return -EIO; | ||
1015 | } | ||
1016 | |||
1017 | /* phy reset */ | ||
1018 | ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); | ||
1019 | if (ret < 0) { | ||
1020 | netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); | ||
1021 | return ret; | ||
1022 | } | ||
1023 | |||
1024 | buf |= PMT_CTL_PHY_RST; | ||
1025 | |||
1026 | ret = smsc75xx_write_reg(dev, PMT_CTL, buf); | ||
1027 | if (ret < 0) { | ||
1028 | netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); | ||
1029 | return ret; | ||
1030 | } | ||
1031 | |||
1032 | timeout = 0; | ||
1033 | do { | ||
1034 | usleep_range(10000, 20000); | ||
1035 | ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); | ||
1036 | if (ret < 0) { | ||
1037 | netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", | ||
1038 | ret); | ||
1039 | return ret; | ||
1040 | } | ||
1041 | timeout++; | ||
1042 | } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); | ||
1043 | |||
1044 | if (timeout >= 100) { | ||
1045 | netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); | ||
1046 | return -EIO; | ||
1047 | } | ||
1048 | |||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
990 | static int smsc75xx_reset(struct usbnet *dev) | 1052 | static int smsc75xx_reset(struct usbnet *dev) |
991 | { | 1053 | { |
992 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); | 1054 | struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); |