diff mbox

[1/3] ARM: Add cpu power management notifiers

Message ID 1307925825-28566-2-git-send-email-ccross@android.com (mailing list archive)
State New, archived
Headers show

Commit Message

Colin Cross June 13, 2011, 12:43 a.m. UTC
During some CPU power modes entered during idle, hotplug and
suspend, peripherals located in the CPU power domain, such as
the GIC and VFP, may be powered down.  Add a notifier chain
that allows drivers for those peripherals to be notified
before and after they may be reset.

Signed-off-by: Colin Cross <ccross@android.com>
---
 arch/arm/Kconfig              |    7 ++
 arch/arm/include/asm/cpu_pm.h |   54 ++++++++++++
 arch/arm/kernel/Makefile      |    1 +
 arch/arm/kernel/cpu_pm.c      |  181 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 243 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/cpu_pm.h
 create mode 100644 arch/arm/kernel/cpu_pm.c

Comments

Rob Herring June 13, 2011, 1:57 a.m. UTC | #1
On 06/12/2011 07:43 PM, Colin Cross wrote:
> During some CPU power modes entered during idle, hotplug and
> suspend, peripherals located in the CPU power domain, such as
> the GIC and VFP, may be powered down.  Add a notifier chain
> that allows drivers for those peripherals to be notified
> before and after they may be reset.
>
> Signed-off-by: Colin Cross<ccross@android.com>
> ---
>   arch/arm/Kconfig              |    7 ++
>   arch/arm/include/asm/cpu_pm.h |   54 ++++++++++++
>   arch/arm/kernel/Makefile      |    1 +
>   arch/arm/kernel/cpu_pm.c      |  181 +++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 243 insertions(+), 0 deletions(-)
>   create mode 100644 arch/arm/include/asm/cpu_pm.h
>   create mode 100644 arch/arm/kernel/cpu_pm.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 9adc278..356f266 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -183,6 +183,13 @@ config FIQ
>   config ARCH_MTD_XIP
>   	bool
>
> +config ARCH_USES_CPU_PM
> +       bool
> +
> +config CPU_PM
> +       def_bool y
> +       depends on ARCH_USES_CPU_PM&&  (PM || CPU_IDLE)
> +
>   config VECTORS_BASE
>   	hex
>   	default 0xffff0000 if MMU || CPU_HIGH_VECTOR
> diff --git a/arch/arm/include/asm/cpu_pm.h b/arch/arm/include/asm/cpu_pm.h
> new file mode 100644
> index 0000000..b4bb715
> --- /dev/null
> +++ b/arch/arm/include/asm/cpu_pm.h
> @@ -0,0 +1,54 @@
> +/*
> + * Copyright (C) 2011 Google, Inc.
> + *
> + * Author:
> + *	Colin Cross<ccross@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _ASMARM_CPU_PM_H
> +#define _ASMARM_CPU_PM_H
> +
> +#include<linux/kernel.h>
> +#include<linux/notifier.h>
> +
> +/* Event codes passed as unsigned long val to notifier calls */
> +enum cpu_pm_event {
> +	/* A single cpu is entering a low power state */
> +	CPU_PM_ENTER,
> +
> +	/* A single cpu failed to enter a low power state */
> +	CPU_PM_ENTER_FAILED,
> +
> +	/* A single cpu is exiting a low power state */
> +	CPU_PM_EXIT,
> +
> +	/* A cpu power domain is entering a low power state */
> +	CPU_COMPLEX_PM_ENTER,
> +
> +	/* A cpu power domain failed to enter a low power state */
> +	CPU_COMPLEX_PM_ENTER_FAILED,
> +
> +	/* A cpu power domain is exiting a low power state */
> +	CPU_COMPLEX_PM_EXIT,
> +};
> +
> +int cpu_pm_register_notifier(struct notifier_block *nb);
> +int cpu_pm_unregister_notifier(struct notifier_block *nb);
> +
> +int cpu_pm_enter(void);
> +int cpu_pm_exit(void);
> +
> +int cpu_complex_pm_enter(void);
> +int cpu_complex_pm_exit(void);
> +
> +#endif
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index a5b31af..8b42d58 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_CPU_PJ4)		+= pj4-cp0.o
>   obj-$(CONFIG_IWMMXT)		+= iwmmxt.o
>   obj-$(CONFIG_CPU_HAS_PMU)	+= pmu.o
>   obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
> +obj-$(CONFIG_CPU_PM)		+= cpu_pm.o
>   AFLAGS_iwmmxt.o			:= -Wa,-mcpu=iwmmxt
>
>   ifneq ($(CONFIG_ARCH_EBSA110),y)
> diff --git a/arch/arm/kernel/cpu_pm.c b/arch/arm/kernel/cpu_pm.c
> new file mode 100644
> index 0000000..48a5b53
> --- /dev/null
> +++ b/arch/arm/kernel/cpu_pm.c
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright (C) 2011 Google, Inc.
> + *
> + * Author:
> + *	Colin Cross<ccross@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include<linux/kernel.h>
> +#include<linux/module.h>
> +#include<linux/notifier.h>
> +#include<linux/spinlock.h>
> +
> +#include<asm/cpu_pm.h>
> +
> +/*
> + * When a CPU goes to a low power state that turns off power to the CPU's
> + * power domain, the contents of some blocks (floating point coprocessors,
> + * interrutp controllers, caches, timers) in the same power domain can
> + * be lost.  The cpm_pm notifiers provide a method for platform idle, suspend,
> + * and hotplug implementations to notify the drivers for these blocks that
> + * they may be reset.
> + *
> + * All cpu_pm notifications must be called with interrupts disabled.
> + *
> + * The notifications are split into two classes, CPU notifications and CPU
> + * complex notifications.
> + *
> + * CPU notifications apply to a single CPU, and must be called on the affected
> + * CPU.  They are used to save per-cpu context for affected blocks.
> + *
> + * CPU complex notifications apply to all CPUs in a single power domain. They
> + * are used to save any global context for affected blocks, and must be called
> + * after all the CPUs in the power domain have been notified of the low power
> + * state.
> + *
> + */
> +
> +static DEFINE_RWLOCK(cpu_pm_notifier_lock);
> +static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
> +
> +int cpu_pm_register_notifier(struct notifier_block *nb)
> +{
> +	unsigned long flags;
> +	int ret;
> +
> +	write_lock_irqsave(&cpu_pm_notifier_lock, flags);
> +	ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
> +	write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
> +
> +int cpu_pm_unregister_notifier(struct notifier_block *nb)
> +{
> +	unsigned long flags;
> +	int ret;
> +
> +	write_lock_irqsave(&cpu_pm_notifier_lock, flags);
> +	ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
> +	write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
> +
> +static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
> +{
> +	int ret;
> +
> +	ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
> +		nr_to_call, nr_calls);
> +
> +	return notifier_to_errno(ret);
> +}
> +
> +/**
> + * cpm_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.
> + *
> + * Must be called on the affected cpu with interrupts disabled.  Platform is
> + * responsible for ensuring that cpu_pm_enter is not called twice on the same
> + * cpu before cpu_pm_exit is called.
> + */
> +int cpu_pm_enter(void)
> +{
> +	int nr_calls;
> +	int ret = 0;
> +
> +	read_lock(&cpu_pm_notifier_lock);
> +	ret = cpu_pm_notify(CPU_PM_ENTER, -1,&nr_calls);
> +	if (ret)
> +		cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
> +	read_unlock(&cpu_pm_notifier_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_pm_enter);
> +
> +/**
> + * cpm_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.
> + *
> + * Must be called on the affected cpu with interrupts disabled.
> + */
> +int cpu_pm_exit(void)
> +{
> +	int ret;
> +
> +	read_lock(&cpu_pm_notifier_lock);
> +	ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
> +	read_unlock(&cpu_pm_notifier_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_pm_exit);
> +
> +/**
> + * cpm_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.
> + *
> + * Must be called after cpu_pm_enter has been called on all cpus in the power
> + * domain, and before cpu_pm_exit has been called on any cpu in the power
> + * domain.
> + *
> + * Must be called with interrupts disabled.
> + */
> +int cpu_complex_pm_enter(void)
> +{
> +	int nr_calls;
> +	int ret = 0;
> +
> +	read_lock(&cpu_pm_notifier_lock);
> +	ret = cpu_pm_notify(CPU_COMPLEX_PM_ENTER, -1,&nr_calls);
> +	if (ret)
> +		cpu_pm_notify(CPU_COMPLEX_PM_ENTER_FAILED, nr_calls - 1, NULL);
> +	read_unlock(&cpu_pm_notifier_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_complex_pm_enter);
> +
> +/**
> + * cpm_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.
> + *
> + * Must be called after cpu_pm_enter has been called on all cpus in the power
> + * domain, and before cpu_pm_exit has been called on any cpu in the power
> + * domain.
> + *
> + * Must be called with interrupts disabled.
> + */
> +int cpu_complex_pm_exit(void)
> +{
> +	int ret;
> +
> +	read_lock(&cpu_pm_notifier_lock);
> +	ret = cpu_pm_notify(CPU_COMPLEX_PM_EXIT, -1, NULL);
> +	read_unlock(&cpu_pm_notifier_lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(cpu_complex_pm_exit);

Nothing really ARM specific here, so why not make this generic for all 
architectures?

Rob
Colin Cross June 14, 2011, 6:15 a.m. UTC | #2
On Mon, Jun 13, 2011 at 3:17 PM, Kevin Hilman <khilman@ti.com> wrote:
> Colin Cross <ccross@android.com> writes:
>
>> During some CPU power modes entered during idle, hotplug and
>> suspend, peripherals located in the CPU power domain, such as
>> the GIC and VFP, may be powered down.  Add a notifier chain
>> that allows drivers for those peripherals to be notified
>> before and after they may be reset.
>>
>> Signed-off-by: Colin Cross <ccross@android.com>
>
> This is great, thanks!
>
> I had hacked up something OMAP-specific a while back to do something
> similar, and have been meaning to make it more generic, so this is
> perfect.  Also, if it is moved outside ARM, note that x86_64 has a
> idle_notifier infrastructure that is somewhat similar, and if you're
> motivated, it should probably be converted to this as well.  See
> arch/x86/kernel/process_64.c.

I'll take a look at x86

> Also, for the sake of the comments/changelog, the usefulness of these
> notifiers is not limited to low-power states where power is off and IP
> blocks may be reset.  It could be considered as simply a generic
> notification mechanism for any CPU PM transitions.
>
> On OMAP we have other peripherals (not in the CPU power domain) where we
> need to control their PM transitions in relation to the CPU PM
> transitions so the notifiers are useful for any low-power state
> transition of the CPU(s).  The drivers for these peripherals use runtime
> PM in their CPU PM notifiers to trigger the device PM transitions. (The
> drivers must use the synchronous versions of the runtime PM get/put
> calls with device in pm_runtime_irq_safe() mode.)

Can you give a concrete example of this so I can describe it correctly?

> In this series, I don't see any calls to cpu_[complex_]pm_[enter|exit]().
> Based on that, I assume you prefer to leave it up to platform-specific
> idle/PM code to place these calls.  Or, do you plan to have this
> triggered by generic CPUidle, suspend/resume and/or hotplug code?  At
> least on OMAP, I prefer having the calls in platform-specific code.

I will post the Tegra code that uses this soon.  I expect that the
decision on exactly when to call these functions will be unique to
each platform, so I think it should start in the platform-specific
code.

> I have a minor enhancement request.  The notifier callbacks provide for
> passing a void * through the notifier chain.  Could you add a way for a
> void * to be registered at cpu_pm_register_notifier() time and that
> would be passed through the notifier call chain?  This would allow using
> the same struct notifier_block and callback for multiple instances of
> the same IP, and the instances could be differentiated in the callback
> using the void *.

The void * passed to the notifier comes from the call to
notifier_call_chain(), not from the call to register_notifier().  You
can get the behavior you want by putting the notifier_block inside
your device struct and using container_of on the notifier_bock.

> Also, FWIW I tested this on OMAP3 (Cortex-A8 UP) using
> cpu_pm_enter/exit() in the code path shared between idle and suspend.  I
> successfully triggered PM transitions in non-CPU power-domain
> peripherals, and it worked great.

Great!  Can I get a tested-by?

> Some other minor comments below...
>
> [...]
>
>> diff --git a/arch/arm/kernel/cpu_pm.c b/arch/arm/kernel/cpu_pm.c
>> new file mode 100644
>> index 0000000..48a5b53
>> --- /dev/null
>> +++ b/arch/arm/kernel/cpu_pm.c
>> @@ -0,0 +1,181 @@
>> +/*
>> + * Copyright (C) 2011 Google, Inc.
>> + *
>> + * Author:
>> + *   Colin Cross <ccross@android.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/notifier.h>
>> +#include <linux/spinlock.h>
>> +
>> +#include <asm/cpu_pm.h>
>> +
>> +/*
>> + * When a CPU goes to a low power state that turns off power to the CPU's
>> + * power domain, the contents of some blocks (floating point coprocessors,
>> + * interrutp controllers, caches, timers) in the same power domain can
>
> typo: interrutp

Will fix

>> + * be lost.  The cpm_pm notifiers provide a method for platform idle, suspend,
>> + * and hotplug implementations to notify the drivers for these blocks that
>> + * they may be reset.
>> + *
>> + * All cpu_pm notifications must be called with interrupts disabled.
>> + *
>> + * The notifications are split into two classes, CPU notifications and CPU
>> + * complex notifications.
>> + *
>> + * CPU notifications apply to a single CPU, and must be called on the affected
>> + * CPU.  They are used to save per-cpu context for affected blocks.
>> + *
>> + * CPU complex notifications apply to all CPUs in a single power domain. They
>> + * are used to save any global context for affected blocks, and must be called
>> + * after all the CPUs in the power domain have been notified of the low power
>> + * state.
>> + *
>> + */
>
> Not directly related to this series, but I'm a bit confused on terminology.
>
> I've seen both 'CPU complex' and 'CPU cluster' used in ARM SMP land, but
> haven't seen precise definitions of either.  Does one imply all CPUs,
> and the other imply all CPUs in the same power domain?

'CPU complex' is the terminology I cribbed from some of nVidia's Tegra
patches, but I think 'CPU cluster' is a better term for what I mean -
the group of CPUs that share some external state, like the L2 or GIC
distributor.  In practice, all existing platforms seem to have a
single cluster (as far as Linux is concerned).  The ARM ARM uses
'cluster', but doesn't directly define it, and doesn't use 'CPU
complex' at all.
Kevin Hilman June 16, 2011, 12:12 a.m. UTC | #3
Colin Cross <ccross@android.com> writes:

> On Mon, Jun 13, 2011 at 3:17 PM, Kevin Hilman <khilman@ti.com> wrote:
>> Colin Cross <ccross@android.com> writes:
>>
>>> During some CPU power modes entered during idle, hotplug and
>>> suspend, peripherals located in the CPU power domain, such as
>>> the GIC and VFP, may be powered down.  Add a notifier chain
>>> that allows drivers for those peripherals to be notified
>>> before and after they may be reset.
>>>
>>> Signed-off-by: Colin Cross <ccross@android.com>
>>
>> This is great, thanks!
>>
>> I had hacked up something OMAP-specific a while back to do something
>> similar, and have been meaning to make it more generic, so this is
>> perfect.  Also, if it is moved outside ARM, note that x86_64 has a
>> idle_notifier infrastructure that is somewhat similar, and if you're
>> motivated, it should probably be converted to this as well.  See
>> arch/x86/kernel/process_64.c.
>
> I'll take a look at x86
>
>> Also, for the sake of the comments/changelog, the usefulness of these
>> notifiers is not limited to low-power states where power is off and IP
>> blocks may be reset.  It could be considered as simply a generic
>> notification mechanism for any CPU PM transitions.
>>
>> On OMAP we have other peripherals (not in the CPU power domain) where we
>> need to control their PM transitions in relation to the CPU PM
>> transitions so the notifiers are useful for any low-power state
>> transition of the CPU(s).  The drivers for these peripherals use runtime
>> PM in their CPU PM notifiers to trigger the device PM transitions. (The
>> drivers must use the synchronous versions of the runtime PM get/put
>> calls with device in pm_runtime_irq_safe() mode.)
>
> Can you give a concrete example of this so I can describe it correctly?
>

One simple example is a clock controlling a thermal sensor.  The sensor
(and thus the functional clock) should be active whenever the CPU is
running, so the idle transition (clock gating) of this IP block should
be coordinated with any idle transitions of the CPU cluster.

Another example is any IP block that is not capable of waking up the CPU
(or $DEITY forbid, where said wakeups are broken.)  A CPU_PM_EXIT
notifier would be used to check for pending activity and take action.

>> In this series, I don't see any calls to cpu_[complex_]pm_[enter|exit]().
>> Based on that, I assume you prefer to leave it up to platform-specific
>> idle/PM code to place these calls.  Or, do you plan to have this
>> triggered by generic CPUidle, suspend/resume and/or hotplug code?  At
>> least on OMAP, I prefer having the calls in platform-specific code.
>
> I will post the Tegra code that uses this soon.  I expect that the
> decision on exactly when to call these functions will be unique to
> each platform, so I think it should start in the platform-specific
> code.

Agreed.

>> I have a minor enhancement request.  The notifier callbacks provide for
>> passing a void * through the notifier chain.  Could you add a way for a
>> void * to be registered at cpu_pm_register_notifier() time and that
>> would be passed through the notifier call chain?  This would allow using
>> the same struct notifier_block and callback for multiple instances of
>> the same IP, and the instances could be differentiated in the callback
>> using the void *.
>
> The void * passed to the notifier comes from the call to
> notifier_call_chain(), not from the call to register_notifier().  

I see now.

> You can get the behavior you want by putting the notifier_block inside
> your device struct and using container_of on the notifier_bock.

Yeah, that's the way I did it, but it requires a separate 'struct
notifier_block' for each device instance.  It's only a minor issue, but
being able to pass in the 'void *' at registration time would allow a
single struct notifier_block' for all instances of the device.

>> Also, FWIW I tested this on OMAP3 (Cortex-A8 UP) using
>> cpu_pm_enter/exit() in the code path shared between idle and suspend.  I
>> successfully triggered PM transitions in non-CPU power-domain
>> peripherals, and it worked great.
>
> Great!  Can I get a tested-by?

Sure, meant to add it the first time.

Tested-by: Kevin Hilman <khilman@ti.com>

Kevin
diff mbox

Patch

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9adc278..356f266 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -183,6 +183,13 @@  config FIQ
 config ARCH_MTD_XIP
 	bool
 
+config ARCH_USES_CPU_PM
+       bool
+
+config CPU_PM
+       def_bool y
+       depends on ARCH_USES_CPU_PM && (PM || CPU_IDLE)
+
 config VECTORS_BASE
 	hex
 	default 0xffff0000 if MMU || CPU_HIGH_VECTOR
diff --git a/arch/arm/include/asm/cpu_pm.h b/arch/arm/include/asm/cpu_pm.h
new file mode 100644
index 0000000..b4bb715
--- /dev/null
+++ b/arch/arm/include/asm/cpu_pm.h
@@ -0,0 +1,54 @@ 
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ASMARM_CPU_PM_H
+#define _ASMARM_CPU_PM_H
+
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+
+/* Event codes passed as unsigned long val to notifier calls */
+enum cpu_pm_event {
+	/* A single cpu is entering a low power state */
+	CPU_PM_ENTER,
+
+	/* A single cpu failed to enter a low power state */
+	CPU_PM_ENTER_FAILED,
+
+	/* A single cpu is exiting a low power state */
+	CPU_PM_EXIT,
+
+	/* A cpu power domain is entering a low power state */
+	CPU_COMPLEX_PM_ENTER,
+
+	/* A cpu power domain failed to enter a low power state */
+	CPU_COMPLEX_PM_ENTER_FAILED,
+
+	/* A cpu power domain is exiting a low power state */
+	CPU_COMPLEX_PM_EXIT,
+};
+
+int cpu_pm_register_notifier(struct notifier_block *nb);
+int cpu_pm_unregister_notifier(struct notifier_block *nb);
+
+int cpu_pm_enter(void);
+int cpu_pm_exit(void);
+
+int cpu_complex_pm_enter(void);
+int cpu_complex_pm_exit(void);
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a5b31af..8b42d58 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -60,6 +60,7 @@  obj-$(CONFIG_CPU_PJ4)		+= pj4-cp0.o
 obj-$(CONFIG_IWMMXT)		+= iwmmxt.o
 obj-$(CONFIG_CPU_HAS_PMU)	+= pmu.o
 obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
+obj-$(CONFIG_CPU_PM)		+= cpu_pm.o
 AFLAGS_iwmmxt.o			:= -Wa,-mcpu=iwmmxt
 
 ifneq ($(CONFIG_ARCH_EBSA110),y)
diff --git a/arch/arm/kernel/cpu_pm.c b/arch/arm/kernel/cpu_pm.c
new file mode 100644
index 0000000..48a5b53
--- /dev/null
+++ b/arch/arm/kernel/cpu_pm.c
@@ -0,0 +1,181 @@ 
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/spinlock.h>
+
+#include <asm/cpu_pm.h>
+
+/*
+ * When a CPU goes to a low power state that turns off power to the CPU's
+ * power domain, the contents of some blocks (floating point coprocessors,
+ * interrutp controllers, caches, timers) in the same power domain can
+ * be lost.  The cpm_pm notifiers provide a method for platform idle, suspend,
+ * and hotplug implementations to notify the drivers for these blocks that
+ * they may be reset.
+ *
+ * All cpu_pm notifications must be called with interrupts disabled.
+ *
+ * The notifications are split into two classes, CPU notifications and CPU
+ * complex notifications.
+ *
+ * CPU notifications apply to a single CPU, and must be called on the affected
+ * CPU.  They are used to save per-cpu context for affected blocks.
+ *
+ * CPU complex notifications apply to all CPUs in a single power domain. They
+ * are used to save any global context for affected blocks, and must be called
+ * after all the CPUs in the power domain have been notified of the low power
+ * state.
+ *
+ */
+
+static DEFINE_RWLOCK(cpu_pm_notifier_lock);
+static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
+
+int cpu_pm_register_notifier(struct notifier_block *nb)
+{
+	unsigned long flags;
+	int ret;
+
+	write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+	ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
+	write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
+
+int cpu_pm_unregister_notifier(struct notifier_block *nb)
+{
+	unsigned long flags;
+	int ret;
+
+	write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+	ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
+	write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
+
+static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
+{
+	int ret;
+
+	ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
+		nr_to_call, nr_calls);
+
+	return notifier_to_errno(ret);
+}
+
+/**
+ * cpm_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.
+ *
+ * Must be called on the affected cpu with interrupts disabled.  Platform is
+ * responsible for ensuring that cpu_pm_enter is not called twice on the same
+ * cpu before cpu_pm_exit is called.
+ */
+int cpu_pm_enter(void)
+{
+	int nr_calls;
+	int ret = 0;
+
+	read_lock(&cpu_pm_notifier_lock);
+	ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
+	if (ret)
+		cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
+	read_unlock(&cpu_pm_notifier_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_enter);
+
+/**
+ * cpm_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.
+ *
+ * Must be called on the affected cpu with interrupts disabled.
+ */
+int cpu_pm_exit(void)
+{
+	int ret;
+
+	read_lock(&cpu_pm_notifier_lock);
+	ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
+	read_unlock(&cpu_pm_notifier_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_exit);
+
+/**
+ * cpm_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.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_complex_pm_enter(void)
+{
+	int nr_calls;
+	int ret = 0;
+
+	read_lock(&cpu_pm_notifier_lock);
+	ret = cpu_pm_notify(CPU_COMPLEX_PM_ENTER, -1, &nr_calls);
+	if (ret)
+		cpu_pm_notify(CPU_COMPLEX_PM_ENTER_FAILED, nr_calls - 1, NULL);
+	read_unlock(&cpu_pm_notifier_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_complex_pm_enter);
+
+/**
+ * cpm_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.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_complex_pm_exit(void)
+{
+	int ret;
+
+	read_lock(&cpu_pm_notifier_lock);
+	ret = cpu_pm_notify(CPU_COMPLEX_PM_EXIT, -1, NULL);
+	read_unlock(&cpu_pm_notifier_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_complex_pm_exit);