summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorMayuresh Kulkarni <mkulkarni@nvidia.com>2013-02-19 18:56:30 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:07:59 -0700
commit1e5cb7b575e2f81fa1abf6ed3c99148e7ff73bff (patch)
tree047a48c15f044e0e6157fb08c0fa49ab4986b118 /drivers/base
parent9f56208cff996e90140bd9fe6eded4149edec19f (diff)
PM/domains: add delayed power off capability
- this commit adds a capability to delay the powering off of the domain - callers can use pm_genpd_set_poweroff_delay to set the power off delay for a domain - it also adds a pm_notifier per pm domain which cancels the delayed power off work when system suspend is invoked - this needs to be done since pm_wq is freezable and causes resume to fail since no one cancels the delayed power off work. as a result, this work is run after resume when pm_wq is unfreezed causing unwanted issues bug 887332 Change-Id: I1befaa6d898f535e969a564ddae12e22ffc69e2d Signed-off-by: Mayuresh Kulkarni <mkulkarni@nvidia.com> Reviewed-on: http://git-master/r/202037 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-by: Arto Merilainen <amerilainen@nvidia.com>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/domain.c83
1 files changed, 78 insertions, 5 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index d1dce3d1c39d..88e2759dbe25 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -629,6 +629,19 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
return ret;
}
+static int __pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+ int ret = 0;
+
+ mutex_lock(&genpd->lock);
+ genpd->in_progress++;
+ ret = pm_genpd_poweroff(genpd);
+ genpd->in_progress--;
+ mutex_unlock(&genpd->lock);
+
+ return ret;
+}
+
/**
* genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
* @work: Work structure used for scheduling the execution of this function.
@@ -645,6 +658,21 @@ static void genpd_power_off_work_fn(struct work_struct *work)
}
/**
+ * genpd_delayed_power_off_work_fn - Power off PM domain after the delay.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_delayed_power_off_work_fn(struct work_struct *work)
+{
+ struct generic_pm_domain *genpd;
+ struct delayed_work *delay_work = to_delayed_work(work);
+
+ genpd = container_of(delay_work, struct generic_pm_domain,
+ power_off_delayed_work);
+
+ __pm_genpd_poweroff(genpd);
+}
+
+/**
* pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
* @dev: Device to suspend.
*
@@ -681,11 +709,11 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (dev->power.irq_safe)
return 0;
- mutex_lock(&genpd->lock);
- genpd->in_progress++;
- pm_genpd_poweroff(genpd);
- genpd->in_progress--;
- mutex_unlock(&genpd->lock);
+ if (genpd->power_off_delay)
+ queue_delayed_work(pm_wq, &genpd->power_off_delayed_work,
+ msecs_to_jiffies(genpd->power_off_delay));
+ else
+ __pm_genpd_poweroff(genpd);
return 0;
}
@@ -716,6 +744,12 @@ static int pm_genpd_runtime_resume(struct device *dev)
if (dev->power.irq_safe)
return genpd_start_dev_no_timing(genpd, dev);
+ if (genpd->power_off_delay) {
+ if (delayed_work_pending(&genpd->power_off_delayed_work))
+ cancel_delayed_work_sync(
+ &genpd->power_off_delayed_work);
+ }
+
mutex_lock(&genpd->lock);
ret = __pm_genpd_poweron(genpd);
if (ret) {
@@ -774,6 +808,7 @@ static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
}
static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void genpd_delayed_power_off_work_fn(struct work_struct *work) {}
#define pm_genpd_runtime_suspend NULL
#define pm_genpd_runtime_resume NULL
@@ -2145,6 +2180,36 @@ static int pm_genpd_default_thaw(struct device *dev)
return cb ? cb(dev) : pm_generic_thaw(dev);
}
+static int pm_genpd_suspend_notifier(struct notifier_block *notifier,
+ unsigned long pm_event, void *unused)
+{
+ struct generic_pm_domain *genpd = container_of(notifier,
+ struct generic_pm_domain, system_suspend_notifier);
+
+ if (!genpd)
+ return NOTIFY_DONE;
+
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ if (genpd->power_off_delay) {
+ /* if a domain has scheduled a delayed work */
+ if (delayed_work_pending(
+ &genpd->power_off_delayed_work)) {
+
+ /* cancel it now */
+ cancel_delayed_work_sync(
+ &genpd->power_off_delayed_work);
+
+ /* call its power off */
+ __pm_genpd_poweroff(genpd);
+ }
+ }
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
#else /* !CONFIG_PM_SLEEP */
#define pm_genpd_default_suspend NULL
@@ -2176,7 +2241,10 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
mutex_init(&genpd->lock);
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+ INIT_DELAYED_WORK(&genpd->power_off_delayed_work,
+ genpd_delayed_power_off_work_fn);
genpd->in_progress = 0;
+ genpd->power_off_delay = 0;
atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
init_waitqueue_head(&genpd->status_wait_queue);
@@ -2218,6 +2286,11 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->dev_ops.freeze_late = pm_genpd_default_freeze_late;
genpd->dev_ops.thaw_early = pm_genpd_default_thaw_early;
genpd->dev_ops.thaw = pm_genpd_default_thaw;
+#ifdef CONFIG_PM_SLEEP
+ genpd->system_suspend_notifier.notifier_call =
+ pm_genpd_suspend_notifier;
+ register_pm_notifier(&genpd->system_suspend_notifier);
+#endif
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);