diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 1e741bca0265..eaa14514e173 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) 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", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 64854d76f529..e729e94cb751 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) (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, diff --git a/include/linux/usb.h b/include/linux/usb.h index 5df7c87b277f..f51f9981de1e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -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;