USB: speed up usb_bus_resume()
authorAlan Stern <stern@rowland.harvard.edu>
Fri, 19 Oct 2012 15:03:39 +0000 (11:03 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Oct 2012 21:51:22 +0000 (14:51 -0700)
This patch (as1620) speeds up USB root-hub resumes in the common case
where every enabled port has its suspend feature set (which currently
will be true for every runtime resume of the root hub).  If all the
enabled ports are suspended then resuming the root hub won't resume
any of the downstream devices.  In this case there's no need for a
Resume Recovery delay, because that delay is meant to give devices a
chance to get ready for active use.

To keep track of the port suspend features, the patch adds a
"port_is_suspended" flag to struct usb_device.  This has to be tracked
separately from the device's state; it's entirely possible for a USB-2
device to be suspended while the suspend feature on its parent port is
clear.  The reason is that devices will go into suspend whenever their
parent hub does.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reviewed-by: Peter Chen <peter.chen@freescale.com>
Tested-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
include/linux/usb.h

index 1e741bca02652e8f4ed2b027654abc3ac9d16a4f..eaa14514e173163bf46aba9dd65cddaa57fb85fd 100644 (file)
@@ -2039,8 +2039,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
        status = hcd->driver->bus_resume(hcd);
        clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
        if (status == 0) {
-               /* TRSMRCY = 10 msec */
-               msleep(10);
+               struct usb_device *udev;
+               int port1;
+
                spin_lock_irq(&hcd_root_hub_lock);
                if (!HCD_DEAD(hcd)) {
                        usb_set_device_state(rhdev, rhdev->actconfig
@@ -2050,6 +2051,20 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
                        hcd->state = HC_STATE_RUNNING;
                }
                spin_unlock_irq(&hcd_root_hub_lock);
+
+               /*
+                * Check whether any of the enabled ports on the root hub are
+                * unsuspended.  If they are then a TRSMRCY delay is needed
+                * (this is what the USB-2 spec calls a "global resume").
+                * Otherwise we can skip the delay.
+                */
+               usb_hub_for_each_child(rhdev, port1, udev) {
+                       if (udev->state != USB_STATE_NOTATTACHED &&
+                                       !udev->port_is_suspended) {
+                               usleep_range(10000, 11000);     /* TRSMRCY */
+                               break;
+                       }
+               }
        } else {
                hcd->state = old_state;
                dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
index 64854d76f52901f39bf658a17790d4b512853a81..e729e94cb751672b5a486f140059c2e886281f0f 100644 (file)
@@ -2876,6 +2876,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                                (PMSG_IS_AUTO(msg) ? "auto-" : ""),
                                udev->do_remote_wakeup);
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
+               udev->port_is_suspended = 1;
                msleep(10);
        }
        usb_mark_last_busy(hub->hdev);
@@ -3040,6 +3041,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
  SuspendCleared:
        if (status == 0) {
+               udev->port_is_suspended = 0;
                if (hub_is_superspeed(hub->hdev)) {
                        if (portchange & USB_PORT_STAT_C_LINK_STATE)
                                clear_port_feature(hub->hdev, port1,
index 5df7c87b277f09b2e4e44e80985ab1e5587cea0a..f51f9981de1ed9157257f63fff244e4397c71fe1 100644 (file)
@@ -482,6 +482,7 @@ struct usb3_lpm_parameters {
  * @connect_time: time device was first connected
  * @do_remote_wakeup:  remote wakeup should be enabled
  * @reset_resume: needs reset instead of resume
+ * @port_is_suspended: the upstream port is suspended (L2 or U3)
  * @wusb_dev: if this is a Wireless USB device, link to the WUSB
  *     specific data for the device.
  * @slot_id: Slot ID assigned by xHCI
@@ -560,6 +561,7 @@ struct usb_device {
 
        unsigned do_remote_wakeup:1;
        unsigned reset_resume:1;
+       unsigned port_is_suspended:1;
 #endif
        struct wusb_dev *wusb_dev;
        int slot_id;