aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-10-19 11:03:39 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-10-24 17:51:22 -0400
commitbfd1e910139be73fb0783a0b3171fc79e6afa031 (patch)
treed44b5d70c611c128ad5a511560905b9a834c2c1d
parent969ddcfc95c9a1849114fb72466d2fdea70f1d48 (diff)
USB: speed up usb_bus_resume()
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>
-rw-r--r--drivers/usb/core/hcd.c19
-rw-r--r--drivers/usb/core/hub.c2
-rw-r--r--include/linux/usb.h2
3 files changed, 21 insertions, 2 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 1e741bca026..eaa14514e17 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2039,8 +2039,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
2039 status = hcd->driver->bus_resume(hcd); 2039 status = hcd->driver->bus_resume(hcd);
2040 clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); 2040 clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
2041 if (status == 0) { 2041 if (status == 0) {
2042 /* TRSMRCY = 10 msec */ 2042 struct usb_device *udev;
2043 msleep(10); 2043 int port1;
2044
2044 spin_lock_irq(&hcd_root_hub_lock); 2045 spin_lock_irq(&hcd_root_hub_lock);
2045 if (!HCD_DEAD(hcd)) { 2046 if (!HCD_DEAD(hcd)) {
2046 usb_set_device_state(rhdev, rhdev->actconfig 2047 usb_set_device_state(rhdev, rhdev->actconfig
@@ -2050,6 +2051,20 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
2050 hcd->state = HC_STATE_RUNNING; 2051 hcd->state = HC_STATE_RUNNING;
2051 } 2052 }
2052 spin_unlock_irq(&hcd_root_hub_lock); 2053 spin_unlock_irq(&hcd_root_hub_lock);
2054
2055 /*
2056 * Check whether any of the enabled ports on the root hub are
2057 * unsuspended. If they are then a TRSMRCY delay is needed
2058 * (this is what the USB-2 spec calls a "global resume").
2059 * Otherwise we can skip the delay.
2060 */
2061 usb_hub_for_each_child(rhdev, port1, udev) {
2062 if (udev->state != USB_STATE_NOTATTACHED &&
2063 !udev->port_is_suspended) {
2064 usleep_range(10000, 11000); /* TRSMRCY */
2065 break;
2066 }
2067 }
2053 } else { 2068 } else {
2054 hcd->state = old_state; 2069 hcd->state = old_state;
2055 dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", 2070 dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 64854d76f52..e729e94cb75 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2876,6 +2876,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2876 (PMSG_IS_AUTO(msg) ? "auto-" : ""), 2876 (PMSG_IS_AUTO(msg) ? "auto-" : ""),
2877 udev->do_remote_wakeup); 2877 udev->do_remote_wakeup);
2878 usb_set_device_state(udev, USB_STATE_SUSPENDED); 2878 usb_set_device_state(udev, USB_STATE_SUSPENDED);
2879 udev->port_is_suspended = 1;
2879 msleep(10); 2880 msleep(10);
2880 } 2881 }
2881 usb_mark_last_busy(hub->hdev); 2882 usb_mark_last_busy(hub->hdev);
@@ -3040,6 +3041,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
3040 3041
3041 SuspendCleared: 3042 SuspendCleared:
3042 if (status == 0) { 3043 if (status == 0) {
3044 udev->port_is_suspended = 0;
3043 if (hub_is_superspeed(hub->hdev)) { 3045 if (hub_is_superspeed(hub->hdev)) {
3044 if (portchange & USB_PORT_STAT_C_LINK_STATE) 3046 if (portchange & USB_PORT_STAT_C_LINK_STATE)
3045 clear_port_feature(hub->hdev, port1, 3047 clear_port_feature(hub->hdev, port1,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 5df7c87b277..f51f9981de1 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -482,6 +482,7 @@ struct usb3_lpm_parameters {
482 * @connect_time: time device was first connected 482 * @connect_time: time device was first connected
483 * @do_remote_wakeup: remote wakeup should be enabled 483 * @do_remote_wakeup: remote wakeup should be enabled
484 * @reset_resume: needs reset instead of resume 484 * @reset_resume: needs reset instead of resume
485 * @port_is_suspended: the upstream port is suspended (L2 or U3)
485 * @wusb_dev: if this is a Wireless USB device, link to the WUSB 486 * @wusb_dev: if this is a Wireless USB device, link to the WUSB
486 * specific data for the device. 487 * specific data for the device.
487 * @slot_id: Slot ID assigned by xHCI 488 * @slot_id: Slot ID assigned by xHCI
@@ -560,6 +561,7 @@ struct usb_device {
560 561
561 unsigned do_remote_wakeup:1; 562 unsigned do_remote_wakeup:1;
562 unsigned reset_resume:1; 563 unsigned reset_resume:1;
564 unsigned port_is_suspended:1;
563#endif 565#endif
564 struct wusb_dev *wusb_dev; 566 struct wusb_dev *wusb_dev;
565 int slot_id; 567 int slot_id;