summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 5d35831e4fb..ac47bfcd287 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 339eb2d9bdd..c2468a7e5b2 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 5cce46c2d92..2538d906bcd 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);