From 8d9cc7f744e97c9eeca1b9eae765e2c60d49c204 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 24 Nov 2017 16:31:32 +0800 Subject: [PATCH] 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 Reviewed-by: Ranjani Vaidyanathan Signed-off-by: Dong Aisheng --- drivers/base/power/domain.c | 26 +++++++++++++++++++++++++- include/linux/pm_domain.h | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index f6204b324368..e6fab684884f 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -247,6 +247,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); @@ -277,6 +280,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); @@ -795,7 +801,20 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, { struct gpd_link *link; - if (!genpd_status_on(genpd) || genpd_is_always_on(genpd)) + /* + * 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_is_always_on(genpd)) + return; + + if (!genpd_status_on(genpd) && + genpd->state_idx == (genpd->state_count - 1)) return; if (genpd->suspended_count != genpd->device_count @@ -807,6 +826,9 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, if (_genpd_power_off(genpd, false)) return; + if (genpd->status == GPD_STATE_POWER_OFF) + return; + genpd->status = GPD_STATE_POWER_OFF; list_for_each_entry(link, &genpd->slave_links, slave_node) { @@ -854,6 +876,8 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, _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 ff88488ef2cc..49e92073517c 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -90,6 +90,7 @@ struct generic_pm_domain { }; }; + 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)