diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 85 |
1 files changed, 57 insertions, 28 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index d6a533e68e0f..e28f6ea46f1a 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -92,6 +92,34 @@ int cpuidle_play_dead(void) | |||
92 | } | 92 | } |
93 | 93 | ||
94 | /** | 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 | /** | ||
95 | * cpuidle_idle_call - the main idle loop | 123 | * cpuidle_idle_call - the main idle loop |
96 | * | 124 | * |
97 | * NOTE: no locks or semaphores should be used here | 125 | * NOTE: no locks or semaphores should be used here |
@@ -113,15 +141,6 @@ int cpuidle_idle_call(void) | |||
113 | if (!dev || !dev->enabled) | 141 | if (!dev || !dev->enabled) |
114 | return -EBUSY; | 142 | return -EBUSY; |
115 | 143 | ||
116 | #if 0 | ||
117 | /* shows regressions, re-enable for 2.6.29 */ | ||
118 | /* | ||
119 | * run any timers that can be run now, at this point | ||
120 | * before calculating the idle duration etc. | ||
121 | */ | ||
122 | hrtimer_peek_ahead_timers(); | ||
123 | #endif | ||
124 | |||
125 | /* ask the governor for the next state */ | 144 | /* ask the governor for the next state */ |
126 | next_state = cpuidle_curr_governor->select(drv, dev); | 145 | next_state = cpuidle_curr_governor->select(drv, dev); |
127 | if (need_resched()) { | 146 | if (need_resched()) { |
@@ -132,23 +151,15 @@ int cpuidle_idle_call(void) | |||
132 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); | 151 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); |
133 | trace_cpu_idle_rcuidle(next_state, dev->cpu); | 152 | trace_cpu_idle_rcuidle(next_state, dev->cpu); |
134 | 153 | ||
135 | 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); | ||
136 | 159 | ||
137 | trace_power_end_rcuidle(dev->cpu); | 160 | trace_power_end_rcuidle(dev->cpu); |
138 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); | 161 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); |
139 | 162 | ||
140 | if (entered_state >= 0) { | ||
141 | /* Update cpuidle counters */ | ||
142 | /* This can be moved to within driver enter routine | ||
143 | * but that results in multiple copies of same code. | ||
144 | */ | ||
145 | dev->states_usage[entered_state].time += | ||
146 | (unsigned long long)dev->last_residency; | ||
147 | dev->states_usage[entered_state].usage++; | ||
148 | } else { | ||
149 | dev->last_residency = 0; | ||
150 | } | ||
151 | |||
152 | /* give the governor an opportunity to reflect on the outcome */ | 163 | /* give the governor an opportunity to reflect on the outcome */ |
153 | if (cpuidle_curr_governor->reflect) | 164 | if (cpuidle_curr_governor->reflect) |
154 | cpuidle_curr_governor->reflect(dev, entered_state); | 165 | cpuidle_curr_governor->reflect(dev, entered_state); |
@@ -299,6 +310,9 @@ int cpuidle_enable_device(struct cpuidle_device *dev) | |||
299 | int ret, i; | 310 | int ret, i; |
300 | struct cpuidle_driver *drv = cpuidle_get_driver(); | 311 | struct cpuidle_driver *drv = cpuidle_get_driver(); |
301 | 312 | ||
313 | if (!dev) | ||
314 | return -EINVAL; | ||
315 | |||
302 | if (dev->enabled) | 316 | if (dev->enabled) |
303 | return 0; | 317 | return 0; |
304 | if (!drv || !cpuidle_curr_governor) | 318 | if (!drv || !cpuidle_curr_governor) |
@@ -383,8 +397,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
383 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); | 397 | struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); |
384 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); | 398 | struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); |
385 | 399 | ||
386 | if (!dev) | ||
387 | return -EINVAL; | ||
388 | if (!try_module_get(cpuidle_driver->owner)) | 400 | if (!try_module_get(cpuidle_driver->owner)) |
389 | return -EINVAL; | 401 | return -EINVAL; |
390 | 402 | ||
@@ -392,13 +404,25 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
392 | 404 | ||
393 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 405 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
394 | list_add(&dev->device_list, &cpuidle_detected_devices); | 406 | list_add(&dev->device_list, &cpuidle_detected_devices); |
395 | if ((ret = cpuidle_add_sysfs(cpu_dev))) { | 407 | ret = cpuidle_add_sysfs(cpu_dev); |
396 | module_put(cpuidle_driver->owner); | 408 | if (ret) |
397 | return ret; | 409 | goto err_sysfs; |
398 | } | 410 | |
411 | ret = cpuidle_coupled_register_device(dev); | ||
412 | if (ret) | ||
413 | goto err_coupled; | ||
399 | 414 | ||
400 | dev->registered = 1; | 415 | dev->registered = 1; |
401 | return 0; | 416 | return 0; |
417 | |||
418 | err_coupled: | ||
419 | cpuidle_remove_sysfs(cpu_dev); | ||
420 | wait_for_completion(&dev->kobj_unregister); | ||
421 | err_sysfs: | ||
422 | list_del(&dev->device_list); | ||
423 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | ||
424 | module_put(cpuidle_driver->owner); | ||
425 | return ret; | ||
402 | } | 426 | } |
403 | 427 | ||
404 | /** | 428 | /** |
@@ -409,6 +433,9 @@ int cpuidle_register_device(struct cpuidle_device *dev) | |||
409 | { | 433 | { |
410 | int ret; | 434 | int ret; |
411 | 435 | ||
436 | if (!dev) | ||
437 | return -EINVAL; | ||
438 | |||
412 | mutex_lock(&cpuidle_lock); | 439 | mutex_lock(&cpuidle_lock); |
413 | 440 | ||
414 | if ((ret = __cpuidle_register_device(dev))) { | 441 | if ((ret = __cpuidle_register_device(dev))) { |
@@ -448,6 +475,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
448 | wait_for_completion(&dev->kobj_unregister); | 475 | wait_for_completion(&dev->kobj_unregister); |
449 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | 476 | per_cpu(cpuidle_devices, dev->cpu) = NULL; |
450 | 477 | ||
478 | cpuidle_coupled_unregister_device(dev); | ||
479 | |||
451 | cpuidle_resume_and_unlock(); | 480 | cpuidle_resume_and_unlock(); |
452 | 481 | ||
453 | module_put(cpuidle_driver->owner); | 482 | module_put(cpuidle_driver->owner); |