From dfe3212e0196c01402154971841463d721dea915 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 17 Aug 2012 22:06:59 +0800 Subject: PM / Sleep: introduce dpm_for_each_dev dpm_list and its pm lock provide a good way to iterate all devices in system. Except this way, there is no other easy way to iterate devices in system. firmware loader need to cache firmware images for devices before system sleep, so introduce the function to meet its demand. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/base/power/main.c') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0113adc310dc..b0b072a88f5f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + +/** + * dpm_for_each_dev - device iterator. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over devices in dpm_list, and call @fn for each device, + * passing it @data. + */ +void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ + struct device *dev; + + if (!fn) + return; + + device_pm_lock(); + list_for_each_entry(dev, &dpm_list, power.entry) + fn(dev, data); + device_pm_unlock(); +} +EXPORT_SYMBOL_GPL(dpm_for_each_dev); -- cgit v1.2.3 From e91c11b1a7f876c6f056d872eb210734150a1795 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:44:28 +0200 Subject: PM: Reorganize device PM initialization Make the device power management initialization more straightforward by moving the initialization of common (i.e. used by both runtime PM and system suspend) fields to a separate routine. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 7 ++----- drivers/base/power/power.h | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers/base/power/main.c') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0113adc310dc..7bd1fe400549 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -57,20 +57,17 @@ static pm_message_t pm_transition; static int async_error; /** - * device_pm_init - Initialize the PM-related part of a device object. + * device_pm_sleep_init - Initialize system suspend-related device fields. * @dev: Device object being initialized. */ -void device_pm_init(struct device *dev) +void device_pm_sleep_init(struct device *dev) { dev->power.is_prepared = false; dev->power.is_suspended = false; init_completion(&dev->power.completion); complete_all(&dev->power.completion); dev->power.wakeup = NULL; - spin_lock_init(&dev->power.lock); - pm_runtime_init(dev); INIT_LIST_HEAD(&dev->power.entry); - dev->power.power_state = PMSG_INVALID; } /** diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index eeb4bff9505c..8a0dcc7f98f9 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -1,5 +1,11 @@ #include +static inline void device_pm_init_common(struct device *dev) +{ + spin_lock_init(&dev->power.lock); + dev->power.power_state = PMSG_INVALID; +} + #ifdef CONFIG_PM_RUNTIME extern void pm_runtime_init(struct device *dev); @@ -25,7 +31,7 @@ static inline struct device *to_device(struct list_head *entry) return container_of(entry, struct device, power.entry); } -extern void device_pm_init(struct device *dev); +extern void device_pm_sleep_init(struct device *dev); extern void device_pm_add(struct device *); extern void device_pm_remove(struct device *); extern void device_pm_move_before(struct device *, struct device *); @@ -34,12 +40,7 @@ extern void device_pm_move_last(struct device *); #else /* !CONFIG_PM_SLEEP */ -static inline void device_pm_init(struct device *dev) -{ - spin_lock_init(&dev->power.lock); - dev->power.power_state = PMSG_INVALID; - pm_runtime_init(dev); -} +static inline void device_pm_sleep_init(struct device *dev) {} static inline void device_pm_add(struct device *dev) { @@ -60,6 +61,13 @@ static inline void device_pm_move_last(struct device *dev) {} #endif /* !CONFIG_PM_SLEEP */ +static inline void device_pm_init(struct device *dev) +{ + device_pm_init_common(dev); + device_pm_sleep_init(dev); + pm_runtime_init(dev); +} + #ifdef CONFIG_PM /* -- cgit v1.2.3 From dbf374142dd7a3c394ec124ebe7339a6c412d9b6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:46:39 +0200 Subject: PM / Domains: Move syscore flag from subsys data to struct device The syscore device PM flag is used to mark the devices (belonging to a PM domain) that should never be turned off, except for the system core (syscore) suspend/hibernation and resume stages. That flag is stored in the device's struct pm_subsys_data object whose address is available from struct device. However, in some situations it may be convenient to set that flag before the device is added to a PM domain, so it is better to move it directly to the "power" member of struct device. Then, it can be checked by the routines in drivers/base/power/runtime.c and drivers/base/power/main.c, which is more straightforward. This also reduces the number of dev_gpd_data() invocations in the generic PM domains framework, so the overhead related to the syscore flag is slightly smaller. Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/base/power/common.c | 15 +++++++++++++++ drivers/base/power/domain.c | 37 ++++++------------------------------- drivers/base/power/main.c | 28 ++++++++++++++++++++++++++++ drivers/base/power/runtime.c | 2 +- drivers/clocksource/sh_cmt.c | 2 +- drivers/clocksource/sh_mtu2.c | 2 +- drivers/clocksource/sh_tmu.c | 2 +- include/linux/pm.h | 5 +++++ include/linux/pm_domain.h | 3 --- 9 files changed, 58 insertions(+), 38 deletions(-) (limited to 'drivers/base/power/main.c') diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 39c32529b833..cf7a85134730 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -83,3 +83,18 @@ int dev_pm_put_subsys_data(struct device *dev) return ret; } EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data); + +/** + * dev_pm_syscore_device - Set/unset the given device's power.syscore flag. + * @dev: Device whose flag is to be modified. + * @val: New value of the flag. + */ +void dev_pm_syscore_device(struct device *dev, bool val) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->power.lock, flags); + dev->power.syscore = val; + spin_unlock_irqrestore(&dev->power.lock, flags); +} +EXPORT_SYMBOL_GPL(dev_pm_syscore_device); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 15234ecd7edb..52172754ff78 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -436,7 +436,7 @@ 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) - || pdd->dev->power.irq_safe || to_gpd_data(pdd)->syscore)) + || pdd->dev->power.irq_safe || pdd->dev->power.syscore)) not_suspended++; if (not_suspended > genpd->in_progress) @@ -578,9 +578,6 @@ static int pm_genpd_runtime_suspend(struct device *dev) might_sleep_if(!genpd->dev_irq_safe); - if (dev_gpd_data(dev)->syscore) - return -EBUSY; - stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; if (stop_ok && !stop_ok(dev)) return -EBUSY; @@ -983,7 +980,7 @@ static int pm_genpd_suspend_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - if (genpd->suspend_power_off || dev_gpd_data(dev)->syscore + if (genpd->suspend_power_off || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; @@ -1016,7 +1013,7 @@ static int pm_genpd_resume_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - if (genpd->suspend_power_off || dev_gpd_data(dev)->syscore + if (genpd->suspend_power_off || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; @@ -1136,8 +1133,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off || dev_gpd_data(dev)->syscore ? - 0 : genpd_stop_dev(genpd, dev); + return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); } /** @@ -1157,8 +1153,7 @@ static int pm_genpd_thaw_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off || dev_gpd_data(dev)->syscore ? - 0 : genpd_start_dev(genpd, dev); + return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev); } /** @@ -1253,7 +1248,7 @@ static int pm_genpd_restore_noirq(struct device *dev) pm_genpd_sync_poweron(genpd); - return dev_gpd_data(dev)->syscore ? 0 : genpd_start_dev(genpd, dev); + return genpd_start_dev(genpd, dev); } /** @@ -1525,26 +1520,6 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, return ret; } -/** - * pm_genpd_dev_syscore - Set/unset the "syscore" flag for a given device. - * @dev: Device to set/unset the flag for. - * @val: The new value of the device's "syscore" flag. - */ -void pm_genpd_dev_syscore(struct device *dev, bool val) -{ - struct pm_subsys_data *psd; - unsigned long flags; - - spin_lock_irqsave(&dev->power.lock, flags); - - psd = dev_to_psd(dev); - if (psd && psd->domain_data) - to_gpd_data(psd->domain_data)->syscore = val; - - spin_unlock_irqrestore(&dev->power.lock, flags); -} -EXPORT_SYMBOL_GPL(pm_genpd_dev_syscore); - /** * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. * @dev: Device to set/unset the flag for. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7bd1fe400549..57f5814c2732 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -405,6 +405,9 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) TRACE_DEVICE(dev); TRACE_RESUME(0); + if (dev->power.syscore) + goto Out; + if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -426,6 +429,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) error = dpm_run_callback(callback, dev, state, info); + Out: TRACE_RESUME(error); return error; } @@ -483,6 +487,9 @@ static int device_resume_early(struct device *dev, pm_message_t state) TRACE_DEVICE(dev); TRACE_RESUME(0); + if (dev->power.syscore) + goto Out; + if (dev->pm_domain) { info = "early power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); @@ -504,6 +511,7 @@ static int device_resume_early(struct device *dev, pm_message_t state) error = dpm_run_callback(callback, dev, state, info); + Out: TRACE_RESUME(error); return error; } @@ -567,6 +575,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) TRACE_DEVICE(dev); TRACE_RESUME(0); + if (dev->power.syscore) + goto Complete; + dpm_wait(dev->parent, async); device_lock(dev); @@ -629,6 +640,8 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) Unlock: device_unlock(dev); + + Complete: complete_all(&dev->power.completion); TRACE_RESUME(error); @@ -719,6 +732,9 @@ static void device_complete(struct device *dev, pm_message_t state) void (*callback)(struct device *) = NULL; char *info = NULL; + if (dev->power.syscore) + return; + device_lock(dev); if (dev->pm_domain) { @@ -831,6 +847,9 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) pm_callback_t callback = NULL; char *info = NULL; + if (dev->power.syscore) + return 0; + if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -914,6 +933,9 @@ static int device_suspend_late(struct device *dev, pm_message_t state) pm_callback_t callback = NULL; char *info = NULL; + if (dev->power.syscore) + return 0; + if (dev->pm_domain) { info = "late power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); @@ -1050,6 +1072,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) goto Complete; } + if (dev->power.syscore) + goto Complete; + device_lock(dev); if (dev->pm_domain) { @@ -1206,6 +1231,9 @@ static int device_prepare(struct device *dev, pm_message_t state) char *info = NULL; int error = 0; + if (dev->power.syscore) + return 0; + device_lock(dev); dev->power.wakeup_path = device_may_wakeup(dev); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 7d9c1cb1c39a..bd1de3980919 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -134,7 +134,7 @@ static int rpm_check_suspend_allowed(struct device *dev) if (dev->power.runtime_error) retval = -EINVAL; - else if (dev->power.disable_depth > 0) + else if (dev->power.disable_depth > 0 || dev->power.syscore) retval = -EACCES; else if (atomic_read(&dev->power.usage_count) > 0) retval = -EAGAIN; diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index c6fbb9f71911..a515605bf8f5 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -717,7 +717,7 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev) struct sh_timer_config *cfg = pdev->dev.platform_data; if (cfg->clocksource_rating || cfg->clockevent_rating) - pm_genpd_dev_syscore(&pdev->dev, true); + dev_pm_syscore_device(&pdev->dev, true); } if (p) { diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 278c18abb2ae..1a95cad96819 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -323,7 +323,7 @@ static int __devinit sh_mtu2_probe(struct platform_device *pdev) struct sh_timer_config *cfg = pdev->dev.platform_data; if (cfg->clockevent_rating) - pm_genpd_dev_syscore(&pdev->dev, true); + dev_pm_syscore_device(&pdev->dev, true); } if (p) { diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 5319689c579c..81b0239718ee 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -453,7 +453,7 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev) struct sh_timer_config *cfg = pdev->dev.platform_data; if (cfg->clocksource_rating || cfg->clockevent_rating) - pm_genpd_dev_syscore(&pdev->dev, true); + dev_pm_syscore_device(&pdev->dev, true); } if (p) { diff --git a/include/linux/pm.h b/include/linux/pm.h index 716517af1543..b79a0dd3bc6d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -43,8 +43,12 @@ struct device; #ifdef CONFIG_PM extern const char power_group_name[]; /* = "power" */ + +extern void dev_pm_syscore_device(struct device *dev, bool val); #else #define power_group_name NULL + +static inline void dev_pm_syscore_device(struct device *dev, bool val) {} #endif typedef struct pm_message { @@ -511,6 +515,7 @@ struct dev_pm_info { bool is_suspended:1; /* Ditto */ bool ignore_children:1; bool early_init:1; /* Owned by the PM core */ + bool syscore:1; spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index dab0938603fa..08adf8e5a80e 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -114,7 +114,6 @@ struct generic_pm_domain_data { struct mutex lock; unsigned int refcount; bool need_restore; - bool syscore; }; #ifdef CONFIG_PM_GENERIC_DOMAINS @@ -153,7 +152,6 @@ static inline int pm_genpd_of_add_device(struct device_node *genpd_node, extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); -extern void pm_genpd_dev_syscore(struct device *dev, bool val); extern void pm_genpd_dev_need_restore(struct device *dev, bool val); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_subdomain); @@ -199,7 +197,6 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, { return -ENOSYS; } -static inline void pm_genpd_dev_syscore(struct device *dev, bool val) {} static inline void pm_genpd_dev_need_restore(struct device *dev, bool val) {} static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_sd) -- cgit v1.2.3 From 997a031107ec962967ce36db9bc500f1fad491c1 Mon Sep 17 00:00:00 2001 From: Feng Hong Date: Wed, 19 Sep 2012 14:16:00 +0200 Subject: PM / Sleep: use resume event when call dpm_resume_early When dpm_suspend_noirq fail, state is PMSG_SUSPEND, should change to PMSG_RESUME when dpm_resume_early is called Signed-off-by: Feng Hong Signed-off-by: Raul Xiong Signed-off-by: Neil Zhang Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base/power/main.c') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0113adc310dc..2700f2e4066f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -996,7 +996,7 @@ int dpm_suspend_end(pm_message_t state) error = dpm_suspend_noirq(state); if (error) { - dpm_resume_early(state); + dpm_resume_early(resume_event(state)); return error; } -- cgit v1.2.3 From 88d26136a256576e444db312179e17af6dd0ea87 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 19 Sep 2012 21:59:02 +0200 Subject: PM: Prevent runtime suspend during system resume This patch (as1591) moves the pm_runtime_get_noresume() and pm_runtime_put_sync() calls from __device_suspend() and device_resume() to device_prepare() and device_complete() in the PM core. The reason for doing this is to make sure that parent devices remain at full power (i.e., don't go into runtime suspend) while their children are being resumed from a system sleep. The PCI core already contained equivalent code to serve the same purpose. The patch removes the duplicated code, since it is no longer needed. One of the comments from the PCI core gets moved into the PM core, and a second comment is added to explain whe the _get_noresume and _put_sync calls are present. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 29 ++++++++++++++++++----------- drivers/pci/pci-driver.c | 17 ----------------- 2 files changed, 18 insertions(+), 28 deletions(-) (limited to 'drivers/base/power/main.c') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 2700f2e4066f..077b9756fd8f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -565,7 +565,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; char *info = NULL; int error = 0; - bool put = false; TRACE_DEVICE(dev); TRACE_RESUME(0); @@ -583,7 +582,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) goto Unlock; pm_runtime_enable(dev); - put = true; if (dev->pm_domain) { info = "power domain "; @@ -636,9 +634,6 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) TRACE_RESUME(error); - if (put) - pm_runtime_put_sync(dev); - return error; } @@ -749,6 +744,8 @@ static void device_complete(struct device *dev, pm_message_t state) } device_unlock(dev); + + pm_runtime_put_sync(dev); } /** @@ -1043,12 +1040,16 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (async_error) goto Complete; - pm_runtime_get_noresume(dev); + /* + * If a device configured to wake up the system from sleep states + * has been suspended at run time and there's a resume request pending + * for it, this is equivalent to the device signaling wakeup, so the + * system suspend operation should be aborted. + */ if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) pm_wakeup_event(dev, 0); if (pm_wakeup_pending()) { - pm_runtime_put_sync(dev); async_error = -EBUSY; goto Complete; } @@ -1111,12 +1112,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) Complete: complete_all(&dev->power.completion); - if (error) { - pm_runtime_put_sync(dev); + if (error) async_error = error; - } else if (dev->power.is_suspended) { + else if (dev->power.is_suspended) __pm_runtime_disable(dev, false); - } return error; } @@ -1209,6 +1208,14 @@ static int device_prepare(struct device *dev, pm_message_t state) char *info = NULL; int error = 0; + /* + * If a device's parent goes into runtime suspend at the wrong time, + * it won't be possible to resume the device. To prevent this we + * block runtime suspend here, during the prepare phase, and allow + * it again during the complete phase. + */ + pm_runtime_get_noresume(dev); + device_lock(dev); dev->power.wakeup_path = device_may_wakeup(dev); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 185be3703343..51cd90bcdd4c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -623,21 +623,6 @@ static int pci_pm_prepare(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - /* - * If a PCI device configured to wake up the system from sleep states - * has been suspended at run time and there's a resume request pending - * for it, this is equivalent to the device signaling wakeup, so the - * system suspend operation should be aborted. - */ - pm_runtime_get_noresume(dev); - if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) - pm_wakeup_event(dev, 0); - - if (pm_wakeup_pending()) { - pm_runtime_put_sync(dev); - return -EBUSY; - } - /* * PCI devices suspended at run time need to be resumed at this * point, because in general it is necessary to reconfigure them for @@ -661,8 +646,6 @@ static void pci_pm_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); - - pm_runtime_put_sync(dev); } #else /* !CONFIG_PM_SLEEP */ -- cgit v1.2.3