Skip to content

Commit

Permalink
PM / Domains: Preliminary support for devices with power.irq_safe set
Browse files Browse the repository at this point in the history
The generic PM domains framework currently doesn't work with devices
whose power.irq_safe flag is set, because runtime PM callbacks for
such devices are run with interrupts disabled and the callbacks
provided by the generic PM domains framework use domain mutexes
and may sleep.  However, such devices very well may belong to
power domains on some systems, so the generic PM domains framework
should take them into account.

For this reason, modify the generic PM domains framework so that the
domain .power_off() and .power_on() callbacks are never executed for
a domain containing devices with power.irq_safe set, although the
.stop_device() and .start_device() callbacks are still run for them.

Additionally, introduce a flag allowing the creator of a
struct generic_pm_domain object to indicate that its .stop_device()
and .start_device() callbacks may be run in interrupt context
(might_sleep_if() triggers if that flag is not set and one of those
callbacks is run in interrupt context).

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
  • Loading branch information
Rafael J. Wysocki committed Aug 25, 2011
1 parent b5e8d26 commit 0aa2a22
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/arm/mach-shmobile/pm-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
pm_genpd_init(genpd, NULL, false);
genpd->stop_device = pm_clk_suspend;
genpd->start_device = pm_clk_resume;
genpd->dev_irq_safe = true;
genpd->active_wakeup = pd_active_wakeup;
genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up;
Expand Down
19 changes: 18 additions & 1 deletion drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)

not_suspended = 0;
list_for_each_entry(pdd, &genpd->dev_list, list_node)
if (pdd->dev->driver && !pm_runtime_suspended(pdd->dev))
if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
|| pdd->dev->power.irq_safe))
not_suspended++;

if (not_suspended > genpd->in_progress)
Expand Down Expand Up @@ -417,12 +418,21 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

might_sleep_if(!genpd->dev_irq_safe);

if (genpd->stop_device) {
int ret = genpd->stop_device(dev);
if (ret)
return ret;
}

/*
* If power.irq_safe is set, this routine will be run with interrupts
* off, so it can't use mutexes.
*/
if (dev->power.irq_safe)
return 0;

mutex_lock(&genpd->lock);
genpd->in_progress++;
pm_genpd_poweroff(genpd);
Expand Down Expand Up @@ -452,6 +462,12 @@ static int pm_genpd_runtime_resume(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;

might_sleep_if(!genpd->dev_irq_safe);

/* If power.irq_safe, the PM domain is never powered off. */
if (dev->power.irq_safe)
goto out;

mutex_lock(&genpd->lock);
ret = __pm_genpd_poweron(genpd);
if (ret) {
Expand Down Expand Up @@ -483,6 +499,7 @@ static int pm_genpd_runtime_resume(struct device *dev)
wake_up_all(&genpd->status_wait_queue);
mutex_unlock(&genpd->lock);

out:
if (genpd->start_device)
genpd->start_device(dev);

Expand Down
1 change: 1 addition & 0 deletions include/linux/pm_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct generic_pm_domain {
unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */
bool suspend_power_off; /* Power status before system suspend */
bool dev_irq_safe; /* Device callbacks are IRQ-safe */
int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain);
int (*start_device)(struct device *dev);
Expand Down

0 comments on commit 0aa2a22

Please sign in to comment.