Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d69d1694 authored by Christoph Fritz's avatar Christoph Fritz Committed by David S. Miller
Browse files

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: default avatarChristoph Fritz <chf.fritz@googlemail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f00e35e2
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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)
@@ -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;
@@ -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 */