mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-20 14:16:02 +00:00
PM / Domains: Add "wait for parent" status for generic PM domains
The next patch will make it possible for a generic PM domain to have multiple parents (i.e. multiple PM domains it depends on). To prepare for that change it is necessary to change pm_genpd_poweron() so that it doesn't jump to the start label after running itself recursively for the parent domain. For this purpose, introduce a new PM domain status value GPD_STATE_WAIT_PARENT that will be set by pm_genpd_poweron() before calling itself recursively for the parent domain and modify the code in drivers/base/power/domain.c so that the GPD_STATE_WAIT_PARENT status is guaranteed to be preserved during the execution of pm_genpd_poweron() for the parent. This change also causes pm_genpd_add_subdomain() and pm_genpd_remove_subdomain() to wait for started pm_genpd_poweron() to complete and allows pm_genpd_runtime_resume() to avoid dropping the lock after powering on the PM domain. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
This commit is contained in:
parent
9e08cf4296
commit
3f241775c3
2 changed files with 61 additions and 30 deletions
|
@ -81,45 +81,59 @@ static void genpd_set_active(struct generic_pm_domain *genpd)
|
|||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_poweron - Restore power to a given PM domain and its parents.
|
||||
* __pm_genpd_poweron - Restore power to a given PM domain and its parents.
|
||||
* @genpd: PM domain to power up.
|
||||
*
|
||||
* Restore power to @genpd and all of its parents so that it is possible to
|
||||
* resume a device belonging to it.
|
||||
*/
|
||||
int pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
int __pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
__releases(&genpd->lock) __acquires(&genpd->lock)
|
||||
{
|
||||
struct generic_pm_domain *parent;
|
||||
DEFINE_WAIT(wait);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&genpd->lock);
|
||||
/* If the domain's parent is being waited for, we have to wait too. */
|
||||
for (;;) {
|
||||
prepare_to_wait(&genpd->status_wait_queue, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
if (genpd->status != GPD_STATE_WAIT_PARENT)
|
||||
break;
|
||||
mutex_unlock(&genpd->lock);
|
||||
|
||||
parent = genpd->parent;
|
||||
schedule();
|
||||
|
||||
mutex_lock(&genpd->lock);
|
||||
}
|
||||
finish_wait(&genpd->status_wait_queue, &wait);
|
||||
|
||||
start:
|
||||
if (genpd->status == GPD_STATE_ACTIVE
|
||||
|| (genpd->prepared_count > 0 && genpd->suspend_power_off))
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
if (genpd->status != GPD_STATE_POWER_OFF) {
|
||||
genpd_set_active(genpd);
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
genpd_sd_counter_inc(parent);
|
||||
if (genpd->parent) {
|
||||
genpd_sd_counter_inc(genpd->parent);
|
||||
genpd->status = GPD_STATE_WAIT_PARENT;
|
||||
|
||||
mutex_unlock(&genpd->lock);
|
||||
|
||||
ret = pm_genpd_poweron(parent);
|
||||
ret = pm_genpd_poweron(genpd->parent);
|
||||
|
||||
mutex_lock(&genpd->lock);
|
||||
|
||||
/*
|
||||
* The "wait for parent" status is guaranteed not to change
|
||||
* while the parent is powering on.
|
||||
*/
|
||||
genpd->status = GPD_STATE_POWER_OFF;
|
||||
wake_up_all(&genpd->status_wait_queue);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
parent = NULL;
|
||||
goto start;
|
||||
}
|
||||
|
||||
if (genpd->power_on) {
|
||||
|
@ -130,16 +144,27 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
|
|||
|
||||
genpd_set_active(genpd);
|
||||
|
||||
out:
|
||||
mutex_unlock(&genpd->lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (genpd->parent)
|
||||
genpd_sd_counter_dec(genpd->parent);
|
||||
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_genpd_poweron - Restore power to a given PM domain and its parents.
|
||||
* @genpd: PM domain to power up.
|
||||
*/
|
||||
int pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&genpd->lock);
|
||||
ret = __pm_genpd_poweron(genpd);
|
||||
mutex_unlock(&genpd->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -225,7 +250,8 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle,
|
|||
*/
|
||||
static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
|
||||
{
|
||||
return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
|
||||
return genpd->status == GPD_STATE_WAIT_PARENT
|
||||
|| genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,11 +287,13 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|
|||
/*
|
||||
* Do not try to power off the domain in the following situations:
|
||||
* (1) The domain is already in the "power off" state.
|
||||
* (2) System suspend is in progress.
|
||||
* (2) The domain is waiting for its parent to power up.
|
||||
* (3) One of the domain's devices is being resumed right now.
|
||||
* (4) System suspend is in progress.
|
||||
*/
|
||||
if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0
|
||||
|| genpd->resume_count > 0)
|
||||
if (genpd->status == GPD_STATE_POWER_OFF
|
||||
|| genpd->status == GPD_STATE_WAIT_PARENT
|
||||
|| genpd->resume_count > 0 || genpd->prepared_count > 0)
|
||||
return 0;
|
||||
|
||||
if (atomic_read(&genpd->sd_count) > 0)
|
||||
|
@ -299,14 +327,15 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|
|||
list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
|
||||
ret = atomic_read(&genpd->sd_count) == 0 ?
|
||||
__pm_genpd_save_device(dle, genpd) : -EBUSY;
|
||||
|
||||
if (genpd_abort_poweroff(genpd))
|
||||
goto out;
|
||||
|
||||
if (ret) {
|
||||
genpd_set_active(genpd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (genpd_abort_poweroff(genpd))
|
||||
goto out;
|
||||
|
||||
if (genpd->status == GPD_STATE_REPEAT) {
|
||||
genpd->poweroff_task = NULL;
|
||||
goto start;
|
||||
|
@ -432,11 +461,12 @@ static int pm_genpd_runtime_resume(struct device *dev)
|
|||
if (IS_ERR(genpd))
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_genpd_poweron(genpd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&genpd->lock);
|
||||
ret = __pm_genpd_poweron(genpd);
|
||||
if (ret) {
|
||||
mutex_unlock(&genpd->lock);
|
||||
return ret;
|
||||
}
|
||||
genpd->status = GPD_STATE_BUSY;
|
||||
genpd->resume_count++;
|
||||
for (;;) {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
enum gpd_status {
|
||||
GPD_STATE_ACTIVE = 0, /* PM domain is active */
|
||||
GPD_STATE_WAIT_PARENT, /* PM domain's parent is being waited for */
|
||||
GPD_STATE_BUSY, /* Something is happening to the PM domain */
|
||||
GPD_STATE_REPEAT, /* Power off in progress, to be repeated */
|
||||
GPD_STATE_POWER_OFF, /* PM domain is off */
|
||||
|
|
Loading…
Reference in a new issue