aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorSteve Glendinning <steve.glendinning@shawell.net>2012-11-28 00:59:49 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-28 11:17:32 -0500
commitb4cdea9cc324d529ad013a7e75949e1bccc1edc3 (patch)
tree7b8500bf42fbf1d454baa9984a3ceb8cfb4ba46b /drivers/net/usb
parenteacdd6c223d699938c56e7c991877433454d3ebc (diff)
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 <steve.glendinning@shawell.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/smsc75xx.c123
1 files changed, 119 insertions, 4 deletions
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 649bf2a4262a..b852c483a2eb 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -57,6 +57,14 @@
57#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ 57#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \
58 WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) 58 WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)
59 59
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
60#define check_warn(ret, fmt, args...) \ 68#define check_warn(ret, fmt, args...) \
61 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) 69 ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); })
62 70
@@ -74,6 +82,7 @@ struct smsc75xx_priv {
74 struct mutex dataport_mutex; 82 struct mutex dataport_mutex;
75 spinlock_t rfe_ctl_lock; 83 spinlock_t rfe_ctl_lock;
76 struct work_struct set_multicast; 84 struct work_struct set_multicast;
85 u8 suspend_flags;
77}; 86};
78 87
79struct usb_context { 88struct usb_context {
@@ -1241,6 +1250,7 @@ static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg,
1241 1250
1242static int smsc75xx_enter_suspend0(struct usbnet *dev) 1251static int smsc75xx_enter_suspend0(struct usbnet *dev)
1243{ 1252{
1253 struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1244 u32 val; 1254 u32 val;
1245 int ret; 1255 int ret;
1246 1256
@@ -1255,11 +1265,14 @@ static int smsc75xx_enter_suspend0(struct usbnet *dev)
1255 1265
1256 smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); 1266 smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1257 1267
1268 pdata->suspend_flags |= SUSPEND_SUSPEND0 | SUSPEND_REMOTEWAKE;
1269
1258 return 0; 1270 return 0;
1259} 1271}
1260 1272
1261static int smsc75xx_enter_suspend1(struct usbnet *dev) 1273static int smsc75xx_enter_suspend1(struct usbnet *dev)
1262{ 1274{
1275 struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1263 u32 val; 1276 u32 val;
1264 int ret; 1277 int ret;
1265 1278
@@ -1281,11 +1294,14 @@ static int smsc75xx_enter_suspend1(struct usbnet *dev)
1281 1294
1282 smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); 1295 smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
1283 1296
1297 pdata->suspend_flags |= SUSPEND_SUSPEND1 | SUSPEND_REMOTEWAKE;
1298
1284 return 0; 1299 return 0;
1285} 1300}
1286 1301
1287static int smsc75xx_enter_suspend2(struct usbnet *dev) 1302static int smsc75xx_enter_suspend2(struct usbnet *dev)
1288{ 1303{
1304 struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1289 u32 val; 1305 u32 val;
1290 int ret; 1306 int ret;
1291 1307
@@ -1298,6 +1314,45 @@ static int smsc75xx_enter_suspend2(struct usbnet *dev)
1298 ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 1314 ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1299 check_warn_return(ret, "Error writing PMT_CTL\n"); 1315 check_warn_return(ret, "Error writing PMT_CTL\n");
1300 1316
1317 pdata->suspend_flags |= SUSPEND_SUSPEND2;
1318
1319 return 0;
1320}
1321
1322static 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
1301 return 0; 1356 return 0;
1302} 1357}
1303 1358
@@ -1338,6 +1393,38 @@ static int smsc75xx_link_ok_nopm(struct usbnet *dev)
1338 return !!(ret & BMSR_LSTATUS); 1393 return !!(ret & BMSR_LSTATUS);
1339} 1394}
1340 1395
1396static 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
1341static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) 1428static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
1342{ 1429{
1343 struct usbnet *dev = usb_get_intfdata(intf); 1430 struct usbnet *dev = usb_get_intfdata(intf);
@@ -1348,9 +1435,20 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
1348 ret = usbnet_suspend(intf, message); 1435 ret = usbnet_suspend(intf, message);
1349 check_warn_goto_done(ret, "usbnet_suspend error\n"); 1436 check_warn_goto_done(ret, "usbnet_suspend error\n");
1350 1437
1438 if (pdata->suspend_flags) {
1439 netdev_warn(dev->net, "error during last resume\n");
1440 pdata->suspend_flags = 0;
1441 }
1442
1351 /* determine if link is up using only _nopm functions */ 1443 /* determine if link is up using only _nopm functions */
1352 link_up = smsc75xx_link_ok_nopm(dev); 1444 link_up = smsc75xx_link_ok_nopm(dev);
1353 1445
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 */
1354 /* if no wol options set, or if link is down and we're not waking on 1452 /* if no wol options set, or if link is down and we're not waking on
1355 * PHY activity, enter lowest power SUSPEND2 mode 1453 * PHY activity, enter lowest power SUSPEND2 mode
1356 */ 1454 */
@@ -1544,14 +1642,21 @@ static int smsc75xx_resume(struct usb_interface *intf)
1544{ 1642{
1545 struct usbnet *dev = usb_get_intfdata(intf); 1643 struct usbnet *dev = usb_get_intfdata(intf);
1546 struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); 1644 struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
1645 u8 suspend_flags = pdata->suspend_flags;
1547 int ret; 1646 int ret;
1548 u32 val; 1647 u32 val;
1549 1648
1550 if (pdata->wolopts) { 1649 netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
1551 netdev_info(dev->net, "resuming from SUSPEND0\n");
1552 1650
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 }
1554 1658
1659 if (suspend_flags & SUSPEND_ALLMODES) {
1555 /* Disable wakeup sources */ 1660 /* Disable wakeup sources */
1556 ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); 1661 ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
1557 check_warn_return(ret, "Error reading WUCSR\n"); 1662 check_warn_return(ret, "Error reading WUCSR\n");
@@ -1571,7 +1676,9 @@ static int smsc75xx_resume(struct usb_interface *intf)
1571 1676
1572 ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); 1677 ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
1573 check_warn_return(ret, "Error writing PMT_CTL\n"); 1678 check_warn_return(ret, "Error writing PMT_CTL\n");
1574 } else { 1679 }
1680
1681 if (suspend_flags & SUSPEND_SUSPEND2) {
1575 netdev_info(dev->net, "resuming from SUSPEND2\n"); 1682 netdev_info(dev->net, "resuming from SUSPEND2\n");
1576 1683
1577 ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); 1684 ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
@@ -1727,6 +1834,12 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev,
1727 return skb; 1834 return skb;
1728} 1835}
1729 1836
1837static int smsc75xx_manage_power(struct usbnet *dev, int on)
1838{
1839 dev->intf->needs_remote_wakeup = on;
1840 return 0;
1841}
1842
1730static const struct driver_info smsc75xx_info = { 1843static const struct driver_info smsc75xx_info = {
1731 .description = "smsc75xx USB 2.0 Gigabit Ethernet", 1844 .description = "smsc75xx USB 2.0 Gigabit Ethernet",
1732 .bind = smsc75xx_bind, 1845 .bind = smsc75xx_bind,
@@ -1736,6 +1849,7 @@ static const struct driver_info smsc75xx_info = {
1736 .rx_fixup = smsc75xx_rx_fixup, 1849 .rx_fixup = smsc75xx_rx_fixup,
1737 .tx_fixup = smsc75xx_tx_fixup, 1850 .tx_fixup = smsc75xx_tx_fixup,
1738 .status = smsc75xx_status, 1851 .status = smsc75xx_status,
1852 .manage_power = smsc75xx_manage_power,
1739 .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, 1853 .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,
1740}; 1854};
1741 1855
@@ -1763,6 +1877,7 @@ static struct usb_driver smsc75xx_driver = {
1763 .reset_resume = smsc75xx_resume, 1877 .reset_resume = smsc75xx_resume,
1764 .disconnect = usbnet_disconnect, 1878 .disconnect = usbnet_disconnect,
1765 .disable_hub_initiated_lpm = 1, 1879 .disable_hub_initiated_lpm = 1,
1880 .supports_autosuspend = 1,
1766}; 1881};
1767 1882
1768module_usb_driver(smsc75xx_driver); 1883module_usb_driver(smsc75xx_driver);