diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 08:12:43 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 08:12:43 -0800 |
commit | 79c9601c2e0dbbe69895d302de4d19f3a31fbd30 (patch) | |
tree | 78d4be2df851b2b4106adcfd736622a90cecf9e9 /arch/arm/mach-at91/cpuidle.c | |
parent | 41440ffe21f29bdb985cab76b2d0b06d83e63b19 (diff) | |
parent | 3d14b5beba35250c548d3851a2b84fce742d8311 (diff) |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (272 commits)
Fix soc_common PCMCIA configuration
ARM: 5827/1: SA1100: h3100/h3600: emit messages on failed gpio_request
ARM: 5826/1: SA1100: h3100/h3600: always build htc-egpio driver
ARM: 5825/1: SA1100: h3600: update defconfig
ARM: 5824/1: SA1100: reuse h3600 PCMCIA driver on h3100
ARM: 5823/1: SA1100: h3100/h3600: add support for gpio-keys
ARM: 5822/1: SA1100: h3100/h3600: clean up #includes
ARM: 5821/1: SA1100: h3100/h3600: revise copyright boilerplates
ARM: 5820/1: SA1100: h3100/h3600: split h3600.c
ARM: 5819/1: SA1100: h3100/h3600: merge h3600.h and h3600_gpio.h into h3xxx.h
ARM: 5818/1: SA1100: h3100/h3600: drop old GPIO definitions
ARM: 5817/1: SA1100: h3100/h3600: configure all unused gpios as inputs
ARM: 5816/1: SA1100: h3600: remove IRQ_GPIO_* definitions
ARM: 5815/1: SA1100: h3100/h3600: remove now unused assign_h3600_egpio handlers
ARM: 5814/1: SA1100: h3100/h3600: convert all users of assign_h3600_egpio to gpiolib
ARM: 5813/1: SA1100: h3100/h3600: add htc-egpio driver
ARM: 5812/1: SA1100: h3100/h3600: separate machine-specific LCD helpers
ARM: 5811/2: pcmcia: convert sa1100_h3600 driver to gpiolib
ARM: 5799/1: SA1100: h3600: stop setting direction for LCD pins
ARM: 5798/1: SA1100: h3600: remove unused cruft from h3600.h
...
Diffstat (limited to 'arch/arm/mach-at91/cpuidle.c')
-rw-r--r-- | arch/arm/mach-at91/cpuidle.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c new file mode 100644 index 00000000000..1cfeac1483d --- /dev/null +++ b/arch/arm/mach-at91/cpuidle.c @@ -0,0 +1,94 @@ +/* + * based on arch/arm/mach-kirkwood/cpuidle.c + * + * CPU idle support for AT91 SoC + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The cpu idle uses wait-for-interrupt and RAM self refresh in order + * to implement two idle states - + * #1 wait-for-interrupt + * #2 wait-for-interrupt and RAM self refresh + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/cpuidle.h> +#include <asm/proc-fns.h> +#include <linux/io.h> + +#include "pm.h" + +#define AT91_MAX_STATES 2 + +static DEFINE_PER_CPU(struct cpuidle_device, at91_cpuidle_device); + +static struct cpuidle_driver at91_idle_driver = { + .name = "at91_idle", + .owner = THIS_MODULE, +}; + +/* Actual code that puts the SoC in different idle states */ +static int at91_enter_idle(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct timeval before, after; + int idle_time; + u32 saved_lpr; + + local_irq_disable(); + do_gettimeofday(&before); + if (state == &dev->states[0]) + /* Wait for interrupt state */ + cpu_do_idle(); + else if (state == &dev->states[1]) { + asm("b 1f; .align 5; 1:"); + asm("mcr p15, 0, r0, c7, c10, 4"); /* drain write buffer */ + saved_lpr = sdram_selfrefresh_enable(); + cpu_do_idle(); + sdram_selfrefresh_disable(saved_lpr); + } + do_gettimeofday(&after); + local_irq_enable(); + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC + + (after.tv_usec - before.tv_usec); + return idle_time; +} + +/* Initialize CPU idle by registering the idle states */ +static int at91_init_cpuidle(void) +{ + struct cpuidle_device *device; + + cpuidle_register_driver(&at91_idle_driver); + + device = &per_cpu(at91_cpuidle_device, smp_processor_id()); + device->state_count = AT91_MAX_STATES; + + /* Wait for interrupt state */ + device->states[0].enter = at91_enter_idle; + device->states[0].exit_latency = 1; + device->states[0].target_residency = 10000; + device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(device->states[0].name, "WFI"); + strcpy(device->states[0].desc, "Wait for interrupt"); + + /* Wait for interrupt and RAM self refresh state */ + device->states[1].enter = at91_enter_idle; + device->states[1].exit_latency = 10; + device->states[1].target_residency = 10000; + device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; + strcpy(device->states[1].name, "RAM_SR"); + strcpy(device->states[1].desc, "WFI and RAM Self Refresh"); + + if (cpuidle_register_device(device)) { + printk(KERN_ERR "at91_init_cpuidle: Failed registering\n"); + return -EIO; + } + return 0; +} + +device_initcall(at91_init_cpuidle); |