summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-08-25 15:37:04 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2011-08-25 15:37:04 +0200
commit0aa2a221696cc8ea20a4cdca01315d3b6b4ecc4d (patch)
treee2af809c44ff7ec93299170fc7ee6871be25108e
parentb5e8d269d814763d597ccc0108d1fa6639ad35a1 (diff)
downloadop-kernel-dev-0aa2a221696cc8ea20a4cdca01315d3b6b4ecc4d.zip
op-kernel-dev-0aa2a221696cc8ea20a4cdca01315d3b6b4ecc4d.tar.gz
PM / Domains: Preliminary support for devices with power.irq_safe set
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>
-rw-r--r--arch/arm/mach-shmobile/pm-sh7372.c1
-rw-r--r--drivers/base/power/domain.c19
-rw-r--r--include/linux/pm_domain.h1
3 files changed, 20 insertions, 1 deletions
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c
index 5d35831..ac47bfc 100644
--- a/arch/arm/mach-shmobile/pm-sh7372.c
+++ b/arch/arm/mach-shmobile/pm-sh7372.c
@@ -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;
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 339eb2d..c2468a7 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -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)
@@ -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);
@@ -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) {
@@ -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);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 5cce46c..2538d90 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -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);
OpenPOWER on IntegriCloud