|
@@ -61,6 +61,8 @@
|
|
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
|
|
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
|
|
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
|
|
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
|
|
|
|
|
|
|
|
+#define CARRIER_CHECK_DELAY (2 * HZ)
|
|
|
|
+
|
|
struct smsc95xx_priv {
|
|
struct smsc95xx_priv {
|
|
u32 mac_cr;
|
|
u32 mac_cr;
|
|
u32 hash_hi;
|
|
u32 hash_hi;
|
|
@@ -69,6 +71,9 @@ struct smsc95xx_priv {
|
|
spinlock_t mac_cr_lock;
|
|
spinlock_t mac_cr_lock;
|
|
u8 features;
|
|
u8 features;
|
|
u8 suspend_flags;
|
|
u8 suspend_flags;
|
|
|
|
+ bool link_ok;
|
|
|
|
+ struct delayed_work carrier_check;
|
|
|
|
+ struct usbnet *dev;
|
|
};
|
|
};
|
|
|
|
|
|
static bool turbo_mode = true;
|
|
static bool turbo_mode = true;
|
|
@@ -624,6 +629,44 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
|
|
intdata);
|
|
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 */
|
|
/* Enable or disable Tx & Rx checksum offload engines */
|
|
static int smsc95xx_set_features(struct net_device *netdev,
|
|
static int smsc95xx_set_features(struct net_device *netdev,
|
|
netdev_features_t features)
|
|
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->flags |= IFF_MULTICAST;
|
|
dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;
|
|
dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;
|
|
dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
|
|
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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
|
|
static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
|
|
{
|
|
{
|
|
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
|
|
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
|
|
|
|
+
|
|
if (pdata) {
|
|
if (pdata) {
|
|
|
|
+ cancel_delayed_work(&pdata->carrier_check);
|
|
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
|
|
netif_dbg(dev, ifdown, dev->net, "free pdata\n");
|
|
kfree(pdata);
|
|
kfree(pdata);
|
|
pdata = NULL;
|
|
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 */
|
|
/* do this first to ensure it's cleared even in error case */
|
|
pdata->suspend_flags = 0;
|
|
pdata->suspend_flags = 0;
|
|
|
|
+ schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
|
|
|
|
|
|
if (suspend_flags & SUSPEND_ALLMODES) {
|
|
if (suspend_flags & SUSPEND_ALLMODES) {
|
|
/* clear wake-up sources */
|
|
/* clear wake-up sources */
|