diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2017-11-24 16:31:32 +0800 |
---|---|---|
committer | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-24 12:41:33 +0300 |
commit | ef02492d48c1881a7f37963c1b337d63472a3253 (patch) | |
tree | bde174a29f1fb865c79113ee54e82d1009b5b5f4 | |
parent | 25bdccfd655cc3c36ab5ec0490d61cf15417a8fa (diff) |
MLK-17074-1 PM / Domains: support enter deepest state for multiple states domains
Currently the generic power domain suspend code pm_genpd_suspend_noirq
will try to power off a domain used by devices in genpd_sync_poweroff
if its status is not GPD_STATE_ACTIVE.
However, for power domains supporting multiple low power states, it may
already enter an intermediate low power state by runtime PM before system
suspend and the status is already GPD_STATE_POWER_OFF which results in
then the power domain stay at an intermediate low power state during
system suspend.
Let's give the power domain a chance to switch to the deepest state in
case it's already off but in an intermediate low power state.
Due to power domain is alway off, so no need to check device wakeup
case anymore.
Reviewed-by: Frank Li <frank.li@nxp.com>
Reviewed-by: Ranjani Vaidyanathan <Ranjani.Vaidyanathan@nxp.com>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
-rw-r--r-- | drivers/base/power/domain.c | 23 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 2 |
2 files changed, 23 insertions, 2 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2d59c2ec06e9..6915ce878713 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -112,6 +112,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) if (!genpd->power_on) return 0; + pr_debug("%s: Power-%s (idle state %d timed %s)\n", genpd->name, "on", + state_idx, timed ? "true" : "false"); + if (!timed) return genpd->power_on(genpd); @@ -142,6 +145,9 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) if (!genpd->power_off) return 0; + pr_debug("%s: Power-%s (idle state %d timed %s)\n", genpd->name, "off", + state_idx, timed ? "true" : "false"); + if (!timed) return genpd->power_off(genpd); @@ -632,7 +638,17 @@ static void genpd_sync_poweroff(struct generic_pm_domain *genpd) { struct gpd_link *link; - if (genpd->status == GPD_STATE_POWER_OFF) + /* + * Give the power domain a chance to switch to the deepest state in + * case it's already off but in an intermediate low power state. + * Due to power domain is alway off, so no need to check device wakeup + * here anymore + */ + + genpd->state_idx_saved = genpd->state_idx; + + if (genpd->status == GPD_STATE_POWER_OFF && + genpd->state_idx == (genpd->state_count - 1)) return; if (genpd->suspended_count != genpd->device_count @@ -643,6 +659,9 @@ static void genpd_sync_poweroff(struct generic_pm_domain *genpd) genpd->state_idx = genpd->state_count - 1; genpd_power_off(genpd, false); + if (genpd->status == GPD_STATE_POWER_OFF) + return; + genpd->status = GPD_STATE_POWER_OFF; list_for_each_entry(link, &genpd->slave_links, slave_node) { @@ -674,6 +693,8 @@ static void genpd_sync_poweron(struct generic_pm_domain *genpd) genpd_power_on(genpd, false); + /* restore save power domain state after resume */ + genpd->state_idx = genpd->state_idx_saved; genpd->status = GPD_STATE_ACTIVE; } diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index cf987dc35081..ee6776b6feb7 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -73,7 +73,7 @@ struct generic_pm_domain { struct genpd_power_state states[GENPD_MAX_NUM_STATES]; unsigned int state_count; /* number of states */ unsigned int state_idx; /* state that genpd will go to when off */ - + unsigned int state_idx_saved; /* saved power state for recovery after system suspend/resume */ }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) |