Commit ffe12855 authored by Ulf Hansson's avatar Ulf Hansson Committed by Rafael J. Wysocki

PM / Domains: Allow runtime PM callbacks to be re-used during system PM

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: ba2bbfbf (PM / Domains: Remove intermediate states from the power off sequence)
Reported-by: default avatarCao Minh Hiep <cm-hiep@jinso.co.jp>
Reported-by: default avatarHarunaga <nx-truong@jinso.co.jp>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Cc: 4.3+ <stable@vger.kernel.org> # 4.3+
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 265e2cf6
...@@ -390,6 +390,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -390,6 +390,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
bool (*stop_ok)(struct device *__dev); bool (*stop_ok)(struct device *__dev);
struct gpd_timing_data *td = &dev_gpd_data(dev)->td; struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start; ktime_t time_start;
s64 elapsed_ns; s64 elapsed_ns;
int ret; int ret;
...@@ -400,11 +401,18 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -400,11 +401,18 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; 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; 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; return -EBUSY;
/* Measure suspend latency. */ /* Measure suspend latency. */
if (runtime_pm)
time_start = ktime_get(); time_start = ktime_get();
ret = genpd_save_dev(genpd, dev); ret = genpd_save_dev(genpd, dev);
...@@ -418,6 +426,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -418,6 +426,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
} }
/* Update suspend latency value if the measured time exceeds it. */ /* Update suspend latency value if the measured time exceeds it. */
if (runtime_pm) {
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->suspend_latency_ns) { if (elapsed_ns > td->suspend_latency_ns) {
td->suspend_latency_ns = elapsed_ns; td->suspend_latency_ns = elapsed_ns;
...@@ -426,6 +435,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -426,6 +435,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
td->constraint_changed = true; td->constraint_changed = true;
} }
}
/* /*
* If power.irq_safe is set, this routine will be run with interrupts * If power.irq_safe is set, this routine will be run with interrupts
...@@ -453,6 +463,7 @@ static int pm_genpd_runtime_resume(struct device *dev) ...@@ -453,6 +463,7 @@ static int pm_genpd_runtime_resume(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
struct gpd_timing_data *td = &dev_gpd_data(dev)->td; struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start; ktime_t time_start;
s64 elapsed_ns; s64 elapsed_ns;
int ret; int ret;
...@@ -479,14 +490,14 @@ static int pm_genpd_runtime_resume(struct device *dev) ...@@ -479,14 +490,14 @@ static int pm_genpd_runtime_resume(struct device *dev)
out: out:
/* Measure resume latency. */ /* Measure resume latency. */
if (timed) if (timed && runtime_pm)
time_start = ktime_get(); time_start = ktime_get();
genpd_start_dev(genpd, dev); genpd_start_dev(genpd, dev);
genpd_restore_dev(genpd, dev); genpd_restore_dev(genpd, dev);
/* Update resume latency value if the measured time exceeds it. */ /* 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)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->resume_latency_ns) { if (elapsed_ns > td->resume_latency_ns) {
td->resume_latency_ns = elapsed_ns; td->resume_latency_ns = elapsed_ns;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment