diff options
Diffstat (limited to 'drivers/base/power/domain.c')
-rw-r--r-- | drivers/base/power/domain.c | 58 |
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); |