#include <linux/io.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/sched.h>
        ktime_t __start = ktime_get();                                          \
        type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);         \
        s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));           \
-       struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev);          \
-       if (__elapsed > __gpd_data->td.field) {                                 \
-               __gpd_data->td.field = __elapsed;                               \
+       struct gpd_timing_data *__td = &dev_gpd_data(dev)->td;                  \
+       if (!__retval && __elapsed > __td->field) {                             \
+               __td->field = __elapsed;                                        \
                dev_warn(dev, name " latency exceeded, new value %lld ns\n",    \
                        __elapsed);                                             \
+               genpd->max_off_time_changed = true;                             \
+               __td->constraint_changed = true;                                \
        }                                                                       \
        __retval;                                                               \
 })
                elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
                if (elapsed_ns > genpd->power_on_latency_ns) {
                        genpd->power_on_latency_ns = elapsed_ns;
+                       genpd->max_off_time_changed = true;
                        if (genpd->name)
                                pr_warning("%s: Power-on latency exceeded, "
                                        "new value %lld ns\n", genpd->name,
 
 #ifdef CONFIG_PM_RUNTIME
 
+static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
+                                    unsigned long val, void *ptr)
+{
+       struct generic_pm_domain_data *gpd_data;
+       struct device *dev;
+
+       gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
+
+       mutex_lock(&gpd_data->lock);
+       dev = gpd_data->base.dev;
+       if (!dev) {
+               mutex_unlock(&gpd_data->lock);
+               return NOTIFY_DONE;
+       }
+       mutex_unlock(&gpd_data->lock);
+
+       for (;;) {
+               struct generic_pm_domain *genpd;
+               struct pm_domain_data *pdd;
+
+               spin_lock_irq(&dev->power.lock);
+
+               pdd = dev->power.subsys_data ?
+                               dev->power.subsys_data->domain_data : NULL;
+               if (pdd) {
+                       to_gpd_data(pdd)->td.constraint_changed = true;
+                       genpd = dev_to_genpd(dev);
+               } else {
+                       genpd = ERR_PTR(-ENODATA);
+               }
+
+               spin_unlock_irq(&dev->power.lock);
+
+               if (!IS_ERR(genpd)) {
+                       mutex_lock(&genpd->lock);
+                       genpd->max_off_time_changed = true;
+                       mutex_unlock(&genpd->lock);
+               }
+
+               dev = dev->parent;
+               if (!dev || dev->power.ignore_children)
+                       break;
+       }
+
+       return NOTIFY_DONE;
+}
+
 /**
  * __pm_genpd_save_device - Save the pre-suspend state of a device.
  * @pdd: Domain data of the device to save the state of.
                return 0;
        }
 
-       genpd->max_off_time_ns = -1;
        if (genpd->gov && genpd->gov->power_down_ok) {
                if (!genpd->gov->power_down_ok(&genpd->domain))
                        return -EAGAIN;
                elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
                if (elapsed_ns > genpd->power_off_latency_ns) {
                        genpd->power_off_latency_ns = elapsed_ns;
+                       genpd->max_off_time_changed = true;
                        if (genpd->name)
                                pr_warning("%s: Power-off latency exceeded, "
                                        "new value %lld ns\n", genpd->name,
        if (dev_gpd_data(dev)->always_on)
                return -EBUSY;
 
-       dev_gpd_data(dev)->td.effective_constraint_ns = -1;
        stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
        if (stop_ok && !stop_ok(dev))
                return -EBUSY;
 
 #else
 
+static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
+                                           unsigned long val, void *ptr)
+{
+       return NOTIFY_DONE;
+}
+
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
 
 #define pm_genpd_runtime_suspend       NULL
        if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
                return -EINVAL;
 
+       gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
+       if (!gpd_data)
+               return -ENOMEM;
+
+       mutex_init(&gpd_data->lock);
+       gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
+       dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+
        genpd_acquire_lock(genpd);
 
        if (genpd->status == GPD_STATE_POWER_OFF) {
                        goto out;
                }
 
-       gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
-       if (!gpd_data) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
        genpd->device_count++;
+       genpd->max_off_time_changed = true;
 
-       dev->pm_domain = &genpd->domain;
        dev_pm_get_subsys_data(dev);
+
+       mutex_lock(&gpd_data->lock);
+       spin_lock_irq(&dev->power.lock);
+       dev->pm_domain = &genpd->domain;
        dev->power.subsys_data->domain_data = &gpd_data->base;
        gpd_data->base.dev = dev;
-       gpd_data->need_restore = false;
        list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
+       gpd_data->need_restore = false;
        if (td)
                gpd_data->td = *td;
 
+       gpd_data->td.constraint_changed = true;
+       gpd_data->td.effective_constraint_ns = -1;
+       spin_unlock_irq(&dev->power.lock);
+       mutex_unlock(&gpd_data->lock);
+
+       genpd_release_lock(genpd);
+
+       return 0;
+
  out:
        genpd_release_lock(genpd);
 
+       dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+       kfree(gpd_data);
        return ret;
 }
 
 int pm_genpd_remove_device(struct generic_pm_domain *genpd,
                           struct device *dev)
 {
+       struct generic_pm_domain_data *gpd_data;
        struct pm_domain_data *pdd;
        int ret = 0;
 
                goto out;
        }
 
+       genpd->device_count--;
+       genpd->max_off_time_changed = true;
+
+       spin_lock_irq(&dev->power.lock);
        dev->pm_domain = NULL;
        pdd = dev->power.subsys_data->domain_data;
        list_del_init(&pdd->list_node);
        dev->power.subsys_data->domain_data = NULL;
-       dev_pm_put_subsys_data(dev);
-       kfree(to_gpd_data(pdd));
+       spin_unlock_irq(&dev->power.lock);
 
-       genpd->device_count--;
+       gpd_data = to_gpd_data(pdd);
+       mutex_lock(&gpd_data->lock);
+       pdd->dev = NULL;
+       mutex_unlock(&gpd_data->lock);
+
+       genpd_release_lock(genpd);
+
+       dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+       kfree(gpd_data);
+       dev_pm_put_subsys_data(dev);
+       return 0;
 
  out:
        genpd_release_lock(genpd);
        genpd->resume_count = 0;
        genpd->device_count = 0;
        genpd->max_off_time_ns = -1;
+       genpd->max_off_time_changed = true;
        genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
        genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
        genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
 
 bool default_stop_ok(struct device *dev)
 {
        struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+       unsigned long flags;
        s64 constraint_ns;
 
        dev_dbg(dev, "%s()\n", __func__);
 
-       constraint_ns = dev_pm_qos_read_value(dev);
+       spin_lock_irqsave(&dev->power.lock, flags);
+
+       if (!td->constraint_changed) {
+               bool ret = td->cached_stop_ok;
+
+               spin_unlock_irqrestore(&dev->power.lock, flags);
+               return ret;
+       }
+       td->constraint_changed = false;
+       td->cached_stop_ok = false;
+       td->effective_constraint_ns = -1;
+       constraint_ns = __dev_pm_qos_read_value(dev);
+
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+
        if (constraint_ns < 0)
                return false;
 
        constraint_ns *= NSEC_PER_USEC;
        /*
         * We can walk the children without any additional locking, because
-        * they all have been suspended at this point.
+        * they all have been suspended at this point and their
+        * effective_constraint_ns fields won't be modified in parallel with us.
         */
        if (!dev->power.ignore_children)
                device_for_each_child(dev, &constraint_ns,
                        return false;
        }
        td->effective_constraint_ns = constraint_ns;
+       td->cached_stop_ok = constraint_ns > td->stop_latency_ns ||
+                               constraint_ns == 0;
        /*
         * The children have been suspended already, so we don't need to take
         * their stop latencies into account here.
         */
-       return constraint_ns > td->stop_latency_ns || constraint_ns == 0;
+       return td->cached_stop_ok;
 }
 
 /**
        s64 min_dev_off_time_ns;
        s64 off_on_time_ns;
 
+       if (genpd->max_off_time_changed) {
+               struct gpd_link *link;
+
+               /*
+                * We have to invalidate the cached results for the masters, so
+                * use the observation that default_power_down_ok() is not
+                * going to be called for any master until this instance
+                * returns.
+                */
+               list_for_each_entry(link, &genpd->slave_links, slave_node)
+                       link->master->max_off_time_changed = true;
+
+               genpd->max_off_time_changed = false;
+               genpd->cached_power_down_ok = false;
+               genpd->max_off_time_ns = -1;
+       } else {
+               return genpd->cached_power_down_ok;
+       }
+
        off_on_time_ns = genpd->power_off_latency_ns +
                                genpd->power_on_latency_ns;
        /*
                        min_dev_off_time_ns = constraint_ns;
        }
 
+       genpd->cached_power_down_ok = true;
+
        /*
         * If the computed minimum device off time is negative, there are no
         * latency constraints, so the domain can spend arbitrary time in the