#include <linux/suspend.h>
#include <linux/export.h>
++++#include "power.h"
++++
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
/**
* genpd_queue_power_off_work - Queue up the execution of genpd_poweroff().
--- - * @genpd: PM domait to power off.
+++ + * @genpd: PM domain to power off.
*
* Queue up the execution of genpd_poweroff() unless it's already been done
* before.
queue_work(pm_wq, &genpd->power_off_work);
}
--- -static int genpd_poweron(struct generic_pm_domain *genpd);
--- -
/**
* __genpd_poweron - Restore power to a given PM domain and its masters.
* @genpd: PM domain to power up.
+++ + * @depth: nesting count for lockdep.
*
* Restore power to @genpd and all of its masters so that it is possible to
* resume a device belonging to it.
*/
--- -static int __genpd_poweron(struct generic_pm_domain *genpd)
+++ +static int __genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
{
struct gpd_link *link;
int ret = 0;
* with it.
*/
list_for_each_entry(link, &genpd->slave_links, slave_node) {
--- - genpd_sd_counter_inc(link->master);
+++ + struct generic_pm_domain *master = link->master;
+++ +
+++ + genpd_sd_counter_inc(master);
+++ +
+++ + mutex_lock_nested(&master->lock, depth + 1);
+++ + ret = __genpd_poweron(master, depth + 1);
+++ + mutex_unlock(&master->lock);
--- - ret = genpd_poweron(link->master);
if (ret) {
--- - genpd_sd_counter_dec(link->master);
+++ + genpd_sd_counter_dec(master);
goto err;
}
}
int ret;
mutex_lock(&genpd->lock);
--- - ret = __genpd_poweron(genpd);
+++ + ret = __genpd_poweron(genpd, 0);
mutex_unlock(&genpd->lock);
return ret;
}
+++ +
static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, int, save_state, dev);
struct generic_pm_domain *genpd;
bool (*stop_ok)(struct device *__dev);
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ + bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start;
s64 elapsed_ns;
int ret;
if (IS_ERR(genpd))
return -EINVAL;
+ + /*
+ + * A runtime PM centric subsystem/driver may re-use the runtime PM
+ + * callbacks for other purposes than runtime PM. In those scenarios
+ + * runtime PM is disabled. Under these circumstances, we shall skip
+ + * validating/measuring the PM QoS latency.
+ + */
stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
- - if (stop_ok && !stop_ok(dev))
+ + if (runtime_pm && stop_ok && !stop_ok(dev))
return -EBUSY;
/* Measure suspend latency. */
- - time_start = ktime_get();
+ + if (runtime_pm)
+ + time_start = ktime_get();
ret = genpd_save_dev(genpd, dev);
if (ret)
}
/* Update suspend latency value if the measured time exceeds it. */
- - elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- - if (elapsed_ns > td->suspend_latency_ns) {
- - td->suspend_latency_ns = elapsed_ns;
- - dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
- - elapsed_ns);
- - genpd->max_off_time_changed = true;
- - td->constraint_changed = true;
+ + if (runtime_pm) {
+ + elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
+ + if (elapsed_ns > td->suspend_latency_ns) {
+ + td->suspend_latency_ns = elapsed_ns;
+ + dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
+ + elapsed_ns);
+ + genpd->max_off_time_changed = true;
+ + td->constraint_changed = true;
+ + }
}
/*
{
struct generic_pm_domain *genpd;
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ + bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start;
s64 elapsed_ns;
int ret;
}
mutex_lock(&genpd->lock);
--- - ret = __genpd_poweron(genpd);
+++ + ret = __genpd_poweron(genpd, 0);
mutex_unlock(&genpd->lock);
if (ret)
out:
/* Measure resume latency. */
- - if (timed)
+ + if (timed && runtime_pm)
time_start = ktime_get();
genpd_start_dev(genpd, dev);
genpd_restore_dev(genpd, dev);
/* Update resume latency value if the measured time exceeds it. */
- - if (timed) {
+ + if (timed && runtime_pm) {
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->resume_latency_ns) {
td->resume_latency_ns = elapsed_ns;
}
dev->power.subsys_data->domain_data = &gpd_data->base;
---- dev->pm_domain = &genpd->domain;
spin_unlock_irq(&dev->power.lock);
++++ dev_pm_domain_set(dev, &genpd->domain);
++++
return gpd_data;
err_free:
static void genpd_free_dev_data(struct device *dev,
struct generic_pm_domain_data *gpd_data)
{
++++ dev_pm_domain_set(dev, NULL);
++++
spin_lock_irq(&dev->power.lock);
---- dev->pm_domain = NULL;
dev->power.subsys_data->domain_data = NULL;
spin_unlock_irq(&dev->power.lock);
return ret;
}
++ +EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
/**
* pm_genpd_remove_device - Remove a device from an I/O PM domain.
return ret;
}
++ +EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
/**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
if (!link)
return -ENOMEM;
--- - mutex_lock(&genpd->lock);
--- - mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+++ + mutex_lock(&subdomain->lock);
+++ + mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
if (genpd->status == GPD_STATE_POWER_OFF
&& subdomain->status != GPD_STATE_POWER_OFF) {
genpd_sd_counter_inc(genpd);
out:
--- - mutex_unlock(&subdomain->lock);
mutex_unlock(&genpd->lock);
+++ + mutex_unlock(&subdomain->lock);
if (ret)
kfree(link);
return ret;
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
return -EINVAL;
--- - mutex_lock(&genpd->lock);
+++ + mutex_lock(&subdomain->lock);
+++ + mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
if (!list_empty(&subdomain->slave_links) || subdomain->device_count) {
pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
if (link->slave != subdomain)
continue;
--- - mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
--- -
list_del(&link->master_node);
list_del(&link->slave_node);
kfree(link);
if (subdomain->status != GPD_STATE_POWER_OFF)
genpd_sd_counter_dec(genpd);
--- - mutex_unlock(&subdomain->lock);
--- -
ret = 0;
break;
}
out:
mutex_unlock(&genpd->lock);
+++ + mutex_unlock(&subdomain->lock);
return ret;
}
}
pd = of_genpd_get_from_provider(&pd_args);
+ + of_node_put(pd_args.np);
if (IS_ERR(pd)) {
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd));
- - of_node_put(dev->of_node);
return -EPROBE_DEFER;
}
if (ret < 0) {
dev_err(dev, "failed to add to PM domain %s: %d",
pd->name, ret);
- - of_node_put(dev->of_node);
goto out;
}