diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 97 |
1 files changed, 89 insertions, 8 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 6588f43017bd..87411cebc577 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -53,6 +53,52 @@ 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 | |||
74 | /** | ||
75 | * cpuidle_play_dead - cpu off-lining | ||
76 | * | ||
77 | * Only returns in case of an error | ||
78 | */ | ||
79 | int cpuidle_play_dead(void) | ||
80 | { | ||
81 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | ||
82 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
83 | int i, dead_state = -1; | ||
84 | int power_usage = -1; | ||
85 | |||
86 | /* Find lowest-power state that supports long-term idle */ | ||
87 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { | ||
88 | struct cpuidle_state *s = &drv->states[i]; | ||
89 | |||
90 | if (s->power_usage < power_usage && s->enter_dead) { | ||
91 | power_usage = s->power_usage; | ||
92 | dead_state = i; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | if (dead_state != -1) | ||
97 | return drv->states[dead_state].enter_dead(dev, dead_state); | ||
98 | |||
99 | return -ENODEV; | ||
100 | } | ||
101 | |||
56 | /** | 102 | /** |
57 | * cpuidle_idle_call - the main idle loop | 103 | * cpuidle_idle_call - the main idle loop |
58 | * | 104 | * |
@@ -63,7 +109,6 @@ int cpuidle_idle_call(void) | |||
63 | { | 109 | { |
64 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | 110 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); |
65 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 111 | struct cpuidle_driver *drv = cpuidle_get_driver(); |
66 | struct cpuidle_state *target_state; | ||
67 | int next_state, entered_state; | 112 | int next_state, entered_state; |
68 | 113 | ||
69 | if (off) | 114 | if (off) |
@@ -92,12 +137,10 @@ int cpuidle_idle_call(void) | |||
92 | return 0; | 137 | return 0; |
93 | } | 138 | } |
94 | 139 | ||
95 | target_state = &drv->states[next_state]; | ||
96 | |||
97 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); | 140 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); |
98 | trace_cpu_idle_rcuidle(next_state, dev->cpu); | 141 | trace_cpu_idle_rcuidle(next_state, dev->cpu); |
99 | 142 | ||
100 | entered_state = target_state->enter(dev, drv, next_state); | 143 | entered_state = cpuidle_enter_ops(dev, drv, next_state); |
101 | 144 | ||
102 | trace_power_end_rcuidle(dev->cpu); | 145 | trace_power_end_rcuidle(dev->cpu); |
103 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); | 146 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); |
@@ -110,6 +153,8 @@ int cpuidle_idle_call(void) | |||
110 | dev->states_usage[entered_state].time += | 153 | dev->states_usage[entered_state].time += |
111 | (unsigned long long)dev->last_residency; | 154 | (unsigned long long)dev->last_residency; |
112 | dev->states_usage[entered_state].usage++; | 155 | dev->states_usage[entered_state].usage++; |
156 | } else { | ||
157 | dev->last_residency = 0; | ||
113 | } | 158 | } |
114 | 159 | ||
115 | /* give the governor an opportunity to reflect on the outcome */ | 160 | /* give the governor an opportunity to reflect on the outcome */ |
@@ -164,6 +209,37 @@ void cpuidle_resume_and_unlock(void) | |||
164 | 209 | ||
165 | EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); | 210 | EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); |
166 | 211 | ||
212 | /** | ||
213 | * cpuidle_wrap_enter - performs timekeeping and irqen around enter function | ||
214 | * @dev: pointer to a valid cpuidle_device object | ||
215 | * @drv: pointer to a valid cpuidle_driver object | ||
216 | * @index: index of the target cpuidle state. | ||
217 | */ | ||
218 | int cpuidle_wrap_enter(struct cpuidle_device *dev, | ||
219 | struct cpuidle_driver *drv, int index, | ||
220 | int (*enter)(struct cpuidle_device *dev, | ||
221 | struct cpuidle_driver *drv, int index)) | ||
222 | { | ||
223 | ktime_t time_start, time_end; | ||
224 | s64 diff; | ||
225 | |||
226 | time_start = ktime_get(); | ||
227 | |||
228 | index = enter(dev, drv, index); | ||
229 | |||
230 | time_end = ktime_get(); | ||
231 | |||
232 | local_irq_enable(); | ||
233 | |||
234 | diff = ktime_to_us(ktime_sub(time_end, time_start)); | ||
235 | if (diff > INT_MAX) | ||
236 | diff = INT_MAX; | ||
237 | |||
238 | dev->last_residency = (int) diff; | ||
239 | |||
240 | return index; | ||
241 | } | ||
242 | |||
167 | #ifdef CONFIG_ARCH_HAS_CPU_RELAX | 243 | #ifdef CONFIG_ARCH_HAS_CPU_RELAX |
168 | static int poll_idle(struct cpuidle_device *dev, | 244 | static int poll_idle(struct cpuidle_device *dev, |
169 | struct cpuidle_driver *drv, int index) | 245 | struct cpuidle_driver *drv, int index) |
@@ -197,6 +273,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) | |||
197 | state->power_usage = -1; | 273 | state->power_usage = -1; |
198 | state->flags = 0; | 274 | state->flags = 0; |
199 | state->enter = poll_idle; | 275 | state->enter = poll_idle; |
276 | state->disable = 0; | ||
200 | } | 277 | } |
201 | #else | 278 | #else |
202 | static void poll_idle_init(struct cpuidle_driver *drv) {} | 279 | static void poll_idle_init(struct cpuidle_driver *drv) {} |
@@ -212,13 +289,14 @@ static void poll_idle_init(struct cpuidle_driver *drv) {} | |||
212 | int cpuidle_enable_device(struct cpuidle_device *dev) | 289 | int cpuidle_enable_device(struct cpuidle_device *dev) |
213 | { | 290 | { |
214 | int ret, i; | 291 | int ret, i; |
292 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
215 | 293 | ||
216 | if (dev->enabled) | 294 | if (dev->enabled) |
217 | return 0; | 295 | return 0; |
218 | if (!cpuidle_get_driver() || !cpuidle_curr_governor) | 296 | if (!drv || !cpuidle_curr_governor) |
219 | return -EIO; | 297 | return -EIO; |
220 | if (!dev->state_count) | 298 | if (!dev->state_count) |
221 | return -EINVAL; | 299 | dev->state_count = drv->state_count; |
222 | 300 | ||
223 | if (dev->registered == 0) { | 301 | if (dev->registered == 0) { |
224 | ret = __cpuidle_register_device(dev); | 302 | ret = __cpuidle_register_device(dev); |
@@ -226,13 +304,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
226 | return ret; | 304 | return ret; |
227 | } | 305 | } |
228 | 306 | ||
229 | poll_idle_init(cpuidle_get_driver()); | 307 | cpuidle_enter_ops = drv->en_core_tk_irqen ? |
308 | cpuidle_enter_tk : cpuidle_enter; | ||
309 | |||
310 | poll_idle_init(drv); | ||
230 | 311 | ||
231 | if ((ret = cpuidle_add_state_sysfs(dev))) | 312 | if ((ret = cpuidle_add_state_sysfs(dev))) |
232 | return ret; | 313 | return ret; |
233 | 314 | ||
234 | if (cpuidle_curr_governor->enable && | 315 | if (cpuidle_curr_governor->enable && |
235 | (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev))) | 316 | (ret = cpuidle_curr_governor->enable(drv, dev))) |
236 | goto fail_sysfs; | 317 | goto fail_sysfs; |
237 | 318 | ||
238 | for (i = 0; i < dev->state_count; i++) { | 319 | for (i = 0; i < dev->state_count; i++) { |