summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2017-11-24 16:31:32 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commitef02492d48c1881a7f37963c1b337d63472a3253 (patch)
treebde174a29f1fb865c79113ee54e82d1009b5b5f4
parent25bdccfd655cc3c36ab5ec0490d61cf15417a8fa (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.c23
-rw-r--r--include/linux/pm_domain.h2
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)