diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 98 |
1 files changed, 58 insertions, 40 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 2f0083a51a9a..bb4e827434ce 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -40,17 +40,6 @@ void disable_cpuidle(void) | |||
40 | off = 1; | 40 | off = 1; |
41 | } | 41 | } |
42 | 42 | ||
43 | #if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT) | ||
44 | static void cpuidle_kick_cpus(void) | ||
45 | { | ||
46 | cpu_idle_wait(); | ||
47 | } | ||
48 | #elif defined(CONFIG_SMP) | ||
49 | # error "Arch needs cpu_idle_wait() equivalent here" | ||
50 | #else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */ | ||
51 | static void cpuidle_kick_cpus(void) {} | ||
52 | #endif | ||
53 | |||
54 | static int __cpuidle_register_device(struct cpuidle_device *dev); | 43 | static int __cpuidle_register_device(struct cpuidle_device *dev); |
55 | 44 | ||
56 | static inline int cpuidle_enter(struct cpuidle_device *dev, | 45 | static inline int cpuidle_enter(struct cpuidle_device *dev, |
@@ -103,6 +92,34 @@ int cpuidle_play_dead(void) | |||
103 | } | 92 | } |
104 | 93 | ||
105 | /** | 94 | /** |
95 | * cpuidle_enter_state - enter the state and update stats | ||
96 | * @dev: cpuidle device for this cpu | ||
97 | * @drv: cpuidle driver for this cpu | ||
98 | * @next_state: index into drv->states of the state to enter | ||
99 | */ | ||
100 | int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, | ||
101 | int next_state) | ||
102 | { | ||
103 | int entered_state; | ||
104 | |||
105 | entered_state = cpuidle_enter_ops(dev, drv, next_state); | ||
106 | |||
107 | if (entered_state >= 0) { | ||
108 | /* Update cpuidle counters */ | ||
109 | /* This can be moved to within driver enter routine | ||
110 | * but that results in multiple copies of same code. | ||
111 | */ | ||
112 | dev->states_usage[entered_state].time += | ||
113 | (unsigned long long)dev->last_residency; | ||
114 | dev->states_usage[entered_state].usage++; | ||
115 | } else { | ||
116 | dev->last_residency = 0; | ||
117 | } | ||
118 | |||
119 | return entered_state; | ||
120 | } | ||
121 | |||
122 | /** | ||
106 | * cpuidle_idle_call - the main idle loop | 123 | * cpuidle_idle_call - the main idle loop |
107 | * | 124 | * |
108 | * NOTE: no locks or semaphores should be used here | 125 | * NOTE: no locks or semaphores should be used here |
@@ -124,15 +141,6 @@ int cpuidle_idle_call(void) | |||
124 | if (!dev || !dev->enabled) | 141 | if (!dev || !dev->enabled) |
125 | return -EBUSY; | 142 | return -EBUSY; |
126 | 143 | ||
127 | #if 0 | ||
128 | /* shows regressions, re-enable for 2.6.29 */ | ||
129 | /* | ||
130 | * run any timers that can be run now, at this point | ||
131 | * before calculating the idle duration etc. | ||
132 | */ | ||
133 | hrtimer_peek_ahead_timers(); | ||
134 | #endif | ||
135 | |||
136 | /* ask the governor for the next state */ | 144 | /* ask the governor for the next state */ |
137 | next_state = cpuidle_curr_governor->select(drv, dev); | 145 | next_state = cpuidle_curr_governor->select(drv, dev); |
138 | if (need_resched()) { | 146 | if (need_resched()) { |
@@ -143,23 +151,15 @@ int cpuidle_idle_call(void) | |||
143 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); | 151 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); |
144 | trace_cpu_idle_rcuidle(next_state, dev->cpu); | 152 | trace_cpu_idle_rcuidle(next_state, dev->cpu); |
145 | 153 | ||
146 | entered_state = cpuidle_enter_ops(dev, drv, next_state); | 154 | if (cpuidle_state_is_coupled(dev, drv, next_state)) |
155 | entered_state = cpuidle_enter_state_coupled(dev, drv, | ||
156 | next_state); | ||
157 | else | ||
158 | entered_state = cpuidle_enter_state(dev, drv, next_state); | ||
147 | 159 | ||
148 | trace_power_end_rcuidle(dev->cpu); | 160 | trace_power_end_rcuidle(dev->cpu); |
149 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); | 161 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); |
150 | 162 | ||
151 | if (entered_state >= 0) { | ||
152 | /* Update cpuidle counters */ | ||
153 | /* This can be moved to within driver enter routine | ||
154 | * but that results in multiple copies of same code. | ||
155 | */ | ||
156 | dev->states_usage[entered_state].time += | ||
157 | (unsigned long long)dev->last_residency; | ||
158 | dev->states_usage[entered_state].usage++; | ||
159 | } else { | ||
160 | dev->last_residency = 0; | ||
161 | } | ||
162 | |||
163 | /* give the governor an opportunity to reflect on the outcome */ | 163 | /* give the governor an opportunity to reflect on the outcome */ |
164 | if (cpuidle_curr_governor->reflect) | 164 | if (cpuidle_curr_governor->reflect) |
165 | cpuidle_curr_governor->reflect(dev, entered_state); | 165 | cpuidle_curr_governor->reflect(dev, entered_state); |
@@ -186,7 +186,7 @@ void cpuidle_uninstall_idle_handler(void) | |||
186 | { | 186 | { |
187 | if (enabled_devices) { | 187 | if (enabled_devices) { |
188 | initialized = 0; | 188 | initialized = 0; |
189 | cpuidle_kick_cpus(); | 189 | kick_all_cpus_sync(); |
190 | } | 190 | } |
191 | } | 191 | } |
192 | 192 | ||
@@ -294,6 +294,9 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
294 | int ret, i; | 294 | int ret, i; |
295 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 295 | struct cpuidle_driver *drv = cpuidle_get_driver(); |
296 | 296 | ||
297 | if (!dev) | ||
298 | return -EINVAL; | ||
299 | |||
297 | if (dev->enabled) | 300 | if (dev->enabled) |
298 | return 0; | 301 | return 0; |
299 | if (!drv || !cpuidle_curr_governor) | 302 | if (!drv || !cpuidle_curr_governor) |
@@ -378,8 +381,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
378 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); | 381 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); |
379 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); | 382 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); |
380 | 383 | ||
381 | if (!dev) | ||
382 | return -EINVAL; | ||
383 | if (!try_module_get(cpuidle_driver->owner)) | 384 | if (!try_module_get(cpuidle_driver->owner)) |
384 | return -EINVAL; | 385 | return -EINVAL; |
385 | 386 | ||
@@ -387,13 +388,25 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
387 | 388 | ||
388 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 389 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
389 | list_add(&dev->device_list, &cpuidle_detected_devices); | 390 | list_add(&dev->device_list, &cpuidle_detected_devices); |
390 | if ((ret = cpuidle_add_sysfs(cpu_dev))) { | 391 | ret = cpuidle_add_sysfs(cpu_dev); |
391 | module_put(cpuidle_driver->owner); | 392 | if (ret) |
392 | return ret; | 393 | goto err_sysfs; |
393 | } | 394 | |
395 | ret = cpuidle_coupled_register_device(dev); | ||
396 | if (ret) | ||
397 | goto err_coupled; | ||
394 | 398 | ||
395 | dev->registered = 1; | 399 | dev->registered = 1; |
396 | return 0; | 400 | return 0; |
401 | |||
402 | err_coupled: | ||
403 | cpuidle_remove_sysfs(cpu_dev); | ||
404 | wait_for_completion(&dev->kobj_unregister); | ||
405 | err_sysfs: | ||
406 | list_del(&dev->device_list); | ||
407 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | ||
408 | module_put(cpuidle_driver->owner); | ||
409 | return ret; | ||
397 | } | 410 | } |
398 | 411 | ||
399 | /** | 412 | /** |
@@ -404,6 +417,9 @@ int cpuidle_register_device(struct cpuidle_device *dev) | |||
404 | { | 417 | { |
405 | int ret; | 418 | int ret; |
406 | 419 | ||
420 | if (!dev) | ||
421 | return -EINVAL; | ||
422 | |||
407 | mutex_lock(&cpuidle_lock); | 423 | mutex_lock(&cpuidle_lock); |
408 | 424 | ||
409 | if ((ret = __cpuidle_register_device(dev))) { | 425 | if ((ret = __cpuidle_register_device(dev))) { |
@@ -443,6 +459,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
443 | wait_for_completion(&dev->kobj_unregister); | 459 | wait_for_completion(&dev->kobj_unregister); |
444 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | 460 | per_cpu(cpuidle_devices, dev->cpu) = NULL; |
445 | 461 | ||
462 | cpuidle_coupled_unregister_device(dev); | ||
463 | |||
446 | cpuidle_resume_and_unlock(); | 464 | cpuidle_resume_and_unlock(); |
447 | 465 | ||
448 | module_put(cpuidle_driver->owner); | 466 | module_put(cpuidle_driver->owner); |