diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8d989b1d3dc5..327b927afb52 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1333,8 +1333,10 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); - if (!status) + if (!status) { udev->reset_resume = 0; + udev->reset_resume_warm = 0; + } return status; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a7c04e24ca48..30ce237569dd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2783,8 +2783,14 @@ static int check_port_resume_type(struct usb_device *udev, struct usb_hub *hub, int port1, int status, unsigned portchange, unsigned portstatus) { + /* Did the port go SS.Inactive? Even if ->persist_enabled is cleared the + * device won't come back until a warm reset completes + */ + if (hub_port_warm_reset_required(hub, portstatus)) { + udev->reset_resume = 1; + udev->reset_resume_warm = 1; /* Is the device still present? */ - if (status || port_is_suspended(hub, portstatus) || + } else if (status || port_is_suspended(hub, portstatus) || !port_is_power_on(hub, portstatus) || !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) @@ -4022,7 +4028,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay, false); + retval = hub_port_reset(hub, port1, udev, delay, + udev->reset_resume_warm); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ @@ -4730,7 +4737,8 @@ static void hub_events(void) /* deal with port status changes */ for (i = 1; i <= hdev->maxchild; i++) { - if (test_bit(i, hub->busy_bits)) + if (test_bit(i, hub->busy_bits) || + test_bit(i, hub->delayed_change_bits)) continue; connect_change = test_bit(i, hub->change_bits); wakeup_change = test_and_clear_bit(i, hub->wakeup_bits); diff --git a/include/linux/usb.h b/include/linux/usb.h index 7454865ad148..ff1b6fe4a0ff 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -572,6 +572,7 @@ struct usb_device { unsigned do_remote_wakeup:1; unsigned reset_resume:1; + unsigned reset_resume_warm:1; unsigned port_is_suspended:1; #endif struct wusb_dev *wusb_dev;