diff options
| author | Christoph Fritz <chf.fritz@googlemail.com> | 2016-05-25 22:06:47 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-05-31 17:22:23 -0400 |
| commit | d69d169493463e6b1da9a1965d35126e479aa27f (patch) | |
| tree | 6853221cbea62c9d745b17e5581e057b3846d344 /drivers | |
| parent | f00e35e259948b995aa1f3ee7fddb05f34a50157 (diff) | |
usbnet: smsc95xx: fix link detection for disabled autonegotiation
To detect link status up/down for connections where autonegotiation is
explicitly disabled, we don't get an irq but need to poll the status
register for link up/down detection.
This patch adds a workqueue to poll for link status.
Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/net/usb/smsc95xx.c | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index d9d2806a47b1..dc989a8b5afb 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c | |||
| @@ -61,6 +61,8 @@ | |||
| 61 | #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ | 61 | #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ |
| 62 | SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) | 62 | SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) |
| 63 | 63 | ||
| 64 | #define CARRIER_CHECK_DELAY (2 * HZ) | ||
| 65 | |||
| 64 | struct smsc95xx_priv { | 66 | struct smsc95xx_priv { |
| 65 | u32 mac_cr; | 67 | u32 mac_cr; |
| 66 | u32 hash_hi; | 68 | u32 hash_hi; |
| @@ -69,6 +71,9 @@ struct smsc95xx_priv { | |||
| 69 | spinlock_t mac_cr_lock; | 71 | spinlock_t mac_cr_lock; |
| 70 | u8 features; | 72 | u8 features; |
| 71 | u8 suspend_flags; | 73 | u8 suspend_flags; |
| 74 | bool link_ok; | ||
| 75 | struct delayed_work carrier_check; | ||
| 76 | struct usbnet *dev; | ||
| 72 | }; | 77 | }; |
| 73 | 78 | ||
| 74 | static bool turbo_mode = true; | 79 | static bool turbo_mode = true; |
| @@ -624,6 +629,44 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb) | |||
| 624 | intdata); | 629 | intdata); |
| 625 | } | 630 | } |
| 626 | 631 | ||
| 632 | static void set_carrier(struct usbnet *dev, bool link) | ||
| 633 | { | ||
| 634 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
| 635 | |||
| 636 | if (pdata->link_ok == link) | ||
| 637 | return; | ||
| 638 | |||
| 639 | pdata->link_ok = link; | ||
| 640 | |||
| 641 | if (link) | ||
| 642 | usbnet_link_change(dev, 1, 0); | ||
| 643 | else | ||
| 644 | usbnet_link_change(dev, 0, 0); | ||
| 645 | } | ||
| 646 | |||
| 647 | static void check_carrier(struct work_struct *work) | ||
| 648 | { | ||
| 649 | struct smsc95xx_priv *pdata = container_of(work, struct smsc95xx_priv, | ||
| 650 | carrier_check.work); | ||
| 651 | struct usbnet *dev = pdata->dev; | ||
| 652 | int ret; | ||
| 653 | |||
| 654 | if (pdata->suspend_flags != 0) | ||
| 655 | return; | ||
| 656 | |||
| 657 | ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMSR); | ||
| 658 | if (ret < 0) { | ||
| 659 | netdev_warn(dev->net, "Failed to read MII_BMSR\n"); | ||
| 660 | return; | ||
| 661 | } | ||
| 662 | if (ret & BMSR_LSTATUS) | ||
| 663 | set_carrier(dev, 1); | ||
| 664 | else | ||
| 665 | set_carrier(dev, 0); | ||
| 666 | |||
| 667 | schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); | ||
| 668 | } | ||
| 669 | |||
| 627 | /* Enable or disable Tx & Rx checksum offload engines */ | 670 | /* Enable or disable Tx & Rx checksum offload engines */ |
| 628 | static int smsc95xx_set_features(struct net_device *netdev, | 671 | static int smsc95xx_set_features(struct net_device *netdev, |
| 629 | netdev_features_t features) | 672 | netdev_features_t features) |
| @@ -1165,13 +1208,20 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) | |||
| 1165 | dev->net->flags |= IFF_MULTICAST; | 1208 | dev->net->flags |= IFF_MULTICAST; |
| 1166 | dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; | 1209 | dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; |
| 1167 | dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; | 1210 | dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; |
| 1211 | |||
| 1212 | pdata->dev = dev; | ||
| 1213 | INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier); | ||
| 1214 | schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); | ||
| 1215 | |||
| 1168 | return 0; | 1216 | return 0; |
| 1169 | } | 1217 | } |
| 1170 | 1218 | ||
| 1171 | static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) | 1219 | static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) |
| 1172 | { | 1220 | { |
| 1173 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | 1221 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); |
| 1222 | |||
| 1174 | if (pdata) { | 1223 | if (pdata) { |
| 1224 | cancel_delayed_work(&pdata->carrier_check); | ||
| 1175 | netif_dbg(dev, ifdown, dev->net, "free pdata\n"); | 1225 | netif_dbg(dev, ifdown, dev->net, "free pdata\n"); |
| 1176 | kfree(pdata); | 1226 | kfree(pdata); |
| 1177 | pdata = NULL; | 1227 | pdata = NULL; |
| @@ -1695,6 +1745,7 @@ static int smsc95xx_resume(struct usb_interface *intf) | |||
| 1695 | 1745 | ||
| 1696 | /* do this first to ensure it's cleared even in error case */ | 1746 | /* do this first to ensure it's cleared even in error case */ |
| 1697 | pdata->suspend_flags = 0; | 1747 | pdata->suspend_flags = 0; |
| 1748 | schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY); | ||
| 1698 | 1749 | ||
| 1699 | if (suspend_flags & SUSPEND_ALLMODES) { | 1750 | if (suspend_flags & SUSPEND_ALLMODES) { |
| 1700 | /* clear wake-up sources */ | 1751 | /* clear wake-up sources */ |
