diff options
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 97 | ||||
-rw-r--r-- | drivers/cpuidle/driver.c | 2 | ||||
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 7 | ||||
-rw-r--r-- | drivers/cpuidle/sysfs.c | 40 |
4 files changed, 135 insertions, 11 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++) { |
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 284d7af5a9c8..40cd3f3024df 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c | |||
@@ -47,7 +47,7 @@ static void __cpuidle_register_driver(struct cpuidle_driver *drv) | |||
47 | */ | 47 | */ |
48 | int cpuidle_register_driver(struct cpuidle_driver *drv) | 48 | int cpuidle_register_driver(struct cpuidle_driver *drv) |
49 | { | 49 | { |
50 | if (!drv) | 50 | if (!drv || !drv->state_count) |
51 | return -EINVAL; | 51 | return -EINVAL; |
52 | 52 | ||
53 | if (cpuidle_disabled()) | 53 | if (cpuidle_disabled()) |
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index ad0952601ae2..06335756ea14 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -236,7 +236,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
236 | { | 236 | { |
237 | struct menu_device *data = &__get_cpu_var(menu_devices); | 237 | struct menu_device *data = &__get_cpu_var(menu_devices); |
238 | int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); | 238 | int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); |
239 | unsigned int power_usage = -1; | 239 | int power_usage = -1; |
240 | int i; | 240 | int i; |
241 | int multiplier; | 241 | int multiplier; |
242 | struct timespec t; | 242 | struct timespec t; |
@@ -280,7 +280,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
280 | * We want to default to C1 (hlt), not to busy polling | 280 | * We want to default to C1 (hlt), not to busy polling |
281 | * unless the timer is happening really really soon. | 281 | * unless the timer is happening really really soon. |
282 | */ | 282 | */ |
283 | if (data->expected_us > 5) | 283 | if (data->expected_us > 5 && |
284 | drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0) | ||
284 | data->last_state_idx = CPUIDLE_DRIVER_STATE_START; | 285 | data->last_state_idx = CPUIDLE_DRIVER_STATE_START; |
285 | 286 | ||
286 | /* | 287 | /* |
@@ -290,6 +291,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
290 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { | 291 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { |
291 | struct cpuidle_state *s = &drv->states[i]; | 292 | struct cpuidle_state *s = &drv->states[i]; |
292 | 293 | ||
294 | if (s->disable) | ||
295 | continue; | ||
293 | if (s->target_residency > data->predicted_us) | 296 | if (s->target_residency > data->predicted_us) |
294 | continue; | 297 | continue; |
295 | if (s->exit_latency > latency_req) | 298 | if (s->exit_latency > latency_req) |
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 3fe41fe4851a..88032b4dc6d2 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/sysfs.h> | 11 | #include <linux/sysfs.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
14 | #include <linux/capability.h> | ||
14 | 15 | ||
15 | #include "cpuidle.h" | 16 | #include "cpuidle.h" |
16 | 17 | ||
@@ -222,6 +223,9 @@ struct cpuidle_state_attr { | |||
222 | #define define_one_state_ro(_name, show) \ | 223 | #define define_one_state_ro(_name, show) \ |
223 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) | 224 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL) |
224 | 225 | ||
226 | #define define_one_state_rw(_name, show, store) \ | ||
227 | static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store) | ||
228 | |||
225 | #define define_show_state_function(_name) \ | 229 | #define define_show_state_function(_name) \ |
226 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 230 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
227 | struct cpuidle_state_usage *state_usage, char *buf) \ | 231 | struct cpuidle_state_usage *state_usage, char *buf) \ |
@@ -229,6 +233,24 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ | |||
229 | return sprintf(buf, "%u\n", state->_name);\ | 233 | return sprintf(buf, "%u\n", state->_name);\ |
230 | } | 234 | } |
231 | 235 | ||
236 | #define define_store_state_function(_name) \ | ||
237 | static ssize_t store_state_##_name(struct cpuidle_state *state, \ | ||
238 | const char *buf, size_t size) \ | ||
239 | { \ | ||
240 | long value; \ | ||
241 | int err; \ | ||
242 | if (!capable(CAP_SYS_ADMIN)) \ | ||
243 | return -EPERM; \ | ||
244 | err = kstrtol(buf, 0, &value); \ | ||
245 | if (err) \ | ||
246 | return err; \ | ||
247 | if (value) \ | ||
248 | state->disable = 1; \ | ||
249 | else \ | ||
250 | state->disable = 0; \ | ||
251 | return size; \ | ||
252 | } | ||
253 | |||
232 | #define define_show_state_ull_function(_name) \ | 254 | #define define_show_state_ull_function(_name) \ |
233 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ | 255 | static ssize_t show_state_##_name(struct cpuidle_state *state, \ |
234 | struct cpuidle_state_usage *state_usage, char *buf) \ | 256 | struct cpuidle_state_usage *state_usage, char *buf) \ |
@@ -251,6 +273,8 @@ define_show_state_ull_function(usage) | |||
251 | define_show_state_ull_function(time) | 273 | define_show_state_ull_function(time) |
252 | define_show_state_str_function(name) | 274 | define_show_state_str_function(name) |
253 | define_show_state_str_function(desc) | 275 | define_show_state_str_function(desc) |
276 | define_show_state_function(disable) | ||
277 | define_store_state_function(disable) | ||
254 | 278 | ||
255 | define_one_state_ro(name, show_state_name); | 279 | define_one_state_ro(name, show_state_name); |
256 | define_one_state_ro(desc, show_state_desc); | 280 | define_one_state_ro(desc, show_state_desc); |
@@ -258,6 +282,7 @@ define_one_state_ro(latency, show_state_exit_latency); | |||
258 | define_one_state_ro(power, show_state_power_usage); | 282 | define_one_state_ro(power, show_state_power_usage); |
259 | define_one_state_ro(usage, show_state_usage); | 283 | define_one_state_ro(usage, show_state_usage); |
260 | define_one_state_ro(time, show_state_time); | 284 | define_one_state_ro(time, show_state_time); |
285 | define_one_state_rw(disable, show_state_disable, store_state_disable); | ||
261 | 286 | ||
262 | static struct attribute *cpuidle_state_default_attrs[] = { | 287 | static struct attribute *cpuidle_state_default_attrs[] = { |
263 | &attr_name.attr, | 288 | &attr_name.attr, |
@@ -266,6 +291,7 @@ static struct attribute *cpuidle_state_default_attrs[] = { | |||
266 | &attr_power.attr, | 291 | &attr_power.attr, |
267 | &attr_usage.attr, | 292 | &attr_usage.attr, |
268 | &attr_time.attr, | 293 | &attr_time.attr, |
294 | &attr_disable.attr, | ||
269 | NULL | 295 | NULL |
270 | }; | 296 | }; |
271 | 297 | ||
@@ -287,8 +313,22 @@ static ssize_t cpuidle_state_show(struct kobject * kobj, | |||
287 | return ret; | 313 | return ret; |
288 | } | 314 | } |
289 | 315 | ||
316 | static ssize_t cpuidle_state_store(struct kobject *kobj, | ||
317 | struct attribute *attr, const char *buf, size_t size) | ||
318 | { | ||
319 | int ret = -EIO; | ||
320 | struct cpuidle_state *state = kobj_to_state(kobj); | ||
321 | struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); | ||
322 | |||
323 | if (cattr->store) | ||
324 | ret = cattr->store(state, buf, size); | ||
325 | |||
326 | return ret; | ||
327 | } | ||
328 | |||
290 | static const struct sysfs_ops cpuidle_state_sysfs_ops = { | 329 | static const struct sysfs_ops cpuidle_state_sysfs_ops = { |
291 | .show = cpuidle_state_show, | 330 | .show = cpuidle_state_show, |
331 | .store = cpuidle_state_store, | ||
292 | }; | 332 | }; |
293 | 333 | ||
294 | static void cpuidle_state_sysfs_release(struct kobject *kobj) | 334 | static void cpuidle_state_sysfs_release(struct kobject *kobj) |