Skip to content

Commit

Permalink
usbnet: smsc95xx: fix link detection for disabled autonegotiation
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
ch-f authored and davem330 committed May 31, 2016
1 parent f00e35e commit d69d169
Showing 1 changed file with 51 additions and 0 deletions.
51 changes: 51 additions & 0 deletions drivers/net/usb/smsc95xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)

#define CARRIER_CHECK_DELAY (2 * HZ)

struct smsc95xx_priv {
u32 mac_cr;
u32 hash_hi;
Expand All @@ -69,6 +71,9 @@ struct smsc95xx_priv {
spinlock_t mac_cr_lock;
u8 features;
u8 suspend_flags;
bool link_ok;
struct delayed_work carrier_check;
struct usbnet *dev;
};

static bool turbo_mode = true;
Expand Down Expand Up @@ -624,6 +629,44 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
intdata);
}

static void set_carrier(struct usbnet *dev, bool link)
{
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);

if (pdata->link_ok == link)
return;

pdata->link_ok = link;

if (link)
usbnet_link_change(dev, 1, 0);
else
usbnet_link_change(dev, 0, 0);
}

static void check_carrier(struct work_struct *work)
{
struct smsc95xx_priv *pdata = container_of(work, struct smsc95xx_priv,
carrier_check.work);
struct usbnet *dev = pdata->dev;
int ret;

if (pdata->suspend_flags != 0)
return;

ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMSR);
if (ret < 0) {
netdev_warn(dev->net, "Failed to read MII_BMSR\n");
return;
}
if (ret & BMSR_LSTATUS)
set_carrier(dev, 1);
else
set_carrier(dev, 0);

schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
}

/* Enable or disable Tx & Rx checksum offload engines */
static int smsc95xx_set_features(struct net_device *netdev,
netdev_features_t features)
Expand Down Expand Up @@ -1165,13 +1208,20 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->flags |= IFF_MULTICAST;
dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;
dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;

pdata->dev = dev;
INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier);
schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);

return 0;
}

static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
{
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);

if (pdata) {
cancel_delayed_work(&pdata->carrier_check);
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
kfree(pdata);
pdata = NULL;
Expand Down Expand Up @@ -1695,6 +1745,7 @@ static int smsc95xx_resume(struct usb_interface *intf)

/* do this first to ensure it's cleared even in error case */
pdata->suspend_flags = 0;
schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);

if (suspend_flags & SUSPEND_ALLMODES) {
/* clear wake-up sources */
Expand Down

0 comments on commit d69d169

Please sign in to comment.