aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/cpuidle.h29
-rw-r--r--arch/arm/kernel/Makefile2
-rw-r--r--arch/arm/kernel/cpuidle.c21
-rw-r--r--drivers/cpuidle/cpuidle.c66
-rw-r--r--include/linux/cpuidle.h13
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
5extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
6 struct cpuidle_driver *drv, int index);
7#else
8static 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
22obj-$(CONFIG_LEDS) += leds.o 22obj-$(CONFIG_LEDS) += leds.o
23obj-$(CONFIG_OC_ETM) += etm.o 23obj-$(CONFIG_OC_ETM) += etm.o
24 24obj-$(CONFIG_CPU_IDLE) += cpuidle.o
25obj-$(CONFIG_ISA_DMA_API) += dma.o 25obj-$(CONFIG_ISA_DMA_API) += dma.o
26obj-$(CONFIG_ARCH_ACORN) += ecard.o 26obj-$(CONFIG_ARCH_ACORN) += ecard.o
27obj-$(CONFIG_FIQ) += fiq.o fiqasm.o 27obj-$(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
15int 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
54static int __cpuidle_register_device(struct cpuidle_device *dev); 54static int __cpuidle_register_device(struct cpuidle_device *dev);
55 55
56static 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
63static 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
69typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
70 struct cpuidle_driver *drv, int index);
71
72static 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
165EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); 182EXPORT_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 */
190int 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
168static int poll_idle(struct cpuidle_device *dev, 216static 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) {}
212int cpuidle_enable_device(struct cpuidle_device *dev) 260int 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);
140extern void cpuidle_resume_and_unlock(void); 143extern void cpuidle_resume_and_unlock(void);
141extern int cpuidle_enable_device(struct cpuidle_device *dev); 144extern int cpuidle_enable_device(struct cpuidle_device *dev);
142extern void cpuidle_disable_device(struct cpuidle_device *dev); 145extern void cpuidle_disable_device(struct cpuidle_device *dev);
143 146extern 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
145static inline void disable_cpuidle(void) { } 151static inline void disable_cpuidle(void) { }
146static inline int cpuidle_idle_call(void) { return -ENODEV; } 152static inline int cpuidle_idle_call(void) { return -ENODEV; }
@@ -157,6 +163,11 @@ static inline void cpuidle_resume_and_unlock(void) { }
157static inline int cpuidle_enable_device(struct cpuidle_device *dev) 163static inline int cpuidle_enable_device(struct cpuidle_device *dev)
158{return -ENODEV; } 164{return -ENODEV; }
159static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } 165static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
166static 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