diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 81 |
1 files changed, 52 insertions, 29 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 8ffef26ffdcf..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 |
@@ -134,23 +151,15 @@ int cpuidle_idle_call(void) | |||
134 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); | 151 | trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); |
135 | trace_cpu_idle_rcuidle(next_state, dev->cpu); | 152 | trace_cpu_idle_rcuidle(next_state, dev->cpu); |
136 | 153 | ||
137 | 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); | ||
138 | 159 | ||
139 | trace_power_end_rcuidle(dev->cpu); | 160 | trace_power_end_rcuidle(dev->cpu); |
140 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); | 161 | trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); |
141 | 162 | ||
142 | if (entered_state >= 0) { | ||
143 | /* Update cpuidle counters */ | ||
144 | /* This can be moved to within driver enter routine | ||
145 | * but that results in multiple copies of same code. | ||
146 | */ | ||
147 | dev->states_usage[entered_state].time += | ||
148 | (unsigned long long)dev->last_residency; | ||
149 | dev->states_usage[entered_state].usage++; | ||
150 | } else { | ||
151 | dev->last_residency = 0; | ||
152 | } | ||
153 | |||
154 | /* give the governor an opportunity to reflect on the outcome */ | 163 | /* give the governor an opportunity to reflect on the outcome */ |
155 | if (cpuidle_curr_governor->reflect) | 164 | if (cpuidle_curr_governor->reflect) |
156 | cpuidle_curr_governor->reflect(dev, entered_state); | 165 | cpuidle_curr_governor->reflect(dev, entered_state); |
@@ -177,7 +186,7 @@ void cpuidle_uninstall_idle_handler(void) | |||
177 | { | 186 | { |
178 | if (enabled_devices) { | 187 | if (enabled_devices) { |
179 | initialized = 0; | 188 | initialized = 0; |
180 | cpuidle_kick_cpus(); | 189 | kick_all_cpus_sync(); |
181 | } | 190 | } |
182 | } | 191 | } |
183 | 192 | ||
@@ -379,13 +388,25 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) | |||
379 | 388 | ||
380 | per_cpu(cpuidle_devices, dev->cpu) = dev; | 389 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
381 | list_add(&dev->device_list, &cpuidle_detected_devices); | 390 | list_add(&dev->device_list, &cpuidle_detected_devices); |
382 | if ((ret = cpuidle_add_sysfs(cpu_dev))) { | 391 | ret = cpuidle_add_sysfs(cpu_dev); |
383 | module_put(cpuidle_driver->owner); | 392 | if (ret) |
384 | return ret; | 393 | goto err_sysfs; |
385 | } | 394 | |
395 | ret = cpuidle_coupled_register_device(dev); | ||
396 | if (ret) | ||
397 | goto err_coupled; | ||
386 | 398 | ||
387 | dev->registered = 1; | 399 | dev->registered = 1; |
388 | 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; | ||
389 | } | 410 | } |
390 | 411 | ||
391 | /** | 412 | /** |
@@ -438,6 +459,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) | |||
438 | wait_for_completion(&dev->kobj_unregister); | 459 | wait_for_completion(&dev->kobj_unregister); |
439 | per_cpu(cpuidle_devices, dev->cpu) = NULL; | 460 | per_cpu(cpuidle_devices, dev->cpu) = NULL; |
440 | 461 | ||
462 | cpuidle_coupled_unregister_device(dev); | ||
463 | |||
441 | cpuidle_resume_and_unlock(); | 464 | cpuidle_resume_and_unlock(); |
442 | 465 | ||
443 | module_put(cpuidle_driver->owner); | 466 | module_put(cpuidle_driver->owner); |