aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Lee <rob.lee@linaro.org>2012-03-20 16:22:42 -0400
committerLen Brown <len.brown@intel.com>2012-03-21 01:59:40 -0400
commite1689795a784a7c41ac4cf9032794986b095a133 (patch)
tree19619c24579875e344af337c86c6a604425627b5
parentc16fa4f2ad19908a47c63d8fa436a1178438c7e7 (diff)
cpuidle: Add common time keeping and irq enabling
Make necessary changes to implement time keeping and irq enabling in the core cpuidle code. This will allow the removal of these functionalities from various platform cpuidle implementations whose timekeeping and irq enabling follows the form in this common code. Signed-off-by: Robert Lee <rob.lee@linaro.org> Tested-by: Jean Pihet <j-pihet@ti.com> Tested-by: Amit Daniel <amit.kachhap@linaro.org> Tested-by: Robert Lee <rob.lee@linaro.org> Reviewed-by: Kevin Hilman <khilman@ti.com> Reviewed-by: Daniel Lezcano <daniel.lezcano@linaro.org> Reviewed-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com> Acked-by: Jean Pihet <j-pihet@ti.com> Signed-off-by: Len Brown <len.brown@intel.com>
-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