diff options
author | Mayuresh Kulkarni <mkulkarni@nvidia.com> | 2013-02-19 18:56:30 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 13:07:59 -0700 |
commit | 1e5cb7b575e2f81fa1abf6ed3c99148e7ff73bff (patch) | |
tree | 047a48c15f044e0e6157fb08c0fa49ab4986b118 /drivers/base | |
parent | 9f56208cff996e90140bd9fe6eded4149edec19f (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.c | 83 |
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); |