summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r--drivers/usb/core/driver.c105
1 files changed, 66 insertions, 39 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 6a3b5cae3a6e..ded550eda5d9 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -26,8 +26,9 @@
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
#include <linux/pm_runtime.h>
-#include "hcd.h"
+
#include "usb.h"
@@ -301,7 +302,7 @@ static int usb_probe_interface(struct device *dev)
intf->condition = USB_INTERFACE_BINDING;
- /* Bound interfaces are initially active. They are
+ /* Probed interfaces are initially active. They are
* runtime-PM-enabled only if the driver has autosuspend support.
* They are sensitive to their children's power states.
*/
@@ -333,7 +334,8 @@ static int usb_probe_interface(struct device *dev)
usb_cancel_queued_reset(intf);
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
- pm_runtime_disable(dev);
+ if (driver->supports_autosuspend)
+ pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
usb_autosuspend_device(udev);
@@ -388,7 +390,8 @@ static int usb_unbind_interface(struct device *dev)
intf->needs_remote_wakeup = 0;
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
- pm_runtime_disable(dev);
+ if (driver->supports_autosuspend)
+ pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
/* Undo any residual pm_autopm_get_interface_* calls */
@@ -437,14 +440,17 @@ int usb_driver_claim_interface(struct usb_driver *driver,
iface->condition = USB_INTERFACE_BOUND;
- /* Bound interfaces are initially active. They are
- * runtime-PM-enabled only if the driver has autosuspend support.
- * They are sensitive to their children's power states.
+ /* Claimed interfaces are initially inactive (suspended) and
+ * runtime-PM-enabled, but only if the driver has autosuspend
+ * support. Otherwise they are marked active, to prevent the
+ * device from being autosuspended, but left disabled. In either
+ * case they are sensitive to their children's power states.
*/
- pm_runtime_set_active(dev);
pm_suspend_ignore_children(dev, false);
if (driver->supports_autosuspend)
pm_runtime_enable(dev);
+ else
+ pm_runtime_set_active(dev);
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
@@ -1170,7 +1176,7 @@ done:
static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
- int i = 0;
+ int i = 0, n = 0;
struct usb_interface *intf;
if (udev->state == USB_STATE_NOTATTACHED ||
@@ -1179,7 +1185,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
- for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ n = udev->actconfig->desc.bNumInterfaces;
+ for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
status = usb_suspend_interface(udev, intf, msg);
if (status != 0)
@@ -1192,7 +1199,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* If the suspend failed, resume interfaces that did get suspended */
if (status != 0) {
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
- while (--i >= 0) {
+ while (++i < n) {
intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, msg, 0);
}
@@ -1263,13 +1270,47 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
return status;
}
+static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
+{
+ int w, i;
+ struct usb_interface *intf;
+
+ /* Remote wakeup is needed only when we actually go to sleep.
+ * For things like FREEZE and QUIESCE, if the device is already
+ * autosuspended then its current wakeup setting is okay.
+ */
+ if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) {
+ if (udev->state != USB_STATE_SUSPENDED)
+ udev->do_remote_wakeup = 0;
+ return;
+ }
+
+ /* If remote wakeup is permitted, see whether any interface drivers
+ * actually want it.
+ */
+ w = 0;
+ if (device_may_wakeup(&udev->dev) && udev->actconfig) {
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ w |= intf->needs_remote_wakeup;
+ }
+ }
+
+ /* If the device is autosuspended with the wrong wakeup setting,
+ * autoresume now so the setting can be changed.
+ */
+ if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup)
+ pm_runtime_resume(&udev->dev);
+ udev->do_remote_wakeup = w;
+}
+
/* The device lock is held by the PM core */
int usb_suspend(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
do_unbind_rebind(udev, DO_UNBIND);
- udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+ choose_wakeup(udev, msg);
return usb_suspend_both(udev, msg);
}
@@ -1320,13 +1361,9 @@ int usb_resume(struct device *dev, pm_message_t msg)
*
* The caller must hold @udev's device lock.
*/
-int usb_enable_autosuspend(struct usb_device *udev)
+void usb_enable_autosuspend(struct usb_device *udev)
{
- if (udev->autosuspend_disabled) {
- udev->autosuspend_disabled = 0;
- usb_autosuspend_device(udev);
- }
- return 0;
+ pm_runtime_allow(&udev->dev);
}
EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
@@ -1339,16 +1376,9 @@ EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
*
* The caller must hold @udev's device lock.
*/
-int usb_disable_autosuspend(struct usb_device *udev)
+void usb_disable_autosuspend(struct usb_device *udev)
{
- int rc = 0;
-
- if (!udev->autosuspend_disabled) {
- rc = usb_autoresume_device(udev);
- if (rc == 0)
- udev->autosuspend_disabled = 1;
- }
- return rc;
+ pm_runtime_forbid(&udev->dev);
}
EXPORT_SYMBOL_GPL(usb_disable_autosuspend);
@@ -1450,9 +1480,6 @@ int usb_autoresume_device(struct usb_device *udev)
* 0, a delayed autosuspend request for @intf's device is attempted. The
* attempt may fail (see autosuspend_check()).
*
- * If the driver has set @intf->needs_remote_wakeup then autosuspend will
- * take place only if the device's remote-wakeup facility is enabled.
- *
* This routine can run only in process context.
*/
void usb_autopm_put_interface(struct usb_interface *intf)
@@ -1495,7 +1522,7 @@ void usb_autopm_put_interface_async(struct usb_interface *intf)
atomic_dec(&intf->pm_usage_cnt);
pm_runtime_put_noidle(&intf->dev);
- if (!udev->autosuspend_disabled) {
+ if (udev->dev.power.runtime_auto) {
/* Optimization: Don't schedule a delayed autosuspend if
* the timer is already running and the expiration time
* wouldn't change.
@@ -1637,14 +1664,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
/* Internal routine to check whether we may autosuspend a device. */
static int autosuspend_check(struct usb_device *udev)
{
- int i;
+ int w, i;
struct usb_interface *intf;
unsigned long suspend_time, j;
/* Fail if autosuspend is disabled, or any interfaces are in use, or
* any interface drivers require remote wakeup but it isn't available.
*/
- udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+ w = 0;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
@@ -1658,12 +1685,7 @@ static int autosuspend_check(struct usb_device *udev)
continue;
if (atomic_read(&intf->dev.power.usage_count) > 0)
return -EBUSY;
- if (intf->needs_remote_wakeup &&
- !udev->do_remote_wakeup) {
- dev_dbg(&udev->dev, "remote wakeup needed "
- "for autosuspend\n");
- return -EOPNOTSUPP;
- }
+ w |= intf->needs_remote_wakeup;
/* Don't allow autosuspend if the device will need
* a reset-resume and any of its interface drivers
@@ -1679,6 +1701,11 @@ static int autosuspend_check(struct usb_device *udev)
}
}
}
+ if (w && !device_can_wakeup(&udev->dev)) {
+ dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ udev->do_remote_wakeup = w;
/* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.