* [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains
@ 2012-07-29 14:12 Rafael J. Wysocki
2012-07-29 14:13 ` [RFC][PATCH 1/6] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
` (6 more replies)
0 siblings, 7 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:12 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
Hi all,
There is a problem with clock sources and clock event devices belonging to PM
domains that they have to be marked as "always on", to prevent those domains
from being turned off unexpectedly, which also prevents the domains from
being turned off during system suspend, although that might be done and would
be desirable.
The following patch series introduces a mechanism by which the generic PM
domains framework can be notified that a given domain may be turned off
after timekeeping has been suspended, which allows the domains that contain
clock sources and clock event devices to be turned off before the system
enters the target sleep state (so that less power is used in that state).
[1/6] Introduce simplified power on routine for PM domains (to be used during
system resume from suspend/hibernation and during "syscore" resume).
[2/6] Introduce function for notifying the generic PM domains framework that
the given device may be treated as suspended (so its domain may be
turned off if this has been the last active device in it).
[3/6] Introduce suspend/resume callbacks for clock event devices.
[4/6] Make the SH TMU driver use suspend/resume callbacks for clock sources
and clock event devices to notify the generic PM domains framework that
those devices may be regarded as suspended (during system suspend) or
that they have to be turned on immediately (during system resume).
[5/6] Like [4/6] but for the SH CMT driver.
[6/6] Analogous to [4/6], but for the SH MTU2 driver.
The patchset has been tested on the SH7372 Mackerel board.
Thanks,
Rafael
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 1/6] PM / Domains: Introduce simplified power on routine for system resume
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
@ 2012-07-29 14:13 ` Rafael J. Wysocki
2012-07-29 14:14 ` [RFC][PATCH 2/6] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
` (5 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:13 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/domain.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -777,6 +777,32 @@ static void pm_genpd_sync_poweroff(struc
}
/**
+ * 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.
* @genpd: PM domain the device belongs to.
@@ -979,7 +1005,7 @@ static int pm_genpd_resume_noirq(struct
* 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
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
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);
}
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 2/6] PM / Domains: Add power off/on function for system core suspend stage
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-07-29 14:13 ` [RFC][PATCH 1/6] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
@ 2012-07-29 14:14 ` Rafael J. Wysocki
2012-07-29 14:15 ` [RFC][PATCH 3/6] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
` (4 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:14 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/domain.c | 60 +++++++++++++++++++++++++++++++++++++++-----
include/linux/pm_domain.h | 13 +++++++++
2 files changed, 67 insertions(+), 6 deletions(-)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -63,6 +63,24 @@ struct generic_pm_domain *dev_to_genpd(s
return pd_to_genpd(dev->pm_domain);
}
+/**
+ * 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 int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
{
return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev,
@@ -750,9 +768,10 @@ static int genpd_thaw_dev(struct generic
* 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(struc
* 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,34 @@ static void pm_genpd_complete(struct 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 (!dev_gpd_data(dev)->always_on)
+ 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
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -172,6 +172,8 @@ extern int pm_genpd_poweron(struct gener
extern bool default_stop_ok(struct device *dev);
+extern void pm_genpd_syscore_switch(struct device *dev, bool suspend);
+
extern struct dev_power_governor pm_domain_always_on_gov;
#else
@@ -241,6 +243,7 @@ static inline bool default_stop_ok(struc
{
return false;
}
+static inline void pm_genpd_syscore_switch(struct device *dev, bool suspend) {}
#define simple_qos_governor NULL
#define pm_domain_always_on_gov NULL
#endif
@@ -250,6 +253,16 @@ static inline int pm_genpd_remove_callba
return __pm_genpd_remove_callbacks(dev, true);
}
+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);
+}
+
#ifdef CONFIG_PM_GENERIC_DOMAINS_RUNTIME
extern void genpd_queue_power_off_work(struct generic_pm_domain *genpd);
extern void pm_genpd_poweroff_unused(void);
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 3/6] timekeeping: Add suspend and resume of clock event devices
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-07-29 14:13 ` [RFC][PATCH 1/6] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
2012-07-29 14:14 ` [RFC][PATCH 2/6] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
@ 2012-07-29 14:15 ` Rafael J. Wysocki
2012-07-29 14:16 ` [RFC][PATCH 4/6] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
` (3 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:15 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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 <rjw@sisk.pl>
---
include/linux/clockchips.h | 5 +++++
kernel/time/clockevents.c | 24 ++++++++++++++++++++++++
kernel/time/timekeeping.c | 2 ++
3 files changed, 31 insertions(+)
Index: linux/include/linux/clockchips.h
=================================--- linux.orig/include/linux/clockchips.h
+++ linux/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
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
Index: linux/kernel/time/clockevents.c
=================================--- linux.orig/kernel/time/clockevents.c
+++ linux/kernel/time/clockevents.c
@@ -397,6 +397,30 @@ void clockevents_exchange_device(struct
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
Index: linux/kernel/time/timekeeping.c
=================================--- linux.orig/kernel/time/timekeeping.c
+++ linux/kernel/time/timekeeping.c
@@ -733,6 +733,7 @@ static void timekeeping_resume(void)
read_persistent_clock(&ts);
+ clockevents_resume();
clocksource_resume();
write_seqlock_irqsave(&timekeeper.lock, flags);
@@ -791,6 +792,7 @@ static int timekeeping_suspend(void)
clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
clocksource_suspend();
+ clockevents_suspend();
return 0;
}
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 4/6] sh: TMU: Introduce clocksource/clock events suspend/resume routines
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (2 preceding siblings ...)
2012-07-29 14:15 ` [RFC][PATCH 3/6] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
@ 2012-07-29 14:16 ` Rafael J. Wysocki
2012-07-29 14:16 ` [RFC][PATCH 5/6] sh: CMT: " Rafael J. Wysocki
` (2 subsequent siblings)
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:16 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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).
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/clocksource/sh_tmu.c | 39 +++++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/drivers/clocksource/sh_tmu.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
struct sh_tmu_priv {
void __iomem *mapbase;
@@ -214,6 +215,22 @@ static void sh_tmu_clocksource_disable(s
sh_tmu_disable(cs_to_sh_tmu(cs));
}
+static void sh_tmu_clocksource_suspend(struct clocksource *cs)
+{
+ struct sh_tmu_priv *p = cs_to_sh_tmu(cs);
+
+ 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);
+ sh_tmu_enable(p);
+}
+
static int sh_tmu_register_clocksource(struct sh_tmu_priv *p,
char *name, unsigned long rating)
{
@@ -224,6 +241,8 @@ static int sh_tmu_register_clocksource(s
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 +320,16 @@ static int sh_tmu_clock_event_next(unsig
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 +345,8 @@ static void sh_tmu_register_clockevent(s
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 +438,12 @@ static int __devinit sh_tmu_probe(struct
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 5/6] sh: CMT: Introduce clocksource/clock events suspend/resume routines
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (3 preceding siblings ...)
2012-07-29 14:16 ` [RFC][PATCH 4/6] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
@ 2012-07-29 14:16 ` Rafael J. Wysocki
2012-07-29 14:17 ` [RFC][PATCH 6/6] sh: MTU2: Introduce clock " Rafael J. Wysocki
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:16 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_cmt.c | 35 +++++++++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)
Index: linux/drivers/clocksource/sh_cmt.c
=================================--- linux.orig/drivers/clocksource/sh_cmt.c
+++ linux/drivers/clocksource/sh_cmt.c
@@ -464,9 +464,20 @@ static void sh_cmt_clocksource_disable(s
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(s
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(unsig
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(s
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
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [RFC][PATCH 6/6] sh: MTU2: Introduce clock events suspend/resume routines
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (4 preceding siblings ...)
2012-07-29 14:16 ` [RFC][PATCH 5/6] sh: CMT: " Rafael J. Wysocki
@ 2012-07-29 14:17 ` Rafael J. Wysocki
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
6 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-07-29 14:17 UTC (permalink / raw)
To: LKML; +Cc: Linux-sh list, Linux PM list, Magnus Damm, Paul Mundt,
Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_mtu2.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
Index: linux/drivers/clocksource/sh_mtu2.c
=================================--- linux.orig/drivers/clocksource/sh_mtu2.c
+++ linux/drivers/clocksource/sh_mtu2.c
@@ -208,6 +208,16 @@ static void sh_mtu2_clock_event_mode(enu
}
}
+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(
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(struc
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (5 preceding siblings ...)
2012-07-29 14:17 ` [RFC][PATCH 6/6] sh: MTU2: Introduce clock " Rafael J. Wysocki
@ 2012-08-05 23:38 ` Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 1/15] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
` (14 more replies)
6 siblings, 15 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:38 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
Hi all,
On Sunday, July 29, 2012, Rafael J. Wysocki wrote:
> There is a problem with clock sources and clock event devices belonging to PM
> domains that they have to be marked as "always on", to prevent those domains
> from being turned off unexpectedly, which also prevents the domains from
> being turned off during system suspend, although that might be done and would
> be desirable.
>
> The following patch series introduces a mechanism by which the generic PM
> domains framework can be notified that a given domain may be turned off
> after timekeeping has been suspended, which allows the domains that contain
> clock sources and clock event devices to be turned off before the system
> enters the target sleep state (so that less power is used in that state).
>
> [1/6] Introduce simplified power on routine for PM domains (to be used during
> system resume from suspend/hibernation and during "syscore" resume).
> [2/6] Introduce function for notifying the generic PM domains framework that
> the given device may be treated as suspended (so its domain may be
> turned off if this has been the last active device in it).
> [3/6] Introduce suspend/resume callbacks for clock event devices.
> [4/6] Make the SH TMU driver use suspend/resume callbacks for clock sources
> and clock event devices to notify the generic PM domains framework that
> those devices may be regarded as suspended (during system suspend) or
> that they have to be turned on immediately (during system resume).
> [5/6] Like [4/6] but for the SH CMT driver.
> [6/6] Analogous to [4/6], but for the SH MTU2 driver.
>
> The patchset has been tested on the SH7372 Mackerel board.
There were no comments, so I think I can go a bit further and add runtime PM
support to those drivers too. :-)
This requires some core changes, but they aren't too invasive in my opinion.
Patches [1-6/15] are more-or-less the same as before modulo some fixes for bugs
found during more thorough testing. The remaining patches make the following
changes:
[7/15] - Clean up device PM initialization.
[8/15] - Allow early platform device drivers to use runtime PM helper functions.
[9/15] - Rename the "always_on" device flag to "syscore".
[10/15] - Move the "syscore" flag to the core (from PM domains framework).
[11/15] - Rework the "starting" of devices flagged as "irq safe" in PM domains.
[12/15] - Add runtime PM support to the SH TMU driver.
[13/15] - Add runtime PM support to the SH CMT driver.
[14/15] - Add runtime PM support to the SH MTU2 driver.
[15/15] - Stop using the "syscore" device flag for runtime PM.
Thanks,
Rafael
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 1/15] PM / Domains: Introduce simplified power on routine for system resume
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
@ 2012-08-05 23:39 ` Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 2/15] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
` (13 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:39 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/domain.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -777,6 +777,32 @@ static void pm_genpd_sync_poweroff(struc
}
/**
+ * 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.
* @genpd: PM domain the device belongs to.
@@ -979,7 +1005,7 @@ static int pm_genpd_resume_noirq(struct
* 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
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
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);
}
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 2/15] PM / Domains: Add power off/on function for system core suspend stage
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 1/15] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
@ 2012-08-05 23:39 ` Rafael J. Wysocki
2012-08-05 23:40 ` [PATCH 3/15] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
` (12 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:39 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/domain.c | 57 +++++++++++++++++++++++++++++++++++++++-----
include/linux/pm_domain.h | 16 ++++++++++++
kernel/power/Kconfig | 4 +++
3 files changed, 71 insertions(+), 6 deletions(-)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -697,6 +697,24 @@ static inline void genpd_power_off_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
* 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(struc
* 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 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
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/include/linux/pm_domain.h
@@ -258,4 +258,20 @@ static inline void genpd_queue_power_off
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 */
Index: linux/kernel/power/Kconfig
=================================--- linux.orig/kernel/power/Kconfig
+++ linux/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
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 3/15] timekeeping: Add suspend and resume of clock event devices
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 1/15] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 2/15] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
@ 2012-08-05 23:40 ` Rafael J. Wysocki
2012-08-05 23:41 ` [PATCH 4/15] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
` (11 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:40 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
include/linux/clockchips.h | 5 +++++
kernel/time/clockevents.c | 24 ++++++++++++++++++++++++
kernel/time/timekeeping.c | 2 ++
3 files changed, 31 insertions(+)
Index: linux/include/linux/clockchips.h
=================================--- linux.orig/include/linux/clockchips.h
+++ linux/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
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
Index: linux/kernel/time/clockevents.c
=================================--- linux.orig/kernel/time/clockevents.c
+++ linux/kernel/time/clockevents.c
@@ -397,6 +397,30 @@ void clockevents_exchange_device(struct
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
Index: linux/kernel/time/timekeeping.c
=================================--- linux.orig/kernel/time/timekeeping.c
+++ linux/kernel/time/timekeeping.c
@@ -750,6 +750,7 @@ static void timekeeping_resume(void)
read_persistent_clock(&ts);
+ clockevents_resume();
clocksource_resume();
write_seqlock_irqsave(&tk->lock, flags);
@@ -809,6 +810,7 @@ static int timekeeping_suspend(void)
clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
clocksource_suspend();
+ clockevents_suspend();
return 0;
}
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 4/15] sh: TMU: Introduce clocksource/clock events suspend/resume routines
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (2 preceding siblings ...)
2012-08-05 23:40 ` [PATCH 3/15] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
@ 2012-08-05 23:41 ` Rafael J. Wysocki
2012-08-05 23:43 ` [PATCH 5/15] sh: CMT: " Rafael J. Wysocki
` (10 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:41 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_tmu.c | 54 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 50 insertions(+), 4 deletions(-)
Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/drivers/clocksource/sh_tmu.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
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(str
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(s
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(unsig
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(s
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
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 5/15] sh: CMT: Introduce clocksource/clock events suspend/resume routines
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (3 preceding siblings ...)
2012-08-05 23:41 ` [PATCH 4/15] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
@ 2012-08-05 23:43 ` Rafael J. Wysocki
2012-08-05 23:43 ` [PATCH 6/15] sh: MTU2: Introduce clock " Rafael J. Wysocki
` (9 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:43 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_cmt.c | 35 +++++++++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)
Index: linux/drivers/clocksource/sh_cmt.c
=================================--- linux.orig/drivers/clocksource/sh_cmt.c
+++ linux/drivers/clocksource/sh_cmt.c
@@ -464,9 +464,20 @@ static void sh_cmt_clocksource_disable(s
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(s
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(unsig
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(s
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
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 6/15] sh: MTU2: Introduce clock events suspend/resume routines
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (4 preceding siblings ...)
2012-08-05 23:43 ` [PATCH 5/15] sh: CMT: " Rafael J. Wysocki
@ 2012-08-05 23:43 ` Rafael J. Wysocki
2012-08-05 23:44 ` [PATCH 7/15] PM: Reorganize device PM initialization Rafael J. Wysocki
` (8 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:43 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_mtu2.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
Index: linux/drivers/clocksource/sh_mtu2.c
=================================--- linux.orig/drivers/clocksource/sh_mtu2.c
+++ linux/drivers/clocksource/sh_mtu2.c
@@ -208,6 +208,16 @@ static void sh_mtu2_clock_event_mode(enu
}
}
+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(
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(struc
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");
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 7/15] PM: Reorganize device PM initialization
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (5 preceding siblings ...)
2012-08-05 23:43 ` [PATCH 6/15] sh: MTU2: Introduce clock " Rafael J. Wysocki
@ 2012-08-05 23:44 ` Rafael J. Wysocki
2012-08-05 23:45 ` [PATCH 8/15] PM / Runtime: Allow helpers to be called by early platform drivers Rafael J. Wysocki
` (7 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:44 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/main.c | 7 ++-----
drivers/base/power/power.h | 22 +++++++++++++++-------
2 files changed, 17 insertions(+), 12 deletions(-)
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/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;
}
/**
Index: linux/drivers/base/power/power.h
=================================--- linux.orig/drivers/base/power/power.h
+++ linux/drivers/base/power/power.h
@@ -1,5 +1,11 @@
#include <linux/pm_qos.h>
+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(s
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 d
#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(s
#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
/*
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 8/15] PM / Runtime: Allow helpers to be called by early platform drivers
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (6 preceding siblings ...)
2012-08-05 23:44 ` [PATCH 7/15] PM: Reorganize device PM initialization Rafael J. Wysocki
@ 2012-08-05 23:45 ` Rafael J. Wysocki
2012-08-05 23:45 ` [PATCH 9/15] PM / Domains: Rename the always_on device flag to syscore Rafael J. Wysocki
` (6 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:45 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/platform.c | 2 ++
drivers/base/power/power.h | 18 ++++++++++++++++--
include/linux/pm.h | 1 +
3 files changed, 19 insertions(+), 2 deletions(-)
Index: linux/drivers/base/power/power.h
=================================--- linux.orig/drivers/base/power/power.h
+++ linux/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) {}
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/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;
Index: linux/drivers/base/platform.c
=================================--- linux.orig/drivers/base/platform.c
+++ linux/drivers/base/platform.c
@@ -22,6 +22,7 @@
#include <linux/pm_runtime.h>
#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(s
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);
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 9/15] PM / Domains: Rename the always_on device flag to syscore
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (7 preceding siblings ...)
2012-08-05 23:45 ` [PATCH 8/15] PM / Runtime: Allow helpers to be called by early platform drivers Rafael J. Wysocki
@ 2012-08-05 23:45 ` Rafael J. Wysocki
2012-08-05 23:46 ` [PATCH 10/15] PM / Domains: Move syscore flag from subsys data to struct device Rafael J. Wysocki
` (5 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:45 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
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(-)
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/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
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
{
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)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -436,7 +436,7 @@ static int pm_genpd_poweroff(struct gene
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(stru
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
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
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
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 de
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
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 generi
}
/**
- * 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 devic
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.
Index: linux/drivers/clocksource/sh_cmt.c
=================================--- linux.orig/drivers/clocksource/sh_cmt.c
+++ linux/drivers/clocksource/sh_cmt.c
@@ -717,7 +717,7 @@ static int __devinit sh_cmt_probe(struct
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) {
Index: linux/drivers/clocksource/sh_mtu2.c
=================================--- linux.orig/drivers/clocksource/sh_mtu2.c
+++ linux/drivers/clocksource/sh_mtu2.c
@@ -323,7 +323,7 @@ static int __devinit sh_mtu2_probe(struc
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) {
Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/drivers/clocksource/sh_tmu.c
@@ -453,7 +453,7 @@ static int __devinit sh_tmu_probe(struct
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) {
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 10/15] PM / Domains: Move syscore flag from subsys data to struct device
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (8 preceding siblings ...)
2012-08-05 23:45 ` [PATCH 9/15] PM / Domains: Rename the always_on device flag to syscore Rafael J. Wysocki
@ 2012-08-05 23:46 ` Rafael J. Wysocki
2012-08-05 23:47 ` [PATCH 11/15] PM / Domains: Do not measure start time for "irq safe" devices Rafael J. Wysocki
` (4 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:46 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
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(-)
Index: linux/drivers/base/power/common.c
=================================--- linux.orig/drivers/base/power/common.c
+++ linux/drivers/base/power/common.c
@@ -85,3 +85,18 @@ int dev_pm_put_subsys_data(struct device
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);
Index: linux/drivers/clocksource/sh_cmt.c
=================================--- linux.orig/drivers/clocksource/sh_cmt.c
+++ linux/drivers/clocksource/sh_cmt.c
@@ -717,7 +717,7 @@ static int __devinit sh_cmt_probe(struct
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) {
Index: linux/drivers/clocksource/sh_mtu2.c
=================================--- linux.orig/drivers/clocksource/sh_mtu2.c
+++ linux/drivers/clocksource/sh_mtu2.c
@@ -323,7 +323,7 @@ static int __devinit sh_mtu2_probe(struc
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) {
Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/drivers/clocksource/sh_tmu.c
@@ -453,7 +453,7 @@ static int __devinit sh_tmu_probe(struct
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) {
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/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;
Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -134,7 +134,7 @@ static int rpm_check_suspend_allowed(str
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;
Index: linux/drivers/base/power/main.c
=================================--- linux.orig/drivers/base/power/main.c
+++ linux/drivers/base/power/main.c
@@ -405,6 +405,9 @@ static int device_resume_noirq(struct de
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 de
error = dpm_run_callback(callback, dev, state, info);
+ Out:
TRACE_RESUME(error);
return error;
}
@@ -483,6 +487,9 @@ static int device_resume_early(struct de
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 de
error = dpm_run_callback(callback, dev, state, info);
+ Out:
TRACE_RESUME(error);
return error;
}
@@ -567,6 +575,9 @@ static int device_resume(struct device *
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 *
Unlock:
device_unlock(dev);
+
+ Complete:
complete_all(&dev->power.completion);
TRACE_RESUME(error);
@@ -719,6 +732,9 @@ static void device_complete(struct devic
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 d
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 de
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 devic
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
char *info = NULL;
int error = 0;
+ if (dev->power.syscore)
+ return 0;
+
device_lock(dev);
dev->power.wakeup_path = device_may_wakeup(dev);
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -436,7 +436,7 @@ static int pm_genpd_poweroff(struct gene
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(stru
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
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
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
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 de
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
pm_genpd_sync_poweron(genpd);
- return dev_gpd_data(dev)->syscore ? 0 : genpd_start_dev(genpd, dev);
+ return genpd_start_dev(genpd, dev);
}
/**
@@ -1526,26 +1521,6 @@ int pm_genpd_remove_device(struct generi
}
/**
- * 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.
* @val: The new value of the device's "need restore" flag.
Index: linux/include/linux/pm_domain.h
=================================--- linux.orig/include/linux/pm_domain.h
+++ linux/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
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
{
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)
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 11/15] PM / Domains: Do not measure start time for "irq safe" devices
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (9 preceding siblings ...)
2012-08-05 23:46 ` [PATCH 10/15] PM / Domains: Move syscore flag from subsys data to struct device Rafael J. Wysocki
@ 2012-08-05 23:47 ` Rafael J. Wysocki
2012-08-05 23:48 ` [PATCH 12/15] sh: TMU: Basic runtime PM support Rafael J. Wysocki
` (3 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:47 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/base/power/domain.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -75,6 +75,12 @@ static int genpd_start_dev(struct generi
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(struc
/* 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);
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 12/15] sh: TMU: Basic runtime PM support
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (10 preceding siblings ...)
2012-08-05 23:47 ` [PATCH 11/15] PM / Domains: Do not measure start time for "irq safe" devices Rafael J. Wysocki
@ 2012-08-05 23:48 ` Rafael J. Wysocki
2012-08-05 23:48 ` [PATCH 13/15] sh: CMT: " Rafael J. Wysocki
` (2 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:48 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_tmu.c | 80 ++++++++++++++++++++++++++++++++++---------
1 file changed, 65 insertions(+), 15 deletions(-)
Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/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
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_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
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(str
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(s
{
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(s
{
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_pr
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_pr
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
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)
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 13/15] sh: CMT: Basic runtime PM support
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (11 preceding siblings ...)
2012-08-05 23:48 ` [PATCH 12/15] sh: TMU: Basic runtime PM support Rafael J. Wysocki
@ 2012-08-05 23:48 ` Rafael J. Wysocki
2012-08-05 23:49 ` [PATCH 14/15] sh: MTU2: " Rafael J. Wysocki
2012-08-05 23:53 ` [PATCH 15/15] PM: Do not use the syscore flag for runtime PM Rafael J. Wysocki
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:48 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_cmt.c | 44 +++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
Index: linux/drivers/clocksource/sh_cmt.c
=================================--- linux.orig/drivers/clocksource/sh_cmt.c
+++ linux/drivers/clocksource/sh_cmt.c
@@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
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_p
{
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
/* 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(str
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_pr
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
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)
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 14/15] sh: MTU2: Basic runtime PM support
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (12 preceding siblings ...)
2012-08-05 23:48 ` [PATCH 13/15] sh: CMT: " Rafael J. Wysocki
@ 2012-08-05 23:49 ` Rafael J. Wysocki
2012-08-11 9:39 ` Geert Uytterhoeven
2012-08-05 23:53 ` [PATCH 15/15] PM: Do not use the syscore flag for runtime PM Rafael J. Wysocki
14 siblings, 1 reply; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:49 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
drivers/clocksource/sh_mtu2.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
Index: linux/drivers/clocksource/sh_mtu2.c
=================================--- linux.orig/drivers/clocksource/sh_mtu2.c
+++ linux/drivers/clocksource/sh_mtu2.c
@@ -32,6 +32,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
struct sh_mtu2_priv {
void __iomem *mapbase;
@@ -123,6 +124,9 @@ static int sh_mtu2_enable(struct sh_mtu2
{
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_mt
/* 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)
@@ -320,15 +327,13 @@ static int __devinit sh_mtu2_probe(struc
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 +346,19 @@ static int __devinit sh_mtu2_probe(struc
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)
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH 15/15] PM: Do not use the syscore flag for runtime PM
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
` (13 preceding siblings ...)
2012-08-05 23:49 ` [PATCH 14/15] sh: MTU2: " Rafael J. Wysocki
@ 2012-08-05 23:53 ` Rafael J. Wysocki
14 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-05 23:53 UTC (permalink / raw)
To: Linux PM list
Cc: LKML, Linux-sh list, Magnus Damm, Paul Mundt, Thomas Gleixner
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 <rjw@sisk.pl>
---
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(-)
Index: linux/include/linux/pm.h
=================================--- linux.orig/include/linux/pm.h
+++ linux/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
Index: linux/drivers/base/power/common.c
=================================--- linux.orig/drivers/base/power/common.c
+++ linux/drivers/base/power/common.c
@@ -85,18 +85,3 @@ int dev_pm_put_subsys_data(struct device
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);
Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -134,7 +134,7 @@ static int rpm_check_suspend_allowed(str
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;
Index: linux/include/linux/device.h
=================================--- linux.orig/include/linux/device.h
+++ linux/include/linux/device.h
@@ -772,6 +772,13 @@ static inline void pm_suspend_ignore_chi
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);
Index: linux/drivers/base/power/domain.c
=================================--- linux.orig/drivers/base/power/domain.c
+++ linux/drivers/base/power/domain.c
@@ -442,7 +442,7 @@ static int pm_genpd_poweroff(struct gene
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)
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH 14/15] sh: MTU2: Basic runtime PM support
2012-08-05 23:49 ` [PATCH 14/15] sh: MTU2: " Rafael J. Wysocki
@ 2012-08-11 9:39 ` Geert Uytterhoeven
2012-08-11 22:29 ` Rafael J. Wysocki
0 siblings, 1 reply; 25+ messages in thread
From: Geert Uytterhoeven @ 2012-08-11 9:39 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Linux PM list, LKML, Linux-sh list, Magnus Damm, Paul Mundt,
Thomas Gleixner
On Mon, Aug 6, 2012 at 1:49 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> @@ -341,8 +346,19 @@ static int __devinit sh_mtu2_probe(struc
> 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)
drivers/clocksource/sh_mtu2.c:356:6: error: 'cfg' undeclared (first
use in this function)
http://kisskb.ellerman.id.au/kisskb/buildresult/6883515/
Presumably there's a
struct sh_timer_config *cfg = p->pdev->dev.platform_data;
missing at the top of sh_mtu2_probe()
> + pm_runtime_irq_safe(&pdev->dev);
> + else
> + pm_runtime_idle(&pdev->dev);
> +
> + return 0;
> }
>
> static int __devexit sh_mtu2_remove(struct platform_device *pdev)
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH 14/15] sh: MTU2: Basic runtime PM support
2012-08-11 9:39 ` Geert Uytterhoeven
@ 2012-08-11 22:29 ` Rafael J. Wysocki
0 siblings, 0 replies; 25+ messages in thread
From: Rafael J. Wysocki @ 2012-08-11 22:29 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Linux PM list, LKML, Linux-sh list, Magnus Damm, Paul Mundt,
Thomas Gleixner
On Saturday, August 11, 2012, Geert Uytterhoeven wrote:
> On Mon, Aug 6, 2012 at 1:49 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > @@ -341,8 +346,19 @@ static int __devinit sh_mtu2_probe(struc
> > 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)
>
> drivers/clocksource/sh_mtu2.c:356:6: error: 'cfg' undeclared (first
> use in this function)
> http://kisskb.ellerman.id.au/kisskb/buildresult/6883515/
>
> Presumably there's a
>
> struct sh_timer_config *cfg = p->pdev->dev.platform_data;
>
> missing at the top of sh_mtu2_probe()
Precisely.
It should be fixed now, thanks for the report!
Rafael
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2012-08-11 22:29 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-29 14:12 [RFC][PATCH 0/6] PM: Suspend/resume for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-07-29 14:13 ` [RFC][PATCH 1/6] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
2012-07-29 14:14 ` [RFC][PATCH 2/6] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
2012-07-29 14:15 ` [RFC][PATCH 3/6] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
2012-07-29 14:16 ` [RFC][PATCH 4/6] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
2012-07-29 14:16 ` [RFC][PATCH 5/6] sh: CMT: " Rafael J. Wysocki
2012-07-29 14:17 ` [RFC][PATCH 6/6] sh: MTU2: Introduce clock " Rafael J. Wysocki
2012-08-05 23:38 ` [PATCH 0/15] PM: Suspend/resume and runtime PM for clock sources/clock event devices in PM domains Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 1/15] PM / Domains: Introduce simplified power on routine for system resume Rafael J. Wysocki
2012-08-05 23:39 ` [PATCH 2/15] PM / Domains: Add power off/on function for system core suspend stage Rafael J. Wysocki
2012-08-05 23:40 ` [PATCH 3/15] timekeeping: Add suspend and resume of clock event devices Rafael J. Wysocki
2012-08-05 23:41 ` [PATCH 4/15] sh: TMU: Introduce clocksource/clock events suspend/resume routines Rafael J. Wysocki
2012-08-05 23:43 ` [PATCH 5/15] sh: CMT: " Rafael J. Wysocki
2012-08-05 23:43 ` [PATCH 6/15] sh: MTU2: Introduce clock " Rafael J. Wysocki
2012-08-05 23:44 ` [PATCH 7/15] PM: Reorganize device PM initialization Rafael J. Wysocki
2012-08-05 23:45 ` [PATCH 8/15] PM / Runtime: Allow helpers to be called by early platform drivers Rafael J. Wysocki
2012-08-05 23:45 ` [PATCH 9/15] PM / Domains: Rename the always_on device flag to syscore Rafael J. Wysocki
2012-08-05 23:46 ` [PATCH 10/15] PM / Domains: Move syscore flag from subsys data to struct device Rafael J. Wysocki
2012-08-05 23:47 ` [PATCH 11/15] PM / Domains: Do not measure start time for "irq safe" devices Rafael J. Wysocki
2012-08-05 23:48 ` [PATCH 12/15] sh: TMU: Basic runtime PM support Rafael J. Wysocki
2012-08-05 23:48 ` [PATCH 13/15] sh: CMT: " Rafael J. Wysocki
2012-08-05 23:49 ` [PATCH 14/15] sh: MTU2: " Rafael J. Wysocki
2012-08-11 9:39 ` Geert Uytterhoeven
2012-08-11 22:29 ` Rafael J. Wysocki
2012-08-05 23:53 ` [PATCH 15/15] PM: Do not use the syscore flag for runtime PM Rafael J. Wysocki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).