Skip to content

Commit b4cdea9

Browse files
steveglendavem330
authored andcommitted
smsc75xx: add support for USB dynamic autosuspend
This patch adds support for USB dynamic autosuspend to the smsc75xx driver. This saves virtually no power in the USB device but enables power savings in upstream hosts and the host CPU. Note currently Linux doesn't automatically enable this functionality by default for devices so to test this: echo auto > /sys/bus/usb/devices/2-1.2/power/control where 2-1.2 is the USB bus address of the LAN7500. Signed-off-by: Steve Glendinning <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent eacdd6c commit b4cdea9

File tree

1 file changed

+119
-4
lines changed

1 file changed

+119
-4
lines changed

drivers/net/usb/smsc75xx.c

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@
5757
#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \
5858
WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
5959

60+
#define SUSPEND_SUSPEND0 (0x01)
61+
#define SUSPEND_SUSPEND1 (0x02)
62+
#define SUSPEND_SUSPEND2 (0x04)
63+
#define SUSPEND_SUSPEND3 (0x08)
64+
#define SUSPEND_REMOTEWAKE (0x10)
65+
#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
66+
SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
67+
6068
#define check_warn(ret, fmt, args...) \
6169
({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
6270

@@ -74,6 +82,7 @@ struct smsc75xx_priv {
7482
struct mutex dataport_mutex;
7583
spinlock_t rfe_ctl_lock;
7684
struct work_struct set_multicast;
85+
u8 suspend_flags;
7786
};
7887

7988
struct usb_context {
@@ -1241,6 +1250,7 @@ static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg,
12411250

12421251
static int smsc75xx_enter_suspend0(struct usbnet *dev)
12431252
{
1253+
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
12441254
u32 val;
12451255
int ret;
12461256

@@ -1255,11 +1265,14 @@ static int smsc75xx_enter_suspend0(struct usbnet *dev)
12551265

12561266
smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
12571267

1268+
pdata->suspend_flags |= SUSPEND_SUSPEND0 | SUSPEND_REMOTEWAKE;
1269+
12581270
return 0;
12591271
}
12601272

12611273
static int smsc75xx_enter_suspend1(struct usbnet *dev)
12621274
{
1275+
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
12631276
u32 val;
12641277
int ret;
12651278

@@ -1281,11 +1294,14 @@ static int smsc75xx_enter_suspend1(struct usbnet *dev)
12811294

12821295
smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
12831296

1297+
pdata->suspend_flags |= SUSPEND_SUSPEND1 | SUSPEND_REMOTEWAKE;
1298+
12841299
return 0;
12851300
}
12861301

12871302
static int smsc75xx_enter_suspend2(struct usbnet *dev)
12881303
{
1304+
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
12891305
u32 val;
12901306
int ret;
12911307

@@ -1298,6 +1314,45 @@ static int smsc75xx_enter_suspend2(struct usbnet *dev)
12981314
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
12991315
check_warn_return(ret, "Error writing PMT_CTL\n");
13001316

1317+
pdata->suspend_flags |= SUSPEND_SUSPEND2;
1318+
1319+
return 0;
1320+
}
1321+
1322+
static int smsc75xx_enter_suspend3(struct usbnet *dev)
1323+
{
1324+
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1325+
u32 val;
1326+
int ret;
1327+
1328+
ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val);
1329+
check_warn_return(ret, "Error reading FCT_RX_CTL\n");
1330+
1331+
if (val & FCT_RX_CTL_RXUSED) {
1332+
netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n");
1333+
return -EBUSY;
1334+
}
1335+
1336+
ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
1337+
check_warn_return(ret, "Error reading PMT_CTL\n");
1338+
1339+
val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
1340+
val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN;
1341+
1342+
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1343+
check_warn_return(ret, "Error writing PMT_CTL\n");
1344+
1345+
/* clear wol status */
1346+
val &= ~PMT_CTL_WUPS;
1347+
val |= PMT_CTL_WUPS_WOL;
1348+
1349+
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1350+
check_warn_return(ret, "Error writing PMT_CTL\n");
1351+
1352+
smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1353+
1354+
pdata->suspend_flags |= SUSPEND_SUSPEND3 | SUSPEND_REMOTEWAKE;
1355+
13011356
return 0;
13021357
}
13031358

@@ -1338,6 +1393,38 @@ static int smsc75xx_link_ok_nopm(struct usbnet *dev)
13381393
return !!(ret & BMSR_LSTATUS);
13391394
}
13401395

1396+
static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up)
1397+
{
1398+
int ret;
1399+
1400+
if (!netif_running(dev->net)) {
1401+
/* interface is ifconfig down so fully power down hw */
1402+
netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n");
1403+
return smsc75xx_enter_suspend2(dev);
1404+
}
1405+
1406+
if (!link_up) {
1407+
/* link is down so enter EDPD mode */
1408+
netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n");
1409+
1410+
/* enable PHY wakeup events for if cable is attached */
1411+
ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
1412+
PHY_INT_MASK_ANEG_COMP);
1413+
check_warn_return(ret, "error enabling PHY wakeup ints\n");
1414+
1415+
netdev_info(dev->net, "entering SUSPEND1 mode\n");
1416+
return smsc75xx_enter_suspend1(dev);
1417+
}
1418+
1419+
/* enable PHY wakeup events so we remote wakeup if cable is pulled */
1420+
ret = smsc75xx_enable_phy_wakeup_interrupts(dev,
1421+
PHY_INT_MASK_LINK_DOWN);
1422+
check_warn_return(ret, "error enabling PHY wakeup ints\n");
1423+
1424+
netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n");
1425+
return smsc75xx_enter_suspend3(dev);
1426+
}
1427+
13411428
static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
13421429
{
13431430
struct usbnet *dev = usb_get_intfdata(intf);
@@ -1348,9 +1435,20 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
13481435
ret = usbnet_suspend(intf, message);
13491436
check_warn_goto_done(ret, "usbnet_suspend error\n");
13501437

1438+
if (pdata->suspend_flags) {
1439+
netdev_warn(dev->net, "error during last resume\n");
1440+
pdata->suspend_flags = 0;
1441+
}
1442+
13511443
/* determine if link is up using only _nopm functions */
13521444
link_up = smsc75xx_link_ok_nopm(dev);
13531445

1446+
if (message.event == PM_EVENT_AUTO_SUSPEND) {
1447+
ret = smsc75xx_autosuspend(dev, link_up);
1448+
goto done;
1449+
}
1450+
1451+
/* if we get this far we're not autosuspending */
13541452
/* if no wol options set, or if link is down and we're not waking on
13551453
* PHY activity, enter lowest power SUSPEND2 mode
13561454
*/
@@ -1544,14 +1642,21 @@ static int smsc75xx_resume(struct usb_interface *intf)
15441642
{
15451643
struct usbnet *dev = usb_get_intfdata(intf);
15461644
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1645+
u8 suspend_flags = pdata->suspend_flags;
15471646
int ret;
15481647
u32 val;
15491648

1550-
if (pdata->wolopts) {
1551-
netdev_info(dev->net, "resuming from SUSPEND0\n");
1649+
netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
15521650

1553-
smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1651+
/* do this first to ensure it's cleared even in error case */
1652+
pdata->suspend_flags = 0;
1653+
1654+
if (suspend_flags & SUSPEND_REMOTEWAKE) {
1655+
ret = smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1656+
check_warn_return(ret, "Error disabling remote wakeup\n");
1657+
}
15541658

1659+
if (suspend_flags & SUSPEND_ALLMODES) {
15551660
/* Disable wakeup sources */
15561661
ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
15571662
check_warn_return(ret, "Error reading WUCSR\n");
@@ -1571,7 +1676,9 @@ static int smsc75xx_resume(struct usb_interface *intf)
15711676

15721677
ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
15731678
check_warn_return(ret, "Error writing PMT_CTL\n");
1574-
} else {
1679+
}
1680+
1681+
if (suspend_flags & SUSPEND_SUSPEND2) {
15751682
netdev_info(dev->net, "resuming from SUSPEND2\n");
15761683

15771684
ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
@@ -1727,6 +1834,12 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev,
17271834
return skb;
17281835
}
17291836

1837+
static int smsc75xx_manage_power(struct usbnet *dev, int on)
1838+
{
1839+
dev->intf->needs_remote_wakeup = on;
1840+
return 0;
1841+
}
1842+
17301843
static const struct driver_info smsc75xx_info = {
17311844
.description = "smsc75xx USB 2.0 Gigabit Ethernet",
17321845
.bind = smsc75xx_bind,
@@ -1736,6 +1849,7 @@ static const struct driver_info smsc75xx_info = {
17361849
.rx_fixup = smsc75xx_rx_fixup,
17371850
.tx_fixup = smsc75xx_tx_fixup,
17381851
.status = smsc75xx_status,
1852+
.manage_power = smsc75xx_manage_power,
17391853
.flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,
17401854
};
17411855

@@ -1763,6 +1877,7 @@ static struct usb_driver smsc75xx_driver = {
17631877
.reset_resume = smsc75xx_resume,
17641878
.disconnect = usbnet_disconnect,
17651879
.disable_hub_initiated_lpm = 1,
1880+
.supports_autosuspend = 1,
17661881
};
17671882

17681883
module_usb_driver(smsc75xx_driver);

0 commit comments

Comments
 (0)