diff options
-rw-r--r-- | arch/arm/include/asm/cpuidle.h | 29 | ||||
-rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kernel/cpuidle.c | 21 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 66 | ||||
-rw-r--r-- | include/linux/cpuidle.h | 13 |
5 files changed, 122 insertions, 9 deletions
diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h new file mode 100644 index 000000000000..2fca60ab513a --- /dev/null +++ b/arch/arm/include/asm/cpuidle.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef __ASM_ARM_CPUIDLE_H | ||
2 | #define __ASM_ARM_CPUIDLE_H | ||
3 | |||
4 | #ifdef CONFIG_CPU_IDLE | ||
5 | extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev, | ||
6 | struct cpuidle_driver *drv, int index); | ||
7 | #else | ||
8 | static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev, | ||
9 | struct cpuidle_driver *drv, int index) { return -ENODEV; } | ||
10 | #endif | ||
11 | |||
12 | /* Common ARM WFI state */ | ||
13 | #define ARM_CPUIDLE_WFI_STATE_PWR(p) {\ | ||
14 | .enter = arm_cpuidle_simple_enter,\ | ||
15 | .exit_latency = 1,\ | ||
16 | .target_residency = 1,\ | ||
17 | .power_usage = p,\ | ||
18 | .flags = CPUIDLE_FLAG_TIME_VALID,\ | ||
19 | .name = "WFI",\ | ||
20 | .desc = "ARM WFI",\ | ||
21 | } | ||
22 | |||
23 | /* | ||
24 | * in case power_specified == 1, give a default WFI power value needed | ||
25 | * by some governors | ||
26 | */ | ||
27 | #define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX) | ||
28 | |||
29 | #endif | ||
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 43b740d0e374..940c27fde498 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -21,7 +21,7 @@ obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += compat.o | |||
21 | 21 | ||
22 | obj-$(CONFIG_LEDS) += leds.o | 22 | obj-$(CONFIG_LEDS) += leds.o |
23 | obj-$(CONFIG_OC_ETM) += etm.o | 23 | obj-$(CONFIG_OC_ETM) += etm.o |
24 | 24 | obj-$(CONFIG_CPU_IDLE) += cpuidle.o | |
25 | obj-$(CONFIG_ISA_DMA_API) += dma.o | 25 | obj-$(CONFIG_ISA_DMA_API) += dma.o |
26 | obj-$(CONFIG_ARCH_ACORN) += ecard.o | 26 | obj-$(CONFIG_ARCH_ACORN) += ecard.o |
27 | obj-$(CONFIG_FIQ) += fiq.o fiqasm.o | 27 | obj-$(CONFIG_FIQ) += fiq.o fiqasm.o |
diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c new file mode 100644 index 000000000000..89545f6c8403 --- /dev/null +++ b/arch/arm/kernel/cpuidle.c | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Linaro Ltd. | ||
3 | * | ||
4 | * The code contained herein is licensed under the GNU General Public | ||
5 | * License. You may obtain a copy of the GNU General Public License | ||
6 | * Version 2 or later at the following locations: | ||
7 | * | ||
8 | * http://www.opensource.org/licenses/gpl-license.html | ||
9 | * http://www.gnu.org/copyleft/gpl.html | ||
10 | */ | ||
11 | |||
12 | #include <linux/cpuidle.h> | ||
13 | #include <asm/proc-fns.h> | ||
14 | |||
15 | int arm_cpuidle_simple_enter(struct cpuidle_device *dev, | ||
16 | struct cpuidle_driver *drv, int index) | ||
17 | { | ||
18 | cpu_do_idle(); | ||
19 | |||
20 | return index; | ||
21 | } | ||
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 59f4261c753a..4869b5500234 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -53,6 +53,24 @@ static void cpuidle_kick_cpus(void) {} | |||
53 | 53 | ||
54 | static int __cpuidle_register_device(struct cpuidle_device *dev); | 54 | static int __cpuidle_register_device(struct cpuidle_device *dev); |
55 | 55 | ||
56 | static inline int cpuidle_enter(struct cpuidle_device *dev, | ||
57 | struct cpuidle_driver *drv, int index) | ||
58 | { | ||
59 | struct cpuidle_state *target_state = &drv->states[index]; | ||
60 | return target_state->enter(dev, drv, index); | ||
61 | } | ||
62 | |||
63 | static inline int cpuidle_enter_tk(struct cpuidle_device *dev, | ||
64 | struct cpuidle_driver *drv, int index) | ||
65 | { | ||
66 | return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter); | ||
67 | } | ||
68 | |||
69 | typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev, | ||
70 | struct cpuidle_driver *drv, int index); | ||
71 | |||
72 | static cpuidle_enter_t cpuidle_enter_ops; | ||
73 | |||
56 | /** | 74 | /** |
57 | * cpuidle_idle_call - the main idle loop | 75 | * cpuidle_idle_call - the main idle loop |
58 | * | 76 | * |
@@ -63,7 +81,6 @@ int cpuidle_idle_call(void) | |||
63 | { | 81 | { |
64 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | 82 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); |
65 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 83 | struct cpuidle_driver *drv = cpuidle_get_driver(); |
66 | struct cpuidle_state *target_state; | ||
67 | int next_state, entered_state; | 84 | int next_state, entered_state; |
68 | 85 | ||
69 | if (off) | 86 | if (off) |
@@ -92,12 +109,10 @@ int cpuidle_idle_call(void) | |||
92 | return 0; | 109 | return 0; |
93 | } | 110 | } |
94 | 111 | ||
95 | target_state = &drv->states[next_state]; | ||
96 | |||
97 | trace_power_start(POWER_CSTATE, next_state, dev->cpu); | 112 | trace_power_start(POWER_CSTATE, next_state, dev->cpu); |
98 | trace_cpu_idle(next_state, dev->cpu); | 113 | trace_cpu_idle(next_state, dev->cpu); |
99 | 114 | ||
100 | entered_state = target_state->enter(dev, drv, next_state); | 115 | entered_state = cpuidle_enter_ops(dev, drv, next_state); |
101 | 116 | ||
102 | trace_power_end(dev->cpu); | 117 | trace_power_end(dev->cpu); |
103 | trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu); | 118 | trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu); |
@@ -110,6 +125,8 @@ int cpuidle_idle_call(void) | |||
110 | dev->states_usage[entered_state].time += | 125 | dev->states_usage[entered_state].time += |
111 | (unsigned long long)dev->last_residency; | 126 | (unsigned long long)dev->last_residency; |
112 | dev->states_usage[entered_state].usage++; | 127 | dev->states_usage[entered_state].usage++; |
128 | } else { | ||
129 | dev->last_residency = 0; | ||
113 | } | 130 | } |
114 | 131 | ||
115 | /* give the governor an opportunity to reflect on the outcome */ | 132 | /* give the governor an opportunity to reflect on the outcome */ |
@@ -164,6 +181,37 @@ void cpuidle_resume_and_unlock(void) | |||
164 | 181 | ||
165 | EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); | 182 | EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); |
166 | 183 | ||
184 | /** | ||
185 | * cpuidle_wrap_enter - performs timekeeping and irqen around enter function | ||
186 | * @dev: pointer to a valid cpuidle_device object | ||
187 | * @drv: pointer to a valid cpuidle_driver object | ||
188 | * @index: index of the target cpuidle state. | ||
189 | */ | ||
190 | int cpuidle_wrap_enter(struct cpuidle_device *dev, | ||
191 | struct cpuidle_driver *drv, int index, | ||
192 | int (*enter)(struct cpuidle_device *dev, | ||
193 | struct cpuidle_driver *drv, int index)) | ||
194 | { | ||
195 | ktime_t time_start, time_end; | ||
196 | s64 diff; | ||
197 | |||
198 | time_start = ktime_get(); | ||
199 | |||
200 | index = enter(dev, drv, index); | ||
201 | |||
202 | time_end = ktime_get(); | ||
203 | |||
204 | local_irq_enable(); | ||
205 | |||
206 | diff = ktime_to_us(ktime_sub(time_end, time_start)); | ||
207 | if (diff > INT_MAX) | ||
208 | diff = INT_MAX; | ||
209 | |||
210 | dev->last_residency = (int) diff; | ||
211 | |||
212 | return index; | ||
213 | } | ||
214 | |||
167 | #ifdef CONFIG_ARCH_HAS_CPU_RELAX | 215 | #ifdef CONFIG_ARCH_HAS_CPU_RELAX |
168 | static int poll_idle(struct cpuidle_device *dev, | 216 | static int poll_idle(struct cpuidle_device *dev, |
169 | struct cpuidle_driver *drv, int index) | 217 | struct cpuidle_driver *drv, int index) |
@@ -212,10 +260,11 @@ static void poll_idle_init(struct cpuidle_driver *drv) {} | |||
212 | int cpuidle_enable_device(struct cpuidle_device *dev) | 260 | int cpuidle_enable_device(struct cpuidle_device *dev) |
213 | { | 261 | { |
214 | int ret, i; | 262 | int ret, i; |
263 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
215 | 264 | ||
216 | if (dev->enabled) | 265 | if (dev->enabled) |
217 | return 0; | 266 | return 0; |
218 | if (!cpuidle_get_driver() || !cpuidle_curr_governor) | 267 | if (!drv || !cpuidle_curr_governor) |
219 | return -EIO; | 268 | return -EIO; |
220 | if (!dev->state_count) | 269 | if (!dev->state_count) |
221 | return -EINVAL; | 270 | return -EINVAL; |
@@ -226,13 +275,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
226 | return ret; | 275 | return ret; |
227 | } | 276 | } |
228 | 277 | ||
229 | poll_idle_init(cpuidle_get_driver()); | 278 | cpuidle_enter_ops = drv->en_core_tk_irqen ? |
279 | cpuidle_enter_tk : cpuidle_enter; | ||
280 | |||
281 | poll_idle_init(drv); | ||
230 | 282 | ||
231 | if ((ret = cpuidle_add_state_sysfs(dev))) | 283 | if ((ret = cpuidle_add_state_sysfs(dev))) |
232 | return ret; | 284 | return ret; |
233 | 285 | ||
234 | if (cpuidle_curr_governor->enable && | 286 | if (cpuidle_curr_governor->enable && |
235 | (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev))) | 287 | (ret = cpuidle_curr_governor->enable(drv, dev))) |
236 | goto fail_sysfs; | 288 | goto fail_sysfs; |
237 | 289 | ||
238 | for (i = 0; i < dev->state_count; i++) { | 290 | for (i = 0; i < dev->state_count; i++) { |
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 712abcc205ae..927db28a2a4c 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include <linux/kobject.h> | 16 | #include <linux/kobject.h> |
17 | #include <linux/completion.h> | 17 | #include <linux/completion.h> |
18 | #include <linux/hrtimer.h> | ||
18 | 19 | ||
19 | #define CPUIDLE_STATE_MAX 8 | 20 | #define CPUIDLE_STATE_MAX 8 |
20 | #define CPUIDLE_NAME_LEN 16 | 21 | #define CPUIDLE_NAME_LEN 16 |
@@ -122,6 +123,8 @@ struct cpuidle_driver { | |||
122 | struct module *owner; | 123 | struct module *owner; |
123 | 124 | ||
124 | unsigned int power_specified:1; | 125 | unsigned int power_specified:1; |
126 | /* set to 1 to use the core cpuidle time keeping (for all states). */ | ||
127 | unsigned int en_core_tk_irqen:1; | ||
125 | struct cpuidle_state states[CPUIDLE_STATE_MAX]; | 128 | struct cpuidle_state states[CPUIDLE_STATE_MAX]; |
126 | int state_count; | 129 | int state_count; |
127 | int safe_state_index; | 130 | int safe_state_index; |
@@ -140,7 +143,10 @@ extern void cpuidle_pause_and_lock(void); | |||
140 | extern void cpuidle_resume_and_unlock(void); | 143 | extern void cpuidle_resume_and_unlock(void); |
141 | extern int cpuidle_enable_device(struct cpuidle_device *dev); | 144 | extern int cpuidle_enable_device(struct cpuidle_device *dev); |
142 | extern void cpuidle_disable_device(struct cpuidle_device *dev); | 145 | extern void cpuidle_disable_device(struct cpuidle_device *dev); |
143 | 146 | extern int cpuidle_wrap_enter(struct cpuidle_device *dev, | |
147 | struct cpuidle_driver *drv, int index, | ||
148 | int (*enter)(struct cpuidle_device *dev, | ||
149 | struct cpuidle_driver *drv, int index)); | ||
144 | #else | 150 | #else |
145 | static inline void disable_cpuidle(void) { } | 151 | static inline void disable_cpuidle(void) { } |
146 | static inline int cpuidle_idle_call(void) { return -ENODEV; } | 152 | static inline int cpuidle_idle_call(void) { return -ENODEV; } |
@@ -157,6 +163,11 @@ static inline void cpuidle_resume_and_unlock(void) { } | |||
157 | static inline int cpuidle_enable_device(struct cpuidle_device *dev) | 163 | static inline int cpuidle_enable_device(struct cpuidle_device *dev) |
158 | {return -ENODEV; } | 164 | {return -ENODEV; } |
159 | static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } | 165 | static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } |
166 | static inline int cpuidle_wrap_enter(struct cpuidle_device *dev, | ||
167 | struct cpuidle_driver *drv, int index, | ||
168 | int (*enter)(struct cpuidle_device *dev, | ||
169 | struct cpuidle_driver *drv, int index)) | ||
170 | { return -ENODEV; } | ||
160 | 171 | ||
161 | #endif | 172 | #endif |
162 | 173 | ||