From 802d8b49a7705298b62ac35a59b867f1288caaf3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:39:16 +0200 Subject: PM / Domains: Introduce simplified power on routine for system resume Introduce function pm_genpd_sync_poweron() for restoring domain power during resume from system suspend and hibernation. It can be much simpler than pm_genpd_poweron(), because it doesn't have to care about locking and it can skip many checks done by the latter. Modify pm_genpd_resume_noirq() and pm_genpd_restore_noirq() to use the new function. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ba3487c9835b..55c39f5b7a59 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -776,6 +776,32 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) } } +/** + * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. + * @genpd: PM domain to power on. + * + * This function is only called in "noirq" stage of system power transitions, so + * it need not acquire locks (all of the "noirq" callbacks are executed + * sequentially, so it is guaranteed that it will never run twice in parallel). + */ +static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) +{ + struct gpd_link *link; + + if (genpd->status != GPD_STATE_POWER_OFF) + return; + + list_for_each_entry(link, &genpd->slave_links, slave_node) { + pm_genpd_sync_poweron(link->master); + genpd_sd_counter_inc(link->master); + } + + if (genpd->power_on) + genpd->power_on(genpd); + + genpd->status = GPD_STATE_ACTIVE; +} + /** * resume_needed - Check whether to resume a device before system suspend. * @dev: Device to check. @@ -979,7 +1005,7 @@ static int pm_genpd_resume_noirq(struct device *dev) * guaranteed that this function will never run twice in parallel for * the same PM domain, so it is not necessary to use locking here. */ - pm_genpd_poweron(genpd); + pm_genpd_sync_poweron(genpd); genpd->suspended_count--; return genpd_start_dev(genpd, dev); @@ -1186,8 +1212,8 @@ static int pm_genpd_restore_noirq(struct device *dev) if (genpd->suspended_count++ == 0) { /* * The boot kernel might put the domain into arbitrary state, - * so make it appear as powered off to pm_genpd_poweron(), so - * that it tries to power it on in case it was really off. + * so make it appear as powered off to pm_genpd_sync_poweron(), + * so that it tries to power it on in case it was really off. */ genpd->status = GPD_STATE_POWER_OFF; if (genpd->suspend_power_off) { @@ -1205,7 +1231,7 @@ static int pm_genpd_restore_noirq(struct device *dev) if (genpd->suspend_power_off) return 0; - pm_genpd_poweron(genpd); + pm_genpd_sync_poweron(genpd); return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); } -- cgit v1.2.3 From 77f827de07432a74821cf0f831d699544b2d474f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:39:57 +0200 Subject: PM / Domains: Add power off/on function for system core suspend stage Introduce function pm_genpd_syscore_switch() and two wrappers around it, pm_genpd_syscore_poweroff() and pm_genpd_syscore_poweron(), allowing the callers to let the generic PM domains framework know that the given device is not necessary any more and its PM domain can be turned off (the former) or that the given device will be required immediately, so its PM domain has to be turned on (the latter) during the system core (syscore) stage of system suspend (or hibernation) and resume. These functions will be used for handling devices registered as clock sources and clock event devices that belong to PM domains. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 57 ++++++++++++++++++++++++++++++++++++++++----- include/linux/pm_domain.h | 16 +++++++++++++ kernel/power/Kconfig | 4 ++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 55c39f5b7a59..515c8ecf01ce 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -697,6 +697,24 @@ static inline void genpd_power_off_work_fn(struct work_struct *work) {} #ifdef CONFIG_PM_SLEEP +/** + * pm_genpd_present - Check if the given PM domain has been initialized. + * @genpd: PM domain to check. + */ +static bool pm_genpd_present(struct generic_pm_domain *genpd) +{ + struct generic_pm_domain *gpd; + + if (IS_ERR_OR_NULL(genpd)) + return false; + + list_for_each_entry(gpd, &gpd_list, gpd_list_node) + if (gpd == genpd) + return true; + + return false; +} + static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, struct device *dev) { @@ -750,9 +768,10 @@ static int genpd_thaw_dev(struct generic_pm_domain *genpd, struct device *dev) * Check if the given PM domain can be powered off (during system suspend or * hibernation) and do that if so. Also, in that case propagate to its masters. * - * This function is only called in "noirq" stages of system power transitions, - * so it need not acquire locks (all of the "noirq" callbacks are executed - * sequentially, so it is guaranteed that it will never run twice in parallel). + * This function is only called in "noirq" and "syscore" stages of system power + * transitions, so it need not acquire locks (all of the "noirq" callbacks are + * executed sequentially, so it is guaranteed that it will never run twice in + * parallel). */ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) { @@ -780,9 +799,10 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. * @genpd: PM domain to power on. * - * This function is only called in "noirq" stage of system power transitions, so - * it need not acquire locks (all of the "noirq" callbacks are executed - * sequentially, so it is guaranteed that it will never run twice in parallel). + * This function is only called in "noirq" and "syscore" stages of system power + * transitions, so it need not acquire locks (all of the "noirq" callbacks are + * executed sequentially, so it is guaranteed that it will never run twice in + * parallel). */ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) { @@ -1272,6 +1292,31 @@ static void pm_genpd_complete(struct device *dev) } } +/** + * pm_genpd_syscore_switch - Switch power during system core suspend or resume. + * @dev: Device that normally is marked as "always on" to switch power for. + * + * This routine may only be called during the system core (syscore) suspend or + * resume phase for devices whose "always on" flags are set. + */ +void pm_genpd_syscore_switch(struct device *dev, bool suspend) +{ + struct generic_pm_domain *genpd; + + genpd = dev_to_genpd(dev); + if (!pm_genpd_present(genpd)) + return; + + if (suspend) { + genpd->suspended_count++; + pm_genpd_sync_poweroff(genpd); + } else { + pm_genpd_sync_poweron(genpd); + genpd->suspended_count--; + } +} +EXPORT_SYMBOL_GPL(pm_genpd_syscore_switch); + #else #define pm_genpd_prepare NULL diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index a7d6172922d4..ab83cf3dfaac 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -258,4 +258,20 @@ static inline void genpd_queue_power_off_work(struct generic_pm_domain *gpd) {} static inline void pm_genpd_poweroff_unused(void) {} #endif +#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP +extern void pm_genpd_syscore_switch(struct device *dev, bool suspend); +#else +static inline void pm_genpd_syscore_switch(struct device *dev, bool suspend) {} +#endif + +static inline void pm_genpd_syscore_poweroff(struct device *dev) +{ + pm_genpd_syscore_switch(dev, true); +} + +static inline void pm_genpd_syscore_poweron(struct device *dev) +{ + pm_genpd_syscore_switch(dev, false); +} + #endif /* _LINUX_PM_DOMAIN_H */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index a70518c9d82f..5dfdc9ea180b 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -263,6 +263,10 @@ config PM_GENERIC_DOMAINS bool depends on PM +config PM_GENERIC_DOMAINS_SLEEP + def_bool y + depends on PM_SLEEP && PM_GENERIC_DOMAINS + config PM_GENERIC_DOMAINS_RUNTIME def_bool y depends on PM_RUNTIME && PM_GENERIC_DOMAINS -- cgit v1.2.3 From adc78e6b9946a4b22e22403d961f3b03c469e5d3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:40:41 +0200 Subject: timekeeping: Add suspend and resume of clock event devices Some clock event devices, for example such that belong to PM domains, need to be handled in a spcial way during the timekeeping suspend and resume (which takes place in the system core, or "syscore", stages of system power transitions) in analogy with clock sources. Introduce .suspend() and .resume() callbacks for clock event devices that will be executed by timekeeping_suspend/_resume(), respectively, next the the clock sources' .suspend() and .resume() callbacks. Signed-off-by: Rafael J. Wysocki --- include/linux/clockchips.h | 8 ++++++++ kernel/time/clockevents.c | 24 ++++++++++++++++++++++++ kernel/time/timekeeping.c | 2 ++ 3 files changed, 34 insertions(+) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index acba894374a1..8a7096fcb01e 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -97,6 +97,8 @@ struct clock_event_device { void (*broadcast)(const struct cpumask *mask); void (*set_mode)(enum clock_event_mode mode, struct clock_event_device *); + void (*suspend)(struct clock_event_device *); + void (*resume)(struct clock_event_device *); unsigned long min_delta_ticks; unsigned long max_delta_ticks; @@ -156,6 +158,9 @@ clockevents_calc_mult_shift(struct clock_event_device *ce, u32 freq, u32 minsec) freq, minsec); } +extern void clockevents_suspend(void); +extern void clockevents_resume(void); + #ifdef CONFIG_GENERIC_CLOCKEVENTS extern void clockevents_notify(unsigned long reason, void *arg); #else @@ -164,6 +169,9 @@ extern void clockevents_notify(unsigned long reason, void *arg); #else /* CONFIG_GENERIC_CLOCKEVENTS_BUILD */ +static inline void clockevents_suspend(void) {} +static inline void clockevents_resume(void) {} + #define clockevents_notify(reason, arg) do { } while (0) #endif diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 7e1ce012a851..30b6de0d977c 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -397,6 +397,30 @@ void clockevents_exchange_device(struct clock_event_device *old, local_irq_restore(flags); } +/** + * clockevents_suspend - suspend clock devices + */ +void clockevents_suspend(void) +{ + struct clock_event_device *dev; + + list_for_each_entry_reverse(dev, &clockevent_devices, list) + if (dev->suspend) + dev->suspend(dev); +} + +/** + * clockevents_resume - resume clock devices + */ +void clockevents_resume(void) +{ + struct clock_event_device *dev; + + list_for_each_entry(dev, &clockevent_devices, list) + if (dev->resume) + dev->resume(dev); +} + #ifdef CONFIG_GENERIC_CLOCKEVENTS /** * clockevents_notify - notification about relevant events diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 34e5eac81424..312a675cb240 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -773,6 +773,7 @@ static void timekeeping_resume(void) read_persistent_clock(&ts); + clockevents_resume(); clocksource_resume(); write_seqlock_irqsave(&tk->lock, flags); @@ -832,6 +833,7 @@ static int timekeeping_suspend(void) clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); clocksource_suspend(); + clockevents_suspend(); return 0; } -- cgit v1.2.3 From eaa49a8cd1f98a6486413d902e7304df026a1fa9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:41:20 +0200 Subject: sh: TMU: Introduce clocksource/clock events suspend/resume routines Introduce suspend/resume routines for SH TMU clock source and clock event device such that if those devices belong to a PM domain, the generic PM domains framework will be notified that the given domain may be turned off (during system suspend) or that it has to be turned on (during system resume). This change allows the A4R domain on SH7372 to be turned off during system suspend (tested on the Mackerel board) if the TMU clock source and/or clock event device is in use. Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_tmu.c | 54 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index c1b51d49d106..7d700829bb41 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -33,6 +33,7 @@ #include #include #include +#include struct sh_tmu_priv { void __iomem *mapbase; @@ -43,6 +44,7 @@ struct sh_tmu_priv { unsigned long periodic; struct clock_event_device ced; struct clocksource cs; + bool cs_enabled; }; static DEFINE_RAW_SPINLOCK(sh_tmu_lock); @@ -204,14 +206,40 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs) int ret; ret = sh_tmu_enable(p); - if (!ret) + if (!ret) { __clocksource_updatefreq_hz(cs, p->rate); + p->cs_enabled = true; + } return ret; } static void sh_tmu_clocksource_disable(struct clocksource *cs) { - sh_tmu_disable(cs_to_sh_tmu(cs)); + struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + + WARN_ON(!p->cs_enabled); + + sh_tmu_disable(p); + p->cs_enabled = false; +} + +static void sh_tmu_clocksource_suspend(struct clocksource *cs) +{ + struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + + if (p->cs_enabled) + sh_tmu_disable(p); + + pm_genpd_syscore_poweroff(&p->pdev->dev); +} + +static void sh_tmu_clocksource_resume(struct clocksource *cs) +{ + struct sh_tmu_priv *p = cs_to_sh_tmu(cs); + + pm_genpd_syscore_poweron(&p->pdev->dev); + if (p->cs_enabled) + sh_tmu_enable(p); } static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, @@ -224,6 +252,8 @@ static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, cs->read = sh_tmu_clocksource_read; cs->enable = sh_tmu_clocksource_enable; cs->disable = sh_tmu_clocksource_disable; + cs->suspend = sh_tmu_clocksource_suspend; + cs->resume = sh_tmu_clocksource_resume; cs->mask = CLOCKSOURCE_MASK(32); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; @@ -301,6 +331,16 @@ static int sh_tmu_clock_event_next(unsigned long delta, return 0; } +static void sh_tmu_clock_event_suspend(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweroff(&ced_to_sh_tmu(ced)->pdev->dev); +} + +static void sh_tmu_clock_event_resume(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweron(&ced_to_sh_tmu(ced)->pdev->dev); +} + static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, char *name, unsigned long rating) { @@ -316,6 +356,8 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, ced->cpumask = cpumask_of(0); ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; + ced->suspend = sh_tmu_clock_event_suspend; + ced->resume = sh_tmu_clock_event_resume; dev_info(&p->pdev->dev, "used for clock events\n"); @@ -407,8 +449,12 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev) struct sh_tmu_priv *p = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) - pm_genpd_dev_always_on(&pdev->dev, true); + if (!is_early_platform_device(pdev)) { + struct sh_timer_config *cfg = pdev->dev.platform_data; + + if (cfg->clocksource_rating || cfg->clockevent_rating) + pm_genpd_dev_always_on(&pdev->dev, true); + } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); -- cgit v1.2.3 From 9bb5ec8872312751f992cdac9968805fe03e81aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:43:03 +0200 Subject: sh: CMT: Introduce clocksource/clock events suspend/resume routines Introduce suspend/resume routines for SH CMT clock event devices and modify the suspend/resume routines for SH CMT clock sources such that if those devices belong to a PM domain, the generic PM domains framework will be notified that the given domain may be turned off (during system suspend) or that it has to be turned on (during system resume). Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_cmt.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 98b06baafcc6..c06d27fc59e8 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -464,9 +464,20 @@ static void sh_cmt_clocksource_disable(struct clocksource *cs) sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); } +static void sh_cmt_clocksource_suspend(struct clocksource *cs) +{ + struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + + sh_cmt_stop(p, FLAG_CLOCKSOURCE); + pm_genpd_syscore_poweroff(&p->pdev->dev); +} + static void sh_cmt_clocksource_resume(struct clocksource *cs) { - sh_cmt_start(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); + struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + + pm_genpd_syscore_poweron(&p->pdev->dev); + sh_cmt_start(p, FLAG_CLOCKSOURCE); } static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, @@ -479,7 +490,7 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, cs->read = sh_cmt_clocksource_read; cs->enable = sh_cmt_clocksource_enable; cs->disable = sh_cmt_clocksource_disable; - cs->suspend = sh_cmt_clocksource_disable; + cs->suspend = sh_cmt_clocksource_suspend; cs->resume = sh_cmt_clocksource_resume; cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; @@ -562,6 +573,16 @@ static int sh_cmt_clock_event_next(unsigned long delta, return 0; } +static void sh_cmt_clock_event_suspend(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweroff(&ced_to_sh_cmt(ced)->pdev->dev); +} + +static void sh_cmt_clock_event_resume(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweron(&ced_to_sh_cmt(ced)->pdev->dev); +} + static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, char *name, unsigned long rating) { @@ -576,6 +597,8 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, ced->cpumask = cpumask_of(0); ced->set_next_event = sh_cmt_clock_event_next; ced->set_mode = sh_cmt_clock_event_mode; + ced->suspend = sh_cmt_clock_event_suspend; + ced->resume = sh_cmt_clock_event_resume; dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); @@ -690,8 +713,12 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev) struct sh_cmt_priv *p = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) - pm_genpd_dev_always_on(&pdev->dev, true); + if (!is_early_platform_device(pdev)) { + struct sh_timer_config *cfg = pdev->dev.platform_data; + + if (cfg->clocksource_rating || cfg->clockevent_rating) + pm_genpd_dev_always_on(&pdev->dev, true); + } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); -- cgit v1.2.3 From cc7ad4564b1097c916834dc919678867631e7676 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:43:41 +0200 Subject: sh: MTU2: Introduce clock events suspend/resume routines Introduce suspend/resume routines for SH MTU2 clock event devices such that if those devices belong to a PM domain, the generic PM domains framework will be notified that the given domain may be turned off (during system suspend) or that it has to be turned on (during system resume). Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_mtu2.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index d9b76ca64a61..a55bb905812e 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -208,6 +208,16 @@ static void sh_mtu2_clock_event_mode(enum clock_event_mode mode, } } +static void sh_mtu2_clock_event_suspend(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweroff(&ced_to_sh_mtu2(ced)->pdev->dev); +} + +static void sh_mtu2_clock_event_resume(struct clock_event_device *ced) +{ + pm_genpd_syscore_poweron(&ced_to_sh_mtu2(ced)->pdev->dev); +} + static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, char *name, unsigned long rating) { @@ -221,6 +231,8 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, ced->rating = rating; ced->cpumask = cpumask_of(0); ced->set_mode = sh_mtu2_clock_event_mode; + ced->suspend = sh_mtu2_clock_event_suspend; + ced->resume = sh_mtu2_clock_event_resume; dev_info(&p->pdev->dev, "used for clock events\n"); clockevents_register_device(ced); @@ -307,8 +319,12 @@ static int __devinit sh_mtu2_probe(struct platform_device *pdev) struct sh_mtu2_priv *p = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) - pm_genpd_dev_always_on(&pdev->dev, true); + if (!is_early_platform_device(pdev)) { + struct sh_timer_config *cfg = pdev->dev.platform_data; + + if (cfg->clockevent_rating) + pm_genpd_dev_always_on(&pdev->dev, true); + } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); -- 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(-) 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 bed2b42d9f0b411f384c5619870ab0fea5dd116b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:45:11 +0200 Subject: PM / Runtime: Allow helpers to be called by early platform drivers Runtime PM helper functions, like pm_runtime_get_sync(), cannot be called by early platform device drivers, because the devices' power management locks are not initialized at that time. This is quite inconvenient, so modify early_platform_add_devices() to initialize the devices power management locks as appropriate and make sure that they won't be initialized more than once if an early platform device is going to be used as a regular one later. Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 2 ++ drivers/base/power/power.h | 18 ++++++++++++++++-- include/linux/pm.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a1a722502587..d51514b79efe 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -22,6 +22,7 @@ #include #include "base.h" +#include "power/power.h" #define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ driver)) @@ -948,6 +949,7 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) dev = &devs[i]->dev; if (!dev->devres_head.next) { + pm_runtime_early_init(dev); INIT_LIST_HEAD(&dev->devres_head); list_add_tail(&dev->devres_head, &early_platform_device_list); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 8a0dcc7f98f9..0dbfdf4419af 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -2,17 +2,31 @@ static inline void device_pm_init_common(struct device *dev) { - spin_lock_init(&dev->power.lock); - dev->power.power_state = PMSG_INVALID; + if (!dev->power.early_init) { + spin_lock_init(&dev->power.lock); + dev->power.power_state = PMSG_INVALID; + dev->power.early_init = true; + } } #ifdef CONFIG_PM_RUNTIME +static inline void pm_runtime_early_init(struct device *dev) +{ + dev->power.disable_depth = 1; + device_pm_init_common(dev); +} + extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); #else /* !CONFIG_PM_RUNTIME */ +static inline void pm_runtime_early_init(struct device *dev) +{ + device_pm_init_common(dev); +} + static inline void pm_runtime_init(struct device *dev) {} static inline void pm_runtime_remove(struct device *dev) {} diff --git a/include/linux/pm.h b/include/linux/pm.h index f067e60a3832..716517af1543 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -510,6 +510,7 @@ struct dev_pm_info { bool is_prepared:1; /* Owned by the PM core */ bool is_suspended:1; /* Ditto */ bool ignore_children:1; + bool early_init:1; /* Owned by the PM core */ spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; -- cgit v1.2.3 From 6fb28badf207a6d8a78906353772e1c3f560a977 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:45:54 +0200 Subject: PM / Domains: Rename the always_on device flag to syscore The always_on device 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. Change name of that flag to "syscore" to better reflect its purpose. Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/base/power/domain.c | 24 ++++++++++++------------ drivers/clocksource/sh_cmt.c | 2 +- drivers/clocksource/sh_mtu2.c | 2 +- drivers/clocksource/sh_tmu.c | 2 +- include/linux/pm_domain.h | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 515c8ecf01ce..15234ecd7edb 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)->always_on)) + || pdd->dev->power.irq_safe || to_gpd_data(pdd)->syscore)) not_suspended++; if (not_suspended > genpd->in_progress) @@ -578,7 +578,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) might_sleep_if(!genpd->dev_irq_safe); - if (dev_gpd_data(dev)->always_on) + if (dev_gpd_data(dev)->syscore) return -EBUSY; stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; @@ -983,7 +983,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)->always_on + if (genpd->suspend_power_off || dev_gpd_data(dev)->syscore || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; @@ -1016,7 +1016,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)->always_on + if (genpd->suspend_power_off || dev_gpd_data(dev)->syscore || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; @@ -1136,7 +1136,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)->always_on ? + return genpd->suspend_power_off || dev_gpd_data(dev)->syscore ? 0 : genpd_stop_dev(genpd, dev); } @@ -1157,7 +1157,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)->always_on ? + return genpd->suspend_power_off || dev_gpd_data(dev)->syscore ? 0 : genpd_start_dev(genpd, dev); } @@ -1253,7 +1253,7 @@ static int pm_genpd_restore_noirq(struct device *dev) pm_genpd_sync_poweron(genpd); - return dev_gpd_data(dev)->always_on ? 0 : genpd_start_dev(genpd, dev); + return dev_gpd_data(dev)->syscore ? 0 : genpd_start_dev(genpd, dev); } /** @@ -1526,11 +1526,11 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, } /** - * pm_genpd_dev_always_on - Set/unset the "always on" flag for a given device. + * 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 "always on" flag. + * @val: The new value of the device's "syscore" flag. */ -void pm_genpd_dev_always_on(struct device *dev, bool val) +void pm_genpd_dev_syscore(struct device *dev, bool val) { struct pm_subsys_data *psd; unsigned long flags; @@ -1539,11 +1539,11 @@ void pm_genpd_dev_always_on(struct device *dev, bool val) psd = dev_to_psd(dev); if (psd && psd->domain_data) - to_gpd_data(psd->domain_data)->always_on = val; + to_gpd_data(psd->domain_data)->syscore = val; spin_unlock_irqrestore(&dev->power.lock, flags); } -EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on); +EXPORT_SYMBOL_GPL(pm_genpd_dev_syscore); /** * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index c06d27fc59e8..c6fbb9f71911 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_always_on(&pdev->dev, true); + pm_genpd_dev_syscore(&pdev->dev, true); } if (p) { diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index a55bb905812e..278c18abb2ae 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_always_on(&pdev->dev, true); + pm_genpd_dev_syscore(&pdev->dev, true); } if (p) { diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 7d700829bb41..5319689c579c 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_always_on(&pdev->dev, true); + pm_genpd_dev_syscore(&pdev->dev, true); } if (p) { diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index ab83cf3dfaac..dab0938603fa 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -114,7 +114,7 @@ struct generic_pm_domain_data { struct mutex lock; unsigned int refcount; bool need_restore; - bool always_on; + bool syscore; }; #ifdef CONFIG_PM_GENERIC_DOMAINS @@ -153,7 +153,7 @@ 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_always_on(struct device *dev, bool val); +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 +199,7 @@ static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, { return -ENOSYS; } -static inline void pm_genpd_dev_always_on(struct device *dev, bool val) {} +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 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(-) 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 e2e3e4e51ebdcd757079bd7ec5dcc9dfb2ebce24 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:47:29 +0200 Subject: PM / Domains: Do not measure start time for "irq safe" devices The genpd_start_dev() routine used by pm_genpd_runtime_resume() to put "irq safe" devices into the full power state measures the time necessary to "start" the device and updates its PM QoS timing data if necessary. This may lead to a deadlock if the given device is a clock source and genpd_start_dev() is invoked from within the clock source's .enable() routine, which will happen if that routine uses pm_runtime_get_sync(), for example, to ensure that the device is operational. For this reason, introduce a special routine analogous to genpd_start_dev(), called genpd_start_dev_no_timing(), that doesn't carry out the time measurement, and make pm_genpd_runtime_resume() use it instead of genpd_start_dev() to power up "irq safe" devices. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 52172754ff78..d7e71b5b080e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -75,6 +75,12 @@ static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) start_latency_ns, "start"); } +static int genpd_start_dev_no_timing(struct generic_pm_domain *genpd, + struct device *dev) +{ + return GENPD_DEV_CALLBACK(genpd, int, start, dev); +} + static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) { bool ret = false; @@ -626,7 +632,7 @@ static int pm_genpd_runtime_resume(struct device *dev) /* If power.irq_safe, the PM domain is never powered off. */ if (dev->power.irq_safe) - return genpd_start_dev(genpd, dev); + return genpd_start_dev_no_timing(genpd, dev); mutex_lock(&genpd->lock); ret = __pm_genpd_poweron(genpd); -- cgit v1.2.3 From 61a53bfaa11644b8e9850ac79024b06465a43518 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:48:17 +0200 Subject: sh: TMU: Basic runtime PM support Modify the SH TMU clock source/clock event device driver to support runtime PM at a basic level (i.e. device clocks can be disabled and enabled, but domain power must be on, because the devices have to be marked as "irq safe"). Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_tmu.c | 80 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 81b0239718ee..0cc4add88279 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -45,6 +45,7 @@ struct sh_tmu_priv { struct clock_event_device ced; struct clocksource cs; bool cs_enabled; + unsigned int enable_count; }; static DEFINE_RAW_SPINLOCK(sh_tmu_lock); @@ -109,7 +110,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); } -static int sh_tmu_enable(struct sh_tmu_priv *p) +static int __sh_tmu_enable(struct sh_tmu_priv *p) { int ret; @@ -137,7 +138,18 @@ static int sh_tmu_enable(struct sh_tmu_priv *p) return 0; } -static void sh_tmu_disable(struct sh_tmu_priv *p) +static int sh_tmu_enable(struct sh_tmu_priv *p) +{ + if (p->enable_count++ > 0) + return 0; + + pm_runtime_get_sync(&p->pdev->dev); + dev_pm_syscore_device(&p->pdev->dev, true); + + return __sh_tmu_enable(p); +} + +static void __sh_tmu_disable(struct sh_tmu_priv *p) { /* disable channel */ sh_tmu_start_stop_ch(p, 0); @@ -149,6 +161,20 @@ static void sh_tmu_disable(struct sh_tmu_priv *p) clk_disable(p->clk); } +static void sh_tmu_disable(struct sh_tmu_priv *p) +{ + if (WARN_ON(p->enable_count == 0)) + return; + + if (--p->enable_count > 0) + return; + + __sh_tmu_disable(p); + + dev_pm_syscore_device(&p->pdev->dev, false); + pm_runtime_put(&p->pdev->dev); +} + static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, int periodic) { @@ -205,11 +231,15 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs) struct sh_tmu_priv *p = cs_to_sh_tmu(cs); int ret; + if (WARN_ON(p->cs_enabled)) + return 0; + ret = sh_tmu_enable(p); if (!ret) { __clocksource_updatefreq_hz(cs, p->rate); p->cs_enabled = true; } + return ret; } @@ -217,7 +247,8 @@ static void sh_tmu_clocksource_disable(struct clocksource *cs) { struct sh_tmu_priv *p = cs_to_sh_tmu(cs); - WARN_ON(!p->cs_enabled); + if (WARN_ON(!p->cs_enabled)) + return; sh_tmu_disable(p); p->cs_enabled = false; @@ -227,19 +258,26 @@ static void sh_tmu_clocksource_suspend(struct clocksource *cs) { struct sh_tmu_priv *p = cs_to_sh_tmu(cs); - if (p->cs_enabled) - sh_tmu_disable(p); + if (!p->cs_enabled) + return; - pm_genpd_syscore_poweroff(&p->pdev->dev); + if (--p->enable_count == 0) { + __sh_tmu_disable(p); + pm_genpd_syscore_poweroff(&p->pdev->dev); + } } static void sh_tmu_clocksource_resume(struct clocksource *cs) { struct sh_tmu_priv *p = cs_to_sh_tmu(cs); - pm_genpd_syscore_poweron(&p->pdev->dev); - if (p->cs_enabled) - sh_tmu_enable(p); + if (!p->cs_enabled) + return; + + if (p->enable_count++ == 0) { + pm_genpd_syscore_poweron(&p->pdev->dev); + __sh_tmu_enable(p); + } } static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, @@ -434,6 +472,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) ret = PTR_ERR(p->clk); goto err1; } + p->cs_enabled = false; + p->enable_count = 0; return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), cfg->clockevent_rating, @@ -447,18 +487,17 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) static int __devinit sh_tmu_probe(struct platform_device *pdev) { struct sh_tmu_priv *p = platform_get_drvdata(pdev); + struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { - struct sh_timer_config *cfg = pdev->dev.platform_data; - - if (cfg->clocksource_rating || cfg->clockevent_rating) - dev_pm_syscore_device(&pdev->dev, true); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); - return 0; + goto out; } p = kmalloc(sizeof(*p), GFP_KERNEL); @@ -471,8 +510,19 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev) if (ret) { kfree(p); platform_set_drvdata(pdev, NULL); + pm_runtime_idle(&pdev->dev); + return ret; } - return ret; + if (is_early_platform_device(pdev)) + return 0; + + out: + if (cfg->clockevent_rating || cfg->clocksource_rating) + pm_runtime_irq_safe(&pdev->dev); + else + pm_runtime_idle(&pdev->dev); + + return 0; } static int __devexit sh_tmu_remove(struct platform_device *pdev) -- cgit v1.2.3 From bad813831e291cf34a007e6f03c37cf95037c868 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:48:57 +0200 Subject: sh: CMT: Basic runtime PM support Modify the SH CMT clock source/clock event device driver to support runtime PM at a basic level (i.e. device clocks can be disabled and enabled, but domain power must be on, because the devices have to be marked as "irq safe"). Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_cmt.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a515605bf8f5..a5f7829f2799 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -33,6 +33,7 @@ #include #include #include +#include struct sh_cmt_priv { void __iomem *mapbase; @@ -52,6 +53,7 @@ struct sh_cmt_priv { struct clock_event_device ced; struct clocksource cs; unsigned long total_cycles; + bool cs_enabled; }; static DEFINE_RAW_SPINLOCK(sh_cmt_lock); @@ -155,6 +157,9 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) { int k, ret; + pm_runtime_get_sync(&p->pdev->dev); + dev_pm_syscore_device(&p->pdev->dev, true); + /* enable clock */ ret = clk_enable(p->clk); if (ret) { @@ -221,6 +226,9 @@ static void sh_cmt_disable(struct sh_cmt_priv *p) /* stop clock */ clk_disable(p->clk); + + dev_pm_syscore_device(&p->pdev->dev, false); + pm_runtime_put(&p->pdev->dev); } /* private flags */ @@ -451,17 +459,26 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs) int ret; struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + WARN_ON(p->cs_enabled); + p->total_cycles = 0; ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); - if (!ret) + if (!ret) { __clocksource_updatefreq_hz(cs, p->rate); + p->cs_enabled = true; + } return ret; } static void sh_cmt_clocksource_disable(struct clocksource *cs) { - sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); + struct sh_cmt_priv *p = cs_to_sh_cmt(cs); + + WARN_ON(!p->cs_enabled); + + sh_cmt_stop(p, FLAG_CLOCKSOURCE); + p->cs_enabled = false; } static void sh_cmt_clocksource_suspend(struct clocksource *cs) @@ -693,6 +710,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) dev_err(&p->pdev->dev, "registration failed\n"); goto err1; } + p->cs_enabled = false; ret = setup_irq(irq, &p->irqaction); if (ret) { @@ -711,18 +729,17 @@ err0: static int __devinit sh_cmt_probe(struct platform_device *pdev) { struct sh_cmt_priv *p = platform_get_drvdata(pdev); + struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { - struct sh_timer_config *cfg = pdev->dev.platform_data; - - if (cfg->clocksource_rating || cfg->clockevent_rating) - dev_pm_syscore_device(&pdev->dev, true); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); - return 0; + goto out; } p = kmalloc(sizeof(*p), GFP_KERNEL); @@ -735,8 +752,19 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev) if (ret) { kfree(p); platform_set_drvdata(pdev, NULL); + pm_runtime_idle(&pdev->dev); + return ret; } - return ret; + if (is_early_platform_device(pdev)) + return 0; + + out: + if (cfg->clockevent_rating || cfg->clocksource_rating) + pm_runtime_irq_safe(&pdev->dev); + else + pm_runtime_idle(&pdev->dev); + + return 0; } static int __devexit sh_cmt_remove(struct platform_device *pdev) -- cgit v1.2.3 From 3cb6f10a4d925ec21f414bc30a8aded2830963e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 13 Aug 2012 14:00:16 +0200 Subject: sh: MTU2: Basic runtime PM support Modify the SH MTU2 clock event device driver to support runtime PM at a basic level (i.e. device clocks can be disabled and enabled, but domain power must be on, because the device has to be marked as "irq safe"). Signed-off-by: Rafael J. Wysocki Acked-by: Magnus Damm --- drivers/clocksource/sh_mtu2.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 1a95cad96819..c5eea858054a 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -32,6 +32,7 @@ #include #include #include +#include struct sh_mtu2_priv { void __iomem *mapbase; @@ -123,6 +124,9 @@ static int sh_mtu2_enable(struct sh_mtu2_priv *p) { int ret; + pm_runtime_get_sync(&p->pdev->dev); + dev_pm_syscore_device(&p->pdev->dev, true); + /* enable clock */ ret = clk_enable(p->clk); if (ret) { @@ -157,6 +161,9 @@ static void sh_mtu2_disable(struct sh_mtu2_priv *p) /* stop clock */ clk_disable(p->clk); + + dev_pm_syscore_device(&p->pdev->dev, false); + pm_runtime_put(&p->pdev->dev); } static irqreturn_t sh_mtu2_interrupt(int irq, void *dev_id) @@ -317,18 +324,17 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) static int __devinit sh_mtu2_probe(struct platform_device *pdev) { struct sh_mtu2_priv *p = platform_get_drvdata(pdev); + struct sh_timer_config *cfg = pdev->dev.platform_data; int ret; if (!is_early_platform_device(pdev)) { - struct sh_timer_config *cfg = pdev->dev.platform_data; - - if (cfg->clockevent_rating) - dev_pm_syscore_device(&pdev->dev, true); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); } if (p) { dev_info(&pdev->dev, "kept as earlytimer\n"); - return 0; + goto out; } p = kmalloc(sizeof(*p), GFP_KERNEL); @@ -341,8 +347,19 @@ static int __devinit sh_mtu2_probe(struct platform_device *pdev) if (ret) { kfree(p); platform_set_drvdata(pdev, NULL); + pm_runtime_idle(&pdev->dev); + return ret; } - return ret; + if (is_early_platform_device(pdev)) + return 0; + + out: + if (cfg->clockevent_rating) + pm_runtime_irq_safe(&pdev->dev); + else + pm_runtime_idle(&pdev->dev); + + return 0; } static int __devexit sh_mtu2_remove(struct platform_device *pdev) -- cgit v1.2.3 From feb70af0e3ac6817327be70b47731039ea135dbc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 13 Aug 2012 14:00:25 +0200 Subject: PM: Do not use the syscore flag for runtime PM The syscore device PM flag used to mark the devices (belonging to PM domains) that should never be turned off, except for the system core (syscore) suspend/hibernation and resume stages, need not be accessed by the runtime PM core functions, because all of the devices it is set for need to be marked as "irq safe" anyway and are protected from being turned off by runtime PM by ensuring that their usage counters are always set. For this reason, make the syscore flag system-wide PM-specific and simplify the code used for manipulating it, because it need not acquire the device's power.lock any more. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/common.c | 15 --------------- drivers/base/power/domain.c | 2 +- drivers/base/power/runtime.c | 2 +- include/linux/device.h | 7 +++++++ include/linux/pm.h | 6 +----- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index cf7a85134730..39c32529b833 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -83,18 +83,3 @@ 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 d7e71b5b080e..5f4606f13be6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -442,7 +442,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 || pdd->dev->power.syscore)) + || pdd->dev->power.irq_safe)) not_suspended++; if (not_suspended > genpd->in_progress) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index bd1de3980919..7d9c1cb1c39a 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 || dev->power.syscore) + else if (dev->power.disable_depth > 0) retval = -EACCES; else if (atomic_read(&dev->power.usage_count) > 0) retval = -EAGAIN; diff --git a/include/linux/device.h b/include/linux/device.h index 52a5f15a2223..86529e642d6c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -772,6 +772,13 @@ static inline void pm_suspend_ignore_children(struct device *dev, bool enable) dev->power.ignore_children = enable; } +static inline void dev_pm_syscore_device(struct device *dev, bool val) +{ +#ifdef CONFIG_PM_SLEEP + dev->power.syscore = val; +#endif +} + static inline void device_lock(struct device *dev) { mutex_lock(&dev->mutex); diff --git a/include/linux/pm.h b/include/linux/pm.h index b79a0dd3bc6d..44d1f2307dbc 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -43,12 +43,8 @@ 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 { @@ -515,13 +511,13 @@ 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; struct completion completion; struct wakeup_source *wakeup; bool wakeup_path:1; + bool syscore:1; #else unsigned int should_wakeup:1; #endif -- cgit v1.2.3