From 5b1b0b812a7b1a5b968c5d06d90d1cb88621b941 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 19 Aug 2011 23:49:48 +0200 Subject: PM / Runtime: Add macro to test for runtime PM events This patch (as1482) adds a macro for testing whether or not a pm_message value represents an autosuspend or autoresume (i.e., a runtime PM) event. Encapsulating this notion seems preferable to open-coding the test all over the place. Signed-off-by: Alan Stern Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/usb/core/driver.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 34e3da5aa72a..e03042883c68 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1046,8 +1046,7 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) /* Non-root devices on a full/low-speed bus must wait for their * companion high-speed root hub, in case a handoff is needed. */ - if (!(msg.event & PM_EVENT_AUTO) && udev->parent && - udev->bus->hs_companion) + if (!PMSG_IS_AUTO(msg) && udev->parent && udev->bus->hs_companion) device_pm_wait_for_dev(&udev->dev, &udev->bus->hs_companion->root_hub->dev); @@ -1075,7 +1074,7 @@ static int usb_suspend_interface(struct usb_device *udev, if (driver->suspend) { status = driver->suspend(intf, msg); - if (status && !(msg.event & PM_EVENT_AUTO)) + if (status && !PMSG_IS_AUTO(msg)) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { @@ -1189,7 +1188,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) status = usb_suspend_interface(udev, intf, msg); /* Ignore errors during system sleep transitions */ - if (!(msg.event & PM_EVENT_AUTO)) + if (!PMSG_IS_AUTO(msg)) status = 0; if (status != 0) break; @@ -1199,7 +1198,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) status = usb_suspend_device(udev, msg); /* Again, ignore errors during system sleep transitions */ - if (!(msg.event & PM_EVENT_AUTO)) + if (!PMSG_IS_AUTO(msg)) status = 0; } -- cgit v1.2.3 From c5a48592d874ddef8c7880311581eccf0eb30c3b Mon Sep 17 00:00:00 2001 From: Jim Wylder Date: Tue, 6 Sep 2011 21:07:20 -0500 Subject: USB: for usb_autopm_get_interface_async -EINPROGRESS is not an error A return value of -EINPROGRESS from pm_runtime_get indicates that the device is already resuming due to a previous call. Internally, usb_autopm_get_interface_async doesn't treat this as an error and increments the usage count, but passes the error status along to the caller. The logical assumption of the caller is that any negative return value reflects the device not resuming and the pm_usage_cnt not being incremented. Since the usage count is being incremented and the device is resuming, return success (0) instead. Signed-off-by: James Wylder Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 34e3da5aa72a..14b83f2a4e87 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1583,7 +1583,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); - if (status > 0) + if (status > 0 || status == -EINPROGRESS) status = 0; return status; } -- cgit v1.2.3 From 65580b4321eb36f16ae8b5987bfa1bb948fc5112 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:52 -0700 Subject: xHCI: set USB2 hardware LPM If the device pass the USB2 software LPM and the host supports hardware LPM, enable hardware LPM for the device to let the host decide when to put the link into lower power state. If hardware LPM is enabled for a port and driver wants to put it into suspend, it must first disable hardware LPM, resume the port into U0, and then suspend the port. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 14 +++++++++ drivers/usb/core/hub.c | 9 ++++++ drivers/usb/core/usb.h | 5 +++ drivers/usb/host/xhci-hub.c | 9 ++++++ drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci.c | 74 ++++++++++++++++++++++++++++++++++++++++++++- drivers/usb/host/xhci.h | 4 +++ include/linux/usb.h | 4 +++ include/linux/usb/hcd.h | 1 + 9 files changed, 120 insertions(+), 1 deletion(-) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 14b83f2a4e87..adf5ca8a2396 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) return 0; } +int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret = -EPERM; + + if (hcd->driver->set_usb2_hw_lpm) { + ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); + if (!ret) + udev->usb2_hw_lpm_enabled = enable; + } + + return ret; +} + #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4ffc3d1bd9e7..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0d023cd2c149..3888778582c4 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -82,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); +extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); #else @@ -96,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) return 0; } +static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + return 0; +} #endif extern struct bus_type usb_bus_type; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d7be6d7324d3..9f844d45c667 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -574,10 +574,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); + if ((temp & PORT_PLS_MASK) != XDEV_U0) { + /* Resume the port to U0 first */ + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ + temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 213a7d73b118..e66e2b03fbbe 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -349,6 +349,7 @@ static const struct hc_driver xhci_pci_hc_driver = { * call back when device connected and addressed */ .update_device = xhci_update_device, + .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b0649a4bd315..4648cc0c5721 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3286,6 +3286,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } + if (udev->usb2_hw_lpm_enabled) { + xhci_set_usb2_hardware_lpm(hcd, udev, 0); + udev->usb2_hw_lpm_enabled = 0; + } + spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); @@ -3699,20 +3704,87 @@ finish: return ret; } +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + __le32 __iomem *pm_addr; + u32 temp; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + + if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || + !udev->lpm_capable) + return -EPERM; + + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (udev->usb2_hw_lpm_capable != 1) + return -EPERM; + + spin_lock_irqsave(&xhci->lock, flags); + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + pm_addr = port_array[port_num] + 1; + temp = xhci_readl(xhci, pm_addr); + + xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", + enable ? "enable" : "disable", port_num); + + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + if (enable) { + temp &= ~PORT_HIRD_MASK; + temp |= PORT_HIRD(hird) | PORT_RWE; + xhci_writel(xhci, temp, pm_addr); + temp = xhci_readl(xhci, pm_addr); + temp |= PORT_HLE; + xhci_writel(xhci, temp, pm_addr); + } else { + temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + xhci_writel(xhci, temp, pm_addr); + } + + spin_unlock_irqrestore(&xhci->lock, flags); + return 0; +} + int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ret; ret = xhci_usb2_software_lpm_test(hcd, udev); - if (!ret) + if (!ret) { xhci_dbg(xhci, "software LPM test succeed\n"); + if (xhci->hw_lpm_support == 1) { + udev->usb2_hw_lpm_capable = 1; + ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); + if (!ret) + udev->usb2_hw_lpm_enabled = 1; + } + } return 0; } #else +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + return 0; +} + int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b24c4fce457e..e738466703a5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -367,7 +367,9 @@ struct xhci_op_regs { #define PORT_L1S_SUCCESS 1 #define PORT_RWE (1 << 3) #define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) #define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) /** * struct xhci_intr_reg - Interrupt Register Set @@ -1677,6 +1679,8 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); diff --git a/include/linux/usb.h b/include/linux/usb.h index 1d00d9bc5d65..6f49a1b39fa6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -411,6 +411,8 @@ struct usb_tt; * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB * @lpm_capable: device supports LPM + * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM + * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) @@ -474,6 +476,8 @@ struct usb_device { unsigned authenticated:1; unsigned wusb:1; unsigned lpm_capable:1; + unsigned usb2_hw_lpm_capable:1; + unsigned usb2_hw_lpm_enabled:1; int string_langid; /* static strings from the device */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 0097136ba45d..a4cd6c58870a 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -343,6 +343,7 @@ struct hc_driver { * address is set */ int (*update_device)(struct usb_hcd *, struct usb_device *); + int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); -- cgit v1.2.3 From f940fcd8eadfe5b909a1474b57de7755edeee62b Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 27 May 2011 09:56:31 -0400 Subject: usb: Add export.h for EXPORT_SYMBOL/THIS_MODULE where needed With module.h being implicitly everywhere via device.h, the absence of explicitly including something for EXPORT_SYMBOL went unnoticed. Since we are heading to fix things up and clean module.h from the device.h file, we need to explicitly include these files now. Use the lightweight version of the header that has just THIS_MODULE and EXPORT_SYMBOL variants. Signed-off-by: Paul Gortmaker --- drivers/usb/core/driver.c | 1 + drivers/usb/core/notify.c | 1 + drivers/usb/gadget/f_fs.c | 1 + drivers/usb/gadget/u_serial.c | 1 + drivers/usb/host/pci-quirks.c | 1 + drivers/usb/host/whci/debug.c | 1 + drivers/usb/mon/mon_bin.c | 1 + drivers/usb/mon/mon_stat.c | 1 + drivers/usb/mon/mon_text.c | 1 + drivers/usb/otg/otg.c | 1 + drivers/usb/otg/ulpi.c | 1 + drivers/usb/storage/protocol.c | 1 + drivers/usb/storage/transport.c | 1 + drivers/usb/wusbcore/devconnect.c | 1 + drivers/usb/wusbcore/mmc.c | 1 + drivers/usb/wusbcore/rh.c | 1 + drivers/usb/wusbcore/security.c | 1 + drivers/usb/wusbcore/wa-rpipe.c | 1 + drivers/usb/wusbcore/wa-xfer.c | 1 + 19 files changed, 19 insertions(+) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 3b029a0a4787..2de2803f6533 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c index 7542dce3f5a1..7728c91dfa2e 100644 --- a/drivers/usb/core/notify.c +++ b/drivers/usb/core/notify.c @@ -10,6 +10,7 @@ #include +#include #include #include #include diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 6b1c20b6c9b2..acb38004eec0 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -20,6 +20,7 @@ #include #include +#include #include #include diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 3a4a664bab44..6597a6813e43 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "u_serial.h" diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 629a96813fd6..27a3dec32fa2 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "pci-quirks.h" diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index 767af265e002..ba61dae9e4d2 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../../wusbcore/wusbhc.h" diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index a04b2ff9dd83..91cd85076a44 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index e5ce42bd316e..ebd6189a5014 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 1c3afcc11bd9..ad408251d955 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index fb7adeff9ffa..307c27bc51eb 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -10,6 +10,7 @@ */ #include +#include #include #include diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 770d799d5afb..0b0466728fdc 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index fc310f75eada..93c1a4d86f51 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -43,6 +43,7 @@ */ #include +#include #include #include diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index ff32390d61e5..0e5c91c6187f 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -46,6 +46,7 @@ #include #include #include +#include #include diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index 7ec24e46b34b..231009af65a3 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -90,6 +90,7 @@ #include #include #include +#include #include "wusbhc.h" static void wusbhc_devconnect_acked_work(struct work_struct *work); diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index 0a57ff0a0b0c..b8c72583c040 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c @@ -38,6 +38,7 @@ */ #include #include +#include #include "wusbhc.h" /* Initialize the MMCIEs handling mechanism */ diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index 39de3900ad20..59ff254dfb6f 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -70,6 +70,7 @@ * wusbhc_rh_start_port_reset() ??? unimplemented */ #include +#include #include "wusbhc.h" /* diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index b60799b811c1..371f61733f05 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "wusbhc.h" static void wusbhc_set_gtk_callback(struct urb *urb); diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index 2acc7f504c51..f0d546c5a089 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -61,6 +61,7 @@ #include #include #include +#include #include "wusbhc.h" #include "wa-hc.h" diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 419334568be6..57c01ab09ad8 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -84,6 +84,7 @@ #include #include #include +#include #include "wa-hc.h" #include "wusbhc.h" -- cgit v1.2.3 From b2c0a863e14676fa5760c6d828fd373288e2f64a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 4 Nov 2011 00:52:46 +0100 Subject: USB: Update last_busy time after autosuspend fails Originally, the runtime PM core would send an idle notification whenever a suspend attempt failed. The idle callback routine could then schedule a delayed suspend for some time later. However this behavior was changed by commit f71648d73c1650b8b4aceb3856bebbde6daa3b86 (PM / Runtime: Remove idle notification after failing suspend). No notifications were sent, and there was no clear mechanism to retry failed suspends. This caused problems for the usbhid driver, because it fails autosuspend attempts as long as a key is being held down. A companion patch changes the PM core's behavior, but we also need to change the USB core. In particular, this patch (as1493) updates the device's last_busy time when an autosuspend fails, so that the PM core will retry the autosuspend in the future when the delay time expires again. Signed-off-by: Alan Stern Tested-by: Henrik Rydberg Cc: Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/usb/core/driver.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/core/driver.c') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 3b029a0a4787..c2c0ae57e7ff 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1667,6 +1667,11 @@ int usb_runtime_suspend(struct device *dev) return -EAGAIN; status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); + + /* Allow a retry if autosuspend failed temporarily */ + if (status == -EAGAIN || status == -EBUSY) + usb_mark_last_busy(udev); + /* The PM core reacts badly unless the return code is 0, * -EAGAIN, or -EBUSY, so always return -EBUSY on an error. */ -- cgit v1.2.3