From: Steve Glendinning <steve.glendinning@shawell.net>
To: netdev@vger.kernel.org
Cc: Ming Lei <ming.lei@canonical.com>,
Oliver Neukum <oneukum@suse.de>,
linux-usb@vger.kernel.org, gregkh@linuxfoundation.org,
Steve Glendinning <steve.glendinning@shawell.net>
Subject: [PATCH][RFC] smsc95xx: enable dynamic autosuspend (RFC)
Date: Mon, 10 Dec 2012 11:51:41 +0000 [thread overview]
Message-ID: <1355140301-10518-1-git-send-email-steve.glendinning@shawell.net> (raw)
In-Reply-To: <1353607526-19307-6-git-send-email-steve.glendinning@shawell.net>
This is a work in-progress patch. It's not yet complete but
I thought I'd share it for comments, feedback and testing.
This patch enables dynamic autosuspend for all devices
supported by the driver, but it will only actually work on
LAN9500A (as this has a new SUSPEND3 mode for this purpose).
Unfortunately we don't know if the connected device supports
this feature until we query its ID register at runtime.
On unsupported devices (LAN9500/9512/9514) this patch claims
to support the feature but if enabled it will always return
failure to the autosuspend call (and fill up the kernel log
with a message every 2s).
Suggestions on how best to indicate this capability at runtime
instead of compile-time would be appreciated, so we don't have
to repeatedly fail if accidentally enabled. Or maybe this is
actually the best way?
We should also be able to identify devices supporting
autosuspend from the USB VID/PID if this would help.
UPDATE: reposting this to a wider audience due to lack of
feedback last time round
Signed-off-by: Steve Glendinning <steve.glendinning@shawell.net>
---
drivers/net/usb/smsc95xx.c | 136 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 9b73670..d9c6674 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -55,6 +55,13 @@
#define FEATURE_PHY_NLP_CROSSOVER (0x02)
#define FEATURE_AUTOSUSPEND (0x04)
+#define SUSPEND_SUSPEND0 (0x01)
+#define SUSPEND_SUSPEND1 (0x02)
+#define SUSPEND_SUSPEND2 (0x04)
+#define SUSPEND_SUSPEND3 (0x08)
+#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
+ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
+
struct smsc95xx_priv {
u32 mac_cr;
u32 hash_hi;
@@ -62,6 +69,7 @@ struct smsc95xx_priv {
u32 wolopts;
spinlock_t mac_cr_lock;
u8 features;
+ u8 suspend_flags;
};
static bool turbo_mode = true;
@@ -1341,6 +1349,8 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev)
if (ret < 0)
netdev_warn(dev->net, "Error reading PM_CTRL\n");
+ pdata->suspend_flags |= SUSPEND_SUSPEND0;
+
return ret;
}
@@ -1393,11 +1403,14 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev)
if (ret < 0)
netdev_warn(dev->net, "Error writing PM_CTRL\n");
+ pdata->suspend_flags |= SUSPEND_SUSPEND1;
+
return ret;
}
static int smsc95xx_enter_suspend2(struct usbnet *dev)
{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
u32 val;
int ret;
@@ -1414,9 +1427,96 @@ static int smsc95xx_enter_suspend2(struct usbnet *dev)
if (ret < 0)
netdev_warn(dev->net, "Error writing PM_CTRL\n");
+ pdata->suspend_flags |= SUSPEND_SUSPEND2;
+
return ret;
}
+static int smsc95xx_enter_suspend3(struct usbnet *dev)
+{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ u32 val;
+ int ret;
+
+ ret = smsc95xx_read_reg_nopm(dev, RX_FIFO_INF, &val);
+ if (ret < 0) {
+ netdev_warn(dev->net, "Error reading RX_FIFO_INF");
+ return ret;
+ }
+
+ if (val & 0xFFFF) {
+ netdev_info(dev->net, "rx fifo not empty in autosuspend");
+ return -EBUSY;
+ }
+
+ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
+ if (ret < 0) {
+ netdev_warn(dev->net, "Error reading PM_CTRL");
+ return ret;
+ }
+
+ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_);
+ val |= PM_CTL_SUS_MODE_3 | PM_CTL_RES_CLR_WKP_STS;
+
+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
+ if (ret < 0) {
+ netdev_warn(dev->net, "Error writing PM_CTRL");
+ return ret;
+ }
+
+ /* clear wol status */
+ val &= ~PM_CTL_WUPS_;
+ val |= PM_CTL_WUPS_WOL_;
+
+ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
+ if (ret < 0) {
+ netdev_warn(dev->net, "Error writing PM_CTRL");
+ return ret;
+ }
+
+ pdata->suspend_flags |= SUSPEND_SUSPEND3;
+
+ return 0;
+}
+
+static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
+{
+ int ret;
+
+ if (!netif_running(dev->net)) {
+ /* interface is ifconfig down so fully power down hw */
+ netdev_dbg(dev->net, "autosuspend entering SUSPEND2");
+ return smsc95xx_enter_suspend2(dev);
+ }
+
+ if (!link_up) {
+ /* link is down so enter EDPD mode */
+ netdev_dbg(dev->net, "autosuspend entering SUSPEND1");
+
+ /* enable PHY wakeup events for if cable is attached */
+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+ PHY_INT_MASK_ANEG_COMP_);
+ if (ret < 0) {
+ netdev_warn(dev->net, "error enabling PHY wakeup ints");
+ return ret;
+ }
+
+ netdev_info(dev->net, "entering SUSPEND1 mode");
+ return smsc95xx_enter_suspend1(dev);
+ }
+
+ /* enable PHY wakeup events so we remote wakeup if cable is pulled */
+ ret = smsc95xx_enable_phy_wakeup_interrupts(dev,
+ PHY_INT_MASK_LINK_DOWN_);
+ if (ret < 0) {
+ netdev_warn(dev->net, "error enabling PHY wakeup ints");
+ return ret;
+ }
+
+ netdev_dbg(dev->net, "autosuspend entering SUSPEND3");
+ return smsc95xx_enter_suspend3(dev);
+}
+
static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
@@ -1424,15 +1524,35 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
u32 val, link_up;
int ret;
+ /* TODO: don't indicate this feature to usb framework if
+ * our current hardware doesn't have the capability
+ */
+ if ((message.event == PM_EVENT_AUTO_SUSPEND) &&
+ (!(pdata->features & FEATURE_AUTOSUSPEND))) {
+ netdev_warn(dev->net, "autosuspend not supported");
+ return -EBUSY;
+ }
+
ret = usbnet_suspend(intf, message);
if (ret < 0) {
netdev_warn(dev->net, "usbnet_suspend error\n");
return ret;
}
+ if (pdata->suspend_flags) {
+ netdev_warn(dev->net, "error during last resume");
+ pdata->suspend_flags = 0;
+ }
+
/* determine if link is up using only _nopm functions */
link_up = smsc95xx_link_ok_nopm(dev);
+ if (message.event == PM_EVENT_AUTO_SUSPEND) {
+ ret = smsc95xx_autosuspend(dev, link_up);
+ goto done;
+ }
+
+ /* if we get this far we're not autosuspending */
/* if no wol options set, or if link is down and we're not waking on
* PHY activity, enter lowest power SUSPEND2 mode
*/
@@ -1694,12 +1814,18 @@ static int smsc95xx_resume(struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ u8 suspend_flags = pdata->suspend_flags;
int ret;
u32 val;
BUG_ON(!dev);
- if (pdata->wolopts) {
+ netdev_dbg(dev->net, "resume suspend_flags=0x%02x", suspend_flags);
+
+ /* do this first to ensure it's cleared even in error case */
+ pdata->suspend_flags = 0;
+
+ if (suspend_flags & SUSPEND_ALLMODES) {
/* clear wake-up sources */
ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
if (ret < 0) {
@@ -1891,6 +2017,12 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
return skb;
}
+static int smsc95xx_manage_power(struct usbnet *dev, int on)
+{
+ dev->intf->needs_remote_wakeup = on;
+ return 0;
+}
+
static const struct driver_info smsc95xx_info = {
.description = "smsc95xx USB 2.0 Ethernet",
.bind = smsc95xx_bind,
@@ -1900,6 +2032,7 @@ static const struct driver_info smsc95xx_info = {
.rx_fixup = smsc95xx_rx_fixup,
.tx_fixup = smsc95xx_tx_fixup,
.status = smsc95xx_status,
+ .manage_power = smsc95xx_manage_power,
.flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR,
};
@@ -2007,6 +2140,7 @@ static struct usb_driver smsc95xx_driver = {
.reset_resume = smsc95xx_resume,
.disconnect = usbnet_disconnect,
.disable_hub_initiated_lpm = 1,
+ .supports_autosuspend = 1,
};
module_usb_driver(smsc95xx_driver);
--
1.7.10.4
next prev parent reply other threads:[~2012-12-10 11:51 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-11-22 18:05 [PATCH 0/5] smsc95xx updates Steve Glendinning
2012-11-22 18:05 ` [PATCH 1/5] smsc95xx: fix error checking of usbnet_resume Steve Glendinning
2012-11-22 20:32 ` David Miller
2012-11-23 12:55 ` Steve Glendinning
2012-11-22 18:05 ` [PATCH 2/5] smsc95xx: detect chip revision specific features Steve Glendinning
2012-11-22 18:05 ` [PATCH 3/5] smsc95xx: support PHY wakeup source Steve Glendinning
2012-11-22 18:05 ` [PATCH 4/5] smsc95xx: refactor entering suspend modes Steve Glendinning
[not found] ` <1353607526-19307-5-git-send-email-steve.glendinning-nksJyM/082jR7s880joybQ@public.gmane.org>
2012-11-26 13:48 ` Bjørn Mork
2012-11-22 18:05 ` [PATCH 5/5][RFC] smsc95xx: enable dynamic autosuspend (RFC) Steve Glendinning
2012-12-10 11:51 ` Steve Glendinning [this message]
[not found] ` <1355140301-10518-1-git-send-email-steve.glendinning-nksJyM/082jR7s880joybQ@public.gmane.org>
2012-12-10 12:09 ` [PATCH][RFC] " Oliver Neukum
2012-12-10 14:18 ` Steve Glendinning
[not found] ` <CAKh2mn5LgZBJDJWNzN8A3NK72Vk_fiUjJfmdn5njSrm400q36w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-12-11 2:24 ` Ming Lei
2012-12-11 10:27 ` Oliver Neukum
[not found] ` <1881907.JmjSTg2PBW-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
2012-12-11 12:53 ` Ming Lei
2012-12-11 13:12 ` Steve Glendinning
2012-12-11 15:19 ` Oliver Neukum
2012-12-11 15:58 ` Ming Lei
2012-12-10 13:59 ` Ming Lei
2012-11-23 19:15 ` [PATCH 0/5] smsc95xx updates David Miller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1355140301-10518-1-git-send-email-steve.glendinning@shawell.net \
--to=steve.glendinning@shawell.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-usb@vger.kernel.org \
--cc=ming.lei@canonical.com \
--cc=netdev@vger.kernel.org \
--cc=oneukum@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).