return retval != -EACCES ? retval : -EIO;
 }
 
+struct rpm_qos_data {
+       ktime_t time_now;
+       s64 constraint_ns;
+};
+
+/**
+ * rpm_update_qos_constraint - Update a given PM QoS constraint data.
+ * @dev: Device whose timing data to use.
+ * @data: PM QoS constraint data to update.
+ *
+ * Use the suspend timing data of @dev to update PM QoS constraint data pointed
+ * to by @data.
+ */
+static int rpm_update_qos_constraint(struct device *dev, void *data)
+{
+       struct rpm_qos_data *qos = data;
+       unsigned long flags;
+       s64 delta_ns;
+       int ret = 0;
+
+       spin_lock_irqsave(&dev->power.lock, flags);
+
+       if (dev->power.max_time_suspended_ns < 0)
+               goto out;
+
+       delta_ns = dev->power.max_time_suspended_ns -
+               ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time));
+       if (delta_ns <= 0) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0)
+               qos->constraint_ns = delta_ns;
+
+ out:
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+
+       return ret;
+}
+
 /**
  * rpm_suspend - Carry out runtime suspend of given device.
  * @dev: Device to suspend.
 {
        int (*callback)(struct device *);
        struct device *parent = NULL;
+       struct rpm_qos_data qos;
        int retval;
 
        trace_rpm_suspend(dev, rpmflags);
                goto out;
        }
 
+       qos.constraint_ns = __dev_pm_qos_read_value(dev);
+       if (qos.constraint_ns < 0) {
+               /* Negative constraint means "never suspend". */
+               retval = -EPERM;
+               goto out;
+       }
+       qos.constraint_ns *= NSEC_PER_USEC;
+       qos.time_now = ktime_get();
+
        __update_runtime_status(dev, RPM_SUSPENDING);
 
+       if (!dev->power.ignore_children) {
+               if (dev->power.irq_safe)
+                       spin_unlock(&dev->power.lock);
+               else
+                       spin_unlock_irq(&dev->power.lock);
+
+               retval = device_for_each_child(dev, &qos,
+                                              rpm_update_qos_constraint);
+
+               if (dev->power.irq_safe)
+                       spin_lock(&dev->power.lock);
+               else
+                       spin_lock_irq(&dev->power.lock);
+
+               if (retval)
+                       goto fail;
+       }
+
+       dev->power.suspend_time = qos.time_now;
+       dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1;
+
        if (dev->pm_domain)
                callback = dev->pm_domain->ops.runtime_suspend;
        else if (dev->type && dev->type->pm)
                callback = NULL;
 
        retval = rpm_callback(callback, dev);
-       if (retval) {
-               __update_runtime_status(dev, RPM_ACTIVE);
-               dev->power.deferred_resume = false;
-               if (retval == -EAGAIN || retval == -EBUSY) {
-                       dev->power.runtime_error = 0;
+       if (retval)
+               goto fail;
 
-                       /*
-                        * 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);
-               }
-               wake_up_all(&dev->power.wait_queue);
-               goto out;
-       }
  no_callback:
        __update_runtime_status(dev, RPM_SUSPENDED);
        pm_runtime_deactivate_timer(dev);
        trace_rpm_return_int(dev, _THIS_IP_, retval);
 
        return retval;
+
+ fail:
+       __update_runtime_status(dev, RPM_ACTIVE);
+       dev->power.suspend_time = ktime_set(0, 0);
+       dev->power.max_time_suspended_ns = -1;
+       dev->power.deferred_resume = false;
+       if (retval == -EAGAIN || retval == -EBUSY) {
+               dev->power.runtime_error = 0;
+
+               /*
+                * 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);
+       }
+       wake_up_all(&dev->power.wait_queue);
+       goto out;
 }
 
 /**
        if (dev->power.no_callbacks)
                goto no_callback;       /* Assume success. */
 
+       dev->power.suspend_time = ktime_set(0, 0);
+       dev->power.max_time_suspended_ns = -1;
+
        __update_runtime_status(dev, RPM_RESUMING);
 
        if (dev->pm_domain)
        setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
                        (unsigned long)dev);
 
+       dev->power.suspend_time = ktime_set(0, 0);
+       dev->power.max_time_suspended_ns = -1;
+
        init_waitqueue_head(&dev->power.wait_queue);
 }
 
        if (dev->power.irq_safe && dev->parent)
                pm_runtime_put_sync(dev->parent);
 }
+
+/**
+ * pm_runtime_update_max_time_suspended - Update device's suspend time data.
+ * @dev: Device to handle.
+ * @delta_ns: Value to subtract from the device's max_time_suspended_ns field.
+ *
+ * Update the device's power.max_time_suspended_ns field by subtracting
+ * @delta_ns from it.  The resulting value of power.max_time_suspended_ns is
+ * never negative.
+ */
+void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->power.lock, flags);
+
+       if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) {
+               if (dev->power.max_time_suspended_ns > delta_ns)
+                       dev->power.max_time_suspended_ns -= delta_ns;
+               else
+                       dev->power.max_time_suspended_ns = 0;
+       }
+
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+}