diff options
Diffstat (limited to 'drivers/usb/core/generic.c')
-rw-r--r-- | drivers/usb/core/generic.c | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index e7ec9b6b7a9..7cbf992adcc 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include "usb.h" +#include "hcd.h" static inline const char *plural(int n) { @@ -193,12 +194,46 @@ static void generic_disconnect(struct usb_device *udev) static int generic_suspend(struct usb_device *udev, pm_message_t msg) { - return usb_port_suspend(udev); + int rc; + + rc = usb_port_suspend(udev); + + /* Root hubs don't have upstream ports to suspend, + * so the line above won't do much for them. We have to + * shut down their downstream HC-to-USB interfaces manually, + * by doing a bus (or "global") suspend. + */ + if (rc == 0 && !udev->parent) { + rc = hcd_bus_suspend(udev->bus); + if (rc) { + dev_dbg(&udev->dev, "'global' suspend %d\n", rc); + usb_port_resume(udev); + } + } + return rc; } static int generic_resume(struct usb_device *udev) { - return usb_port_resume(udev); + int rc; + + rc = usb_port_resume(udev); + + /* Root hubs don't have upstream ports to resume or reset, + * so the line above won't do much for them. We have to + * start up their downstream HC-to-USB interfaces manually, + * by doing a bus (or "global") resume. + */ + if (rc == 0 && !udev->parent) { + rc = hcd_bus_resume(udev->bus); + if (rc) + dev_dbg(&udev->dev, "'global' resume %d\n", rc); + else { + /* TRSMRCY = 10 msec */ + msleep(10); + } + } + return rc; } #endif /* CONFIG_PM */ |