Merge remote-tracking branch 'origin/develop-3.0' into develop-3.0-jb
[firefly-linux-kernel-4.4.55.git] / drivers / base / power / runtime.c
index 0d4587b15c5599372e7b9f917fcea636404bf8d9..184cf54fa01cfbde621a31047866e8c0f1246ade 100644 (file)
@@ -278,6 +278,9 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
  * 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.
  */
@@ -389,10 +392,21 @@ static int rpm_suspend(struct device *dev, int rpmflags)
        if (retval) {
                __update_runtime_status(dev, RPM_ACTIVE);
                dev->power.deferred_resume = 0;
-               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);
@@ -732,6 +746,8 @@ int __pm_runtime_idle(struct device *dev, int rpmflags)
        unsigned long flags;
        int retval;
 
+       might_sleep_if(!(rpmflags & RPM_ASYNC));
+
        if (rpmflags & RPM_GET_PUT) {
                if (!atomic_dec_and_test(&dev->power.usage_count))
                        return 0;
@@ -761,6 +777,8 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags)
        unsigned long flags;
        int retval;
 
+       might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
        if (rpmflags & RPM_GET_PUT) {
                if (!atomic_dec_and_test(&dev->power.usage_count))
                        return 0;
@@ -789,6 +807,8 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
        unsigned long flags;
        int retval;
 
+       might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe);
+
        if (rpmflags & RPM_GET_PUT)
                atomic_inc(&dev->power.usage_count);
 
@@ -978,6 +998,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_barrier);
  */
 void __pm_runtime_disable(struct device *dev, bool check_resume)
 {
+       might_sleep();
        spin_lock_irq(&dev->power.lock);
 
        if (dev->power.disable_depth > 0) {
@@ -1184,6 +1205,8 @@ void __pm_runtime_use_autosuspend(struct device *dev, bool use)
 {
        int old_delay, old_use;
 
+       might_sleep();
+
        spin_lock_irq(&dev->power.lock);
        old_delay = dev->power.autosuspend_delay;
        old_use = dev->power.use_autosuspend;