--- /dev/null
+From 886486b792e4f6f96d4fbe8ec5bf20811cab7d6a Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Thu, 3 Nov 2011 23:39:18 +0100
+Subject: PM / Runtime: Automatically retry failed autosuspends
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit 886486b792e4f6f96d4fbe8ec5bf20811cab7d6a upstream.
+
+Originally, the runtime PM core would send an idle notification
+whenever a suspend attempt failed. The idle callback routine could
+then schedule a delayed suspend for some time later.
+
+However this behavior was changed by commit
+f71648d73c1650b8b4aceb3856bebbde6daa3b86 (PM / Runtime: Remove idle
+notification after failing suspend). No notifications were sent, and
+there was no clear mechanism to retry failed suspends.
+
+This caused problems for the usbhid driver, because it fails
+autosuspend attempts as long as a key is being held down. Therefore
+this patch (as1492) adds a mechanism for retrying failed
+autosuspends. If the callback routine updates the last_busy field so
+that the next autosuspend expiration time is in the future, the
+autosuspend will automatically be rescheduled.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Tested-by: Henrik Rydberg <rydberg@euromail.se>
+Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/power/runtime_pm.txt | 10 ++++++++++
+ drivers/base/power/runtime.c | 18 ++++++++++++++++--
+ 2 files changed, 26 insertions(+), 2 deletions(-)
+
+--- a/Documentation/power/runtime_pm.txt
++++ b/Documentation/power/runtime_pm.txt
+@@ -782,6 +782,16 @@ will behave normally, not taking the aut
+ Similarly, if the power.use_autosuspend field isn't set then the autosuspend
+ helper functions will behave just like the non-autosuspend counterparts.
+
++Under some circumstances a driver or subsystem may want to prevent a device
++from autosuspending immediately, even though the usage counter is zero and the
++autosuspend delay time has expired. If the ->runtime_suspend() callback
++returns -EAGAIN or -EBUSY, and if the next autosuspend delay expiration time is
++in the future (as it normally would be if the callback invoked
++pm_runtime_mark_last_busy()), the PM core will automatically reschedule the
++autosuspend. The ->runtime_suspend() callback can't do this rescheduling
++itself because no suspend requests of any kind are accepted while the device is
++suspending (i.e., while the callback is running).
++
+ The implementation is well suited for asynchronous use in interrupt contexts.
+ However such use inevitably involves races, because the PM core can't
+ synchronize ->runtime_suspend() callbacks with the arrival of I/O requests.
+--- a/drivers/base/power/runtime.c
++++ b/drivers/base/power/runtime.c
+@@ -285,6 +285,9 @@ static int rpm_callback(int (*cb)(struct
+ * If a deferred resume was requested while the callback was running then carry
+ * it out; otherwise send an idle notification for the device (if the suspend
+ * failed) or for its parent (if the suspend succeeded).
++ * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO
++ * flag is set and the next autosuspend-delay expiration time is in the
++ * future, schedule another autosuspend attempt.
+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */
+@@ -396,10 +399,21 @@ static int rpm_suspend(struct device *de
+ if (retval) {
+ __update_runtime_status(dev, RPM_ACTIVE);
+ dev->power.deferred_resume = false;
+- if (retval == -EAGAIN || retval == -EBUSY)
++ if (retval == -EAGAIN || retval == -EBUSY) {
+ dev->power.runtime_error = 0;
+- else
++
++ /*
++ * If the callback routine failed an autosuspend, and
++ * if the last_busy time has been updated so that there
++ * is a new autosuspend expiration time, automatically
++ * reschedule another autosuspend.
++ */
++ if ((rpmflags & RPM_AUTO) &&
++ pm_runtime_autosuspend_expiration(dev) != 0)
++ goto repeat;
++ } else {
+ pm_runtime_cancel_pending(dev);
++ }
+ } else {
+ no_callback:
+ __update_runtime_status(dev, RPM_SUSPENDED);