Skip to content

Commit

Permalink
PM / Domains: Allow runtime PM callbacks to be re-used during system PM
Browse files Browse the repository at this point in the history
A runtime PM centric subsystem/driver may typically use the runtime PM
helpers, pm_runtime_force_suspend|resume() in the system PM path. This
means the genpd's runtime PM callbacks might be invoked even when runtime
PM has been disabled for the device.

To properly cope with these and similar scenarios when these helper
functions are used, change genpd to skip validating and measuring the
device PM QOS latency.

This is needed because otherwise genpd may prevent the device to be put
into low power state. If this occurs during system PM, it causes the
sequence to be aborted as a device's system PM callback returns -EBUSY.

Fixes: ba2bbfb (PM / Domains: Remove intermediate states from the power off sequence)
Reported-by: Cao Minh Hiep <cm-hiep@jinso.co.jp>
Reported-by: Harunaga <nx-truong@jinso.co.jp>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Cc: 4.3+ <stable@vger.kernel.org> # 4.3+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Ulf Hansson authored and Rafael J. Wysocki committed Dec 10, 2015
1 parent 265e2cf commit ffe1285
Showing 1 changed file with 22 additions and 11 deletions.
33 changes: 22 additions & 11 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ static int pm_genpd_runtime_suspend(struct device *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;
Expand All @@ -400,12 +401,19 @@ static int pm_genpd_runtime_suspend(struct device *dev)
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)
Expand All @@ -418,13 +426,15 @@ static int pm_genpd_runtime_suspend(struct device *dev)
}

/* 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;
}
}

/*
Expand Down Expand Up @@ -453,6 +463,7 @@ static int pm_genpd_runtime_resume(struct device *dev)
{
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;
Expand All @@ -479,14 +490,14 @@ static int pm_genpd_runtime_resume(struct device *dev)

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;
Expand Down

0 comments on commit ffe1285

Please sign in to comment.