diff options
Diffstat (limited to 'arch/arm/mach-kirkwood/cpuidle.c')
-rw-r--r-- | arch/arm/mach-kirkwood/cpuidle.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/arch/arm/mach-kirkwood/cpuidle.c b/arch/arm/mach-kirkwood/cpuidle.c new file mode 100644 index 000000000000..f68d33f1f396 --- /dev/null +++ b/arch/arm/mach-kirkwood/cpuidle.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-kirkwood/cpuidle.c | ||
3 | * | ||
4 | * CPU idle Marvell Kirkwood SoCs | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | * | ||
10 | * The cpu idle uses wait-for-interrupt and DDR self refresh in order | ||
11 | * to implement two idle states - | ||
12 | * #1 wait-for-interrupt | ||
13 | * #2 wait-for-interrupt and DDR self refresh | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/cpuidle.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <asm/proc-fns.h> | ||
22 | #include <mach/kirkwood.h> | ||
23 | |||
24 | #define KIRKWOOD_MAX_STATES 2 | ||
25 | |||
26 | static struct cpuidle_driver kirkwood_idle_driver = { | ||
27 | .name = "kirkwood_idle", | ||
28 | .owner = THIS_MODULE, | ||
29 | }; | ||
30 | |||
31 | static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device); | ||
32 | |||
33 | /* Actual code that puts the SoC in different idle states */ | ||
34 | static int kirkwood_enter_idle(struct cpuidle_device *dev, | ||
35 | struct cpuidle_state *state) | ||
36 | { | ||
37 | struct timeval before, after; | ||
38 | int idle_time; | ||
39 | |||
40 | local_irq_disable(); | ||
41 | do_gettimeofday(&before); | ||
42 | if (state == &dev->states[0]) | ||
43 | /* Wait for interrupt state */ | ||
44 | cpu_do_idle(); | ||
45 | else if (state == &dev->states[1]) { | ||
46 | /* | ||
47 | * Following write will put DDR in self refresh. | ||
48 | * Note that we have 256 cycles before DDR puts it | ||
49 | * self in self-refresh, so the wait-for-interrupt | ||
50 | * call afterwards won't get the DDR from self refresh | ||
51 | * mode. | ||
52 | */ | ||
53 | writel(0x7, DDR_OPERATION_BASE); | ||
54 | cpu_do_idle(); | ||
55 | } | ||
56 | do_gettimeofday(&after); | ||
57 | local_irq_enable(); | ||
58 | idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC + | ||
59 | (after.tv_usec - before.tv_usec); | ||
60 | return idle_time; | ||
61 | } | ||
62 | |||
63 | /* Initialize CPU idle by registering the idle states */ | ||
64 | static int kirkwood_init_cpuidle(void) | ||
65 | { | ||
66 | struct cpuidle_device *device; | ||
67 | |||
68 | cpuidle_register_driver(&kirkwood_idle_driver); | ||
69 | |||
70 | device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id()); | ||
71 | device->state_count = KIRKWOOD_MAX_STATES; | ||
72 | |||
73 | /* Wait for interrupt state */ | ||
74 | device->states[0].enter = kirkwood_enter_idle; | ||
75 | device->states[0].exit_latency = 1; | ||
76 | device->states[0].target_residency = 10000; | ||
77 | device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; | ||
78 | strcpy(device->states[0].name, "WFI"); | ||
79 | strcpy(device->states[0].desc, "Wait for interrupt"); | ||
80 | |||
81 | /* Wait for interrupt and DDR self refresh state */ | ||
82 | device->states[1].enter = kirkwood_enter_idle; | ||
83 | device->states[1].exit_latency = 10; | ||
84 | device->states[1].target_residency = 10000; | ||
85 | device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; | ||
86 | strcpy(device->states[1].name, "DDR SR"); | ||
87 | strcpy(device->states[1].desc, "WFI and DDR Self Refresh"); | ||
88 | |||
89 | if (cpuidle_register_device(device)) { | ||
90 | printk(KERN_ERR "kirkwood_init_cpuidle: Failed registering\n"); | ||
91 | return -EIO; | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | device_initcall(kirkwood_init_cpuidle); | ||