summaryrefslogtreecommitdiff
path: root/drivers/base/power/domain.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r--drivers/base/power/domain.c58
1 files changed, 37 insertions, 21 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 8428d02cfe58..3dadc7df9aed 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -448,6 +448,9 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
if (!genpd->power_off)
return 0;
+ if (atomic_read(&genpd->sd_count) > 0)
+ return -EBUSY;
+
if (!timed)
return genpd->power_off(genpd);
@@ -497,6 +500,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
struct pm_domain_data *pdd;
struct gpd_link *link;
unsigned int not_suspended = 0;
+ int ret;
/*
* Do not try to power off the domain in the following situations:
@@ -544,24 +548,21 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
if (!genpd->gov)
genpd->state_idx = 0;
- if (genpd->power_off) {
- int ret;
+ /* Choose the deepest state if no devices using this domain */
+ if (!genpd->device_count)
+ genpd->state_idx = genpd->state_count - 1;
- if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
-
- /*
- * If sd_count > 0 at this point, one of the subdomains hasn't
- * managed to call genpd_power_on() for the master yet after
- * incrementing it. In that case genpd_power_on() will wait
- * for us to drop the lock, so we can call .power_off() and let
- * the genpd_power_on() restore power for us (this shouldn't
- * happen very often).
- */
- ret = _genpd_power_off(genpd, true);
- if (ret)
- return ret;
- }
+ /*
+ * If sd_count > 0 at this point, one of the subdomains hasn't
+ * managed to call genpd_power_on() for the master yet after
+ * incrementing it. In that case genpd_power_on() will wait
+ * for us to drop the lock, so we can call .power_off() and let
+ * the genpd_power_on() restore power for us (this shouldn't
+ * happen very often).
+ */
+ ret = _genpd_power_off(genpd, true);
+ if (ret)
+ return ret;
genpd->status = GPD_STATE_POWER_OFF;
genpd_update_accounting(genpd);
@@ -960,11 +961,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.
+ */
+ genpd->state_idx_saved = genpd->state_idx;
+
+ if (genpd_is_always_on(genpd))
return;
- if (genpd->suspended_count != genpd->device_count
- || atomic_read(&genpd->sd_count) > 0)
+ if (!genpd_status_on(genpd) &&
+ genpd->state_idx == (genpd->state_count - 1))
+ return;
+
+ if (genpd->suspended_count != genpd->device_count)
return;
/* Choose the deepest state when suspending */
@@ -972,6 +982,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) {
@@ -1019,6 +1032,9 @@ 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;
}
@@ -1829,7 +1845,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
return ret;
}
} else if (!gov && genpd->state_count > 1) {
- pr_warn("%s: no governor for states\n", genpd->name);
+ pr_debug("%s: no governor for states\n", genpd->name);
}
device_initialize(&genpd->dev);