Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 256944
b: refs/heads/master
c: c6d22b3
h: refs/heads/master
v: v3
  • Loading branch information
Rafael J. Wysocki committed Jul 11, 2011
1 parent ed319db commit 852e260
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 46 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 17b75eca7683d4942f4d8d00563fd15f37c39589
refs/heads/master: c6d22b37263607ba5aeeb2e11169fa65caa29bee
144 changes: 99 additions & 45 deletions trunk/drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ static void genpd_acquire_lock(struct generic_pm_domain *genpd)
for (;;) {
prepare_to_wait(&genpd->status_wait_queue, &wait,
TASK_UNINTERRUPTIBLE);
if (genpd->status != GPD_STATE_BUSY)
if (genpd->status == GPD_STATE_ACTIVE
|| genpd->status == GPD_STATE_POWER_OFF)
break;
mutex_unlock(&genpd->lock);

Expand All @@ -60,6 +61,12 @@ static void genpd_release_lock(struct generic_pm_domain *genpd)
mutex_unlock(&genpd->lock);
}

static void genpd_set_active(struct generic_pm_domain *genpd)
{
if (genpd->resume_count == 0)
genpd->status = GPD_STATE_ACTIVE;
}

/**
* pm_genpd_poweron - Restore power to a given PM domain and its parents.
* @genpd: PM domain to power up.
Expand All @@ -75,42 +82,24 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)

start:
if (parent) {
mutex_lock(&parent->lock);
genpd_acquire_lock(parent);
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
} else {
mutex_lock(&genpd->lock);
}
/*
* Wait for the domain to transition into either the active,
* or the power off state.
*/
for (;;) {
prepare_to_wait(&genpd->status_wait_queue, &wait,
TASK_UNINTERRUPTIBLE);
if (genpd->status != GPD_STATE_BUSY)
break;
mutex_unlock(&genpd->lock);
if (parent)
mutex_unlock(&parent->lock);

schedule();

if (parent) {
mutex_lock(&parent->lock);
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
} else {
mutex_lock(&genpd->lock);
}
}
finish_wait(&genpd->status_wait_queue, &wait);

if (genpd->status == GPD_STATE_ACTIVE
|| (genpd->prepared_count > 0 && genpd->suspend_power_off))
goto out;

if (genpd->status != GPD_STATE_POWER_OFF) {
genpd_set_active(genpd);
goto out;
}

if (parent && parent->status != GPD_STATE_ACTIVE) {
mutex_unlock(&genpd->lock);
mutex_unlock(&parent->lock);
genpd_release_lock(parent);

ret = pm_genpd_poweron(parent);
if (ret)
Expand All @@ -125,14 +114,14 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
goto out;
}

genpd->status = GPD_STATE_ACTIVE;
genpd_set_active(genpd);
if (parent)
parent->sd_count++;

out:
mutex_unlock(&genpd->lock);
if (parent)
mutex_unlock(&parent->lock);
genpd_release_lock(parent);

return ret;
}
Expand Down Expand Up @@ -209,6 +198,20 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle,
dle->need_restore = false;
}

/**
* genpd_abort_poweroff - Check if a PM domain power off should be aborted.
* @genpd: PM domain to check.
*
* Return true if a PM domain's status changed to GPD_STATE_ACTIVE during
* a "power off" operation, which means that a "power on" has occured in the
* meantime, or if its resume_count field is different from zero, which means
* that one of its devices has been resumed in the meantime.
*/
static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
{
return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
}

/**
* pm_genpd_poweroff - Remove power from a given PM domain.
* @genpd: PM domain to power down.
Expand All @@ -223,9 +226,17 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
struct generic_pm_domain *parent;
struct dev_list_entry *dle;
unsigned int not_suspended;
int ret;
int ret = 0;

if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0)
start:
/*
* 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.
* (3) One of the domain's devices is being resumed right now.
*/
if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0
|| genpd->resume_count > 0)
return 0;

if (genpd->sd_count > 0)
Expand All @@ -239,34 +250,54 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
if (not_suspended > genpd->in_progress)
return -EBUSY;

if (genpd->poweroff_task) {
/*
* Another instance of pm_genpd_poweroff() is executing
* callbacks, so tell it to start over and return.
*/
genpd->status = GPD_STATE_REPEAT;
return 0;
}

if (genpd->gov && genpd->gov->power_down_ok) {
if (!genpd->gov->power_down_ok(&genpd->domain))
return -EAGAIN;
}

genpd->status = GPD_STATE_BUSY;
genpd->poweroff_task = current;

list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
ret = __pm_genpd_save_device(dle, genpd);
if (ret)
goto err_dev;
}

mutex_unlock(&genpd->lock);
if (genpd_abort_poweroff(genpd))
goto out;

if (genpd->status == GPD_STATE_REPEAT) {
genpd->poweroff_task = NULL;
goto start;
}
}

parent = genpd->parent;
if (parent) {
mutex_unlock(&genpd->lock);

genpd_acquire_lock(parent);
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
} else {
mutex_lock(&genpd->lock);

if (genpd_abort_poweroff(genpd)) {
genpd_release_lock(parent);
goto out;
}
}

if (genpd->power_off)
genpd->power_off(genpd);

genpd->status = GPD_STATE_POWER_OFF;
wake_up_all(&genpd->status_wait_queue);

if (parent) {
genpd_sd_counter_dec(parent);
Expand All @@ -276,16 +307,17 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
genpd_release_lock(parent);
}

return 0;
out:
genpd->poweroff_task = NULL;
wake_up_all(&genpd->status_wait_queue);
return ret;

err_dev:
list_for_each_entry_continue(dle, &genpd->dev_list, node)
__pm_genpd_restore_device(dle, genpd);

genpd->status = GPD_STATE_ACTIVE;
wake_up_all(&genpd->status_wait_queue);

return ret;
genpd_set_active(genpd);
goto out;
}

/**
Expand Down Expand Up @@ -327,11 +359,11 @@ static int pm_genpd_runtime_suspend(struct device *dev)
return ret;
}

genpd_acquire_lock(genpd);
mutex_lock(&genpd->lock);
genpd->in_progress++;
pm_genpd_poweroff(genpd);
genpd->in_progress--;
genpd_release_lock(genpd);
mutex_unlock(&genpd->lock);

return 0;
}
Expand Down Expand Up @@ -365,6 +397,7 @@ static void __pm_genpd_runtime_resume(struct device *dev,
static int pm_genpd_runtime_resume(struct device *dev)
{
struct generic_pm_domain *genpd;
DEFINE_WAIT(wait);
int ret;

dev_dbg(dev, "%s()\n", __func__);
Expand All @@ -377,12 +410,31 @@ static int pm_genpd_runtime_resume(struct device *dev)
if (ret)
return ret;

genpd_acquire_lock(genpd);
mutex_lock(&genpd->lock);
genpd->status = GPD_STATE_BUSY;
genpd->resume_count++;
for (;;) {
prepare_to_wait(&genpd->status_wait_queue, &wait,
TASK_UNINTERRUPTIBLE);
/*
* If current is the powering off task, we have been called
* reentrantly from one of the device callbacks, so we should
* not wait.
*/
if (!genpd->poweroff_task || genpd->poweroff_task == current)
break;
mutex_unlock(&genpd->lock);

schedule();

mutex_lock(&genpd->lock);
}
finish_wait(&genpd->status_wait_queue, &wait);
__pm_genpd_runtime_resume(dev, genpd);
genpd->status = GPD_STATE_ACTIVE;
genpd->resume_count--;
genpd_set_active(genpd);
wake_up_all(&genpd->status_wait_queue);
genpd_release_lock(genpd);
mutex_unlock(&genpd->lock);

if (genpd->start_device)
genpd->start_device(dev);
Expand Down Expand Up @@ -1130,6 +1182,8 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->sd_count = 0;
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
init_waitqueue_head(&genpd->status_wait_queue);
genpd->poweroff_task = NULL;
genpd->resume_count = 0;
genpd->device_count = 0;
genpd->suspended_count = 0;
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/pm_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */
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 */
};

Expand All @@ -34,6 +35,8 @@ struct generic_pm_domain {
unsigned int sd_count; /* Number of subdomains with power "on" */
enum gpd_status status; /* Current state of the domain */
wait_queue_head_t status_wait_queue;
struct task_struct *poweroff_task; /* Powering off task */
unsigned int resume_count; /* Number of devices being resumed */
unsigned int device_count; /* Number of devices */
unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */
Expand Down

0 comments on commit 852e260

Please sign in to comment.