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/net/usb | |
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/net/usb')
-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 */ |