[RFC PATCH 16/17] ARM: PM: enhance idle pm notifiers
Lorenzo Pieralisi
lorenzo.pieralisi at arm.com
Thu Jul 7 11:50:29 EDT 2011
This patch adds notifiers to manage low-power entry/exit in a platform
independent manner through a series of callbacks.
The goal is to enhance CPU specific notifiers with a different notifier
chain that executes callbacks defined to put the system into low-power
states (C-state). The callback must be executed with IRQ disabled
and caches still up and running, which in particular means that spinlocks
implemented as ldrex/strex are still usable on ARM.
The callbacks are a means to achieve common idle code, where the
platform_pm_enter()/exit() functions trigger the actions required to
enter/exit low-power states (PCU, clock tree and power domain
programming) for a specific platform.
Within the common idle code for ARM, the callbacks executed upon
platform_pm_enter/exit run with a virtual mapping cloned from init_mm
which means that the virtual address space is still accessible.
The notifier is passed a (void *) argument, that in the context of
common idle code is meant to define cpu and cluster states in order to allow
the platform specific callback to handle power down/up actions accordingly.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi at arm.com>
---
arch/arm/include/asm/cpu_pm.h | 15 +++++++
arch/arm/kernel/cpu_pm.c | 92 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/cpu_pm.h b/arch/arm/include/asm/cpu_pm.h
index b4bb715..19b8106 100644
--- a/arch/arm/include/asm/cpu_pm.h
+++ b/arch/arm/include/asm/cpu_pm.h
@@ -42,8 +42,21 @@ enum cpu_pm_event {
CPU_COMPLEX_PM_EXIT,
};
+enum platform_pm_event {
+ /* Time to execute code to shutdown cpu/cluster */
+ CPU_PM_SHUTDOWN,
+
+ /* Shutdown cpu/cluster failed */
+ CPU_PM_SHUTDOWN_FAILED,
+
+ /* Time to execute code to wakeup cpu/cluster */
+ CPU_PM_WAKEUP,
+};
+
int cpu_pm_register_notifier(struct notifier_block *nb);
int cpu_pm_unregister_notifier(struct notifier_block *nb);
+int platform_pm_register_notifier(struct notifier_block *nb);
+int platform__pm_unregister_notifier(struct notifier_block *nb);
int cpu_pm_enter(void);
int cpu_pm_exit(void);
@@ -51,4 +64,6 @@ int cpu_pm_exit(void);
int cpu_complex_pm_enter(void);
int cpu_complex_pm_exit(void);
+int platform_pm_enter(void *);
+int platform_pm_exit(void *);
#endif
diff --git a/arch/arm/kernel/cpu_pm.c b/arch/arm/kernel/cpu_pm.c
index 48a5b53..2f1f661 100644
--- a/arch/arm/kernel/cpu_pm.c
+++ b/arch/arm/kernel/cpu_pm.c
@@ -47,6 +47,8 @@
static DEFINE_RWLOCK(cpu_pm_notifier_lock);
static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
+static DEFINE_RWLOCK(platform_pm_notifier_lock);
+static RAW_NOTIFIER_HEAD(platform_pm_notifier_chain);
int cpu_pm_register_notifier(struct notifier_block *nb)
{
@@ -74,6 +76,33 @@ int cpu_pm_unregister_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
+int platform_pm_register_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&platform_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_register(&platform_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&platform_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_pm_register_notifier);
+
+int platform_pm_unregister_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&platform_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_unregister(&platform_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&platform_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_pm_unregister_notifier);
+
+/* These two functions are not really worth duplicating, they must be merged */
static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
{
int ret;
@@ -84,8 +113,19 @@ static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
return notifier_to_errno(ret);
}
+static int __platform_pm_notify(enum platform_pm_event event,
+ void *arg, int nr_to_call, int *nr_calls)
+{
+ int ret;
+
+ ret = __raw_notifier_call_chain(&platform_pm_notifier_chain, event, arg,
+ nr_to_call, nr_calls);
+
+ return notifier_to_errno(ret);
+}
+
/**
- * cpm_pm_enter
+ * cpu_pm_enter
*
* Notifies listeners that a single cpu is entering a low power state that may
* cause some blocks in the same power domain as the cpu to reset.
@@ -110,7 +150,7 @@ int cpu_pm_enter(void)
EXPORT_SYMBOL_GPL(cpu_pm_enter);
/**
- * cpm_pm_exit
+ * cpu_pm_exit
*
* Notifies listeners that a single cpu is exiting a low power state that may
* have caused some blocks in the same power domain as the cpu to reset.
@@ -130,7 +170,7 @@ int cpu_pm_exit(void)
EXPORT_SYMBOL_GPL(cpu_pm_exit);
/**
- * cpm_complex_pm_enter
+ * cpu_complex_pm_enter
*
* Notifies listeners that all cpus in a power domain are entering a low power
* state that may cause some blocks in the same power domain to reset.
@@ -157,7 +197,7 @@ int cpu_complex_pm_enter(void)
EXPORT_SYMBOL_GPL(cpu_complex_pm_enter);
/**
- * cpm_pm_enter
+ * cpu_complex_pm_exit
*
* Notifies listeners that a single cpu is entering a low power state that may
* cause some blocks in the same power domain as the cpu to reset.
@@ -179,3 +219,47 @@ int cpu_complex_pm_exit(void)
return ret;
}
EXPORT_SYMBOL_GPL(cpu_complex_pm_exit);
+/*
+ * platform_pm_enter
+ *
+ * Notifies listeners that either cpu or cluster should enter low-power
+ * Should carry out the actions needed before issuing a processor specific
+ * instruction (wfi on ARM)
+ * Must be called with IRQ disabled
+ * arg is a parameter containing information about targeted platform state
+ */
+int platform_pm_enter(void *arg)
+{
+ int nr_calls;
+ int ret = 0;
+
+ read_lock(&platform_pm_notifier_lock);
+ ret = __platform_pm_notify(CPU_PM_SHUTDOWN, arg, -1, &nr_calls);
+ if (ret)
+ __platform_pm_notify(CPU_PM_SHUTDOWN_FAILED, arg,
+ nr_calls - 1, NULL);
+ read_unlock(&platform_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_pm_enter);
+
+/*
+ * platform_pm_exit
+ *
+ * Notifies listeners that either cpu or cluster should undo actions executed
+ * before entering low-power mode
+ * Must be called with IRQ disabled
+ * arg is a parameter containing information about targeted platform state
+ */
+int platform_pm_exit(void *arg)
+{
+ int ret;
+
+ read_lock(&platform_pm_notifier_lock);
+ ret = __platform_pm_notify(CPU_PM_WAKEUP, arg, -1, NULL);
+ read_unlock(&platform_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_pm_exit);
--
1.7.4.4
More information about the linux-arm-kernel
mailing list