diff options
Diffstat (limited to 'drivers/idle/intel_idle.c')
-rw-r--r-- | drivers/idle/intel_idle.c | 69 |
1 files changed, 62 insertions, 7 deletions
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 56ac09d6c930..7acb32e7f817 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c | |||
@@ -59,6 +59,8 @@ | |||
59 | #include <linux/hrtimer.h> /* ktime_get_real() */ | 59 | #include <linux/hrtimer.h> /* ktime_get_real() */ |
60 | #include <trace/events/power.h> | 60 | #include <trace/events/power.h> |
61 | #include <linux/sched.h> | 61 | #include <linux/sched.h> |
62 | #include <linux/notifier.h> | ||
63 | #include <linux/cpu.h> | ||
62 | #include <asm/mwait.h> | 64 | #include <asm/mwait.h> |
63 | 65 | ||
64 | #define INTEL_IDLE_VERSION "0.4" | 66 | #define INTEL_IDLE_VERSION "0.4" |
@@ -73,6 +75,7 @@ static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1; | |||
73 | 75 | ||
74 | static unsigned int mwait_substates; | 76 | static unsigned int mwait_substates; |
75 | 77 | ||
78 | #define LAPIC_TIMER_ALWAYS_RELIABLE 0xFFFFFFFF | ||
76 | /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ | 79 | /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ |
77 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ | 80 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ |
78 | 81 | ||
@@ -82,6 +85,14 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state); | |||
82 | static struct cpuidle_state *cpuidle_state_table; | 85 | static struct cpuidle_state *cpuidle_state_table; |
83 | 86 | ||
84 | /* | 87 | /* |
88 | * Set this flag for states where the HW flushes the TLB for us | ||
89 | * and so we don't need cross-calls to keep it consistent. | ||
90 | * If this flag is set, SW flushes the TLB, so even if the | ||
91 | * HW doesn't do the flushing, this flag is safe to use. | ||
92 | */ | ||
93 | #define CPUIDLE_FLAG_TLB_FLUSHED 0x10000 | ||
94 | |||
95 | /* | ||
85 | * States are indexed by the cstate number, | 96 | * States are indexed by the cstate number, |
86 | * which is also the index into the MWAIT hint array. | 97 | * which is also the index into the MWAIT hint array. |
87 | * Thus C0 is a dummy. | 98 | * Thus C0 is a dummy. |
@@ -122,7 +133,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { | |||
122 | .driver_data = (void *) 0x00, | 133 | .driver_data = (void *) 0x00, |
123 | .flags = CPUIDLE_FLAG_TIME_VALID, | 134 | .flags = CPUIDLE_FLAG_TIME_VALID, |
124 | .exit_latency = 1, | 135 | .exit_latency = 1, |
125 | .target_residency = 4, | 136 | .target_residency = 1, |
126 | .enter = &intel_idle }, | 137 | .enter = &intel_idle }, |
127 | { /* MWAIT C2 */ | 138 | { /* MWAIT C2 */ |
128 | .name = "SNB-C3", | 139 | .name = "SNB-C3", |
@@ -130,7 +141,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { | |||
130 | .driver_data = (void *) 0x10, | 141 | .driver_data = (void *) 0x10, |
131 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, | 142 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, |
132 | .exit_latency = 80, | 143 | .exit_latency = 80, |
133 | .target_residency = 160, | 144 | .target_residency = 211, |
134 | .enter = &intel_idle }, | 145 | .enter = &intel_idle }, |
135 | { /* MWAIT C3 */ | 146 | { /* MWAIT C3 */ |
136 | .name = "SNB-C6", | 147 | .name = "SNB-C6", |
@@ -138,7 +149,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { | |||
138 | .driver_data = (void *) 0x20, | 149 | .driver_data = (void *) 0x20, |
139 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, | 150 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, |
140 | .exit_latency = 104, | 151 | .exit_latency = 104, |
141 | .target_residency = 208, | 152 | .target_residency = 345, |
142 | .enter = &intel_idle }, | 153 | .enter = &intel_idle }, |
143 | { /* MWAIT C4 */ | 154 | { /* MWAIT C4 */ |
144 | .name = "SNB-C7", | 155 | .name = "SNB-C7", |
@@ -146,7 +157,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = { | |||
146 | .driver_data = (void *) 0x30, | 157 | .driver_data = (void *) 0x30, |
147 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, | 158 | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, |
148 | .exit_latency = 109, | 159 | .exit_latency = 109, |
149 | .target_residency = 300, | 160 | .target_residency = 345, |
150 | .enter = &intel_idle }, | 161 | .enter = &intel_idle }, |
151 | }; | 162 | }; |
152 | 163 | ||
@@ -220,8 +231,6 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state) | |||
220 | kt_before = ktime_get_real(); | 231 | kt_before = ktime_get_real(); |
221 | 232 | ||
222 | stop_critical_timings(); | 233 | stop_critical_timings(); |
223 | trace_power_start(POWER_CSTATE, (eax >> 4) + 1, cpu); | ||
224 | trace_cpu_idle((eax >> 4) + 1, cpu); | ||
225 | if (!need_resched()) { | 234 | if (!need_resched()) { |
226 | 235 | ||
227 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 236 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
@@ -243,6 +252,39 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state) | |||
243 | return usec_delta; | 252 | return usec_delta; |
244 | } | 253 | } |
245 | 254 | ||
255 | static void __setup_broadcast_timer(void *arg) | ||
256 | { | ||
257 | unsigned long reason = (unsigned long)arg; | ||
258 | int cpu = smp_processor_id(); | ||
259 | |||
260 | reason = reason ? | ||
261 | CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; | ||
262 | |||
263 | clockevents_notify(reason, &cpu); | ||
264 | } | ||
265 | |||
266 | static int __cpuinit setup_broadcast_cpuhp_notify(struct notifier_block *n, | ||
267 | unsigned long action, void *hcpu) | ||
268 | { | ||
269 | int hotcpu = (unsigned long)hcpu; | ||
270 | |||
271 | switch (action & 0xf) { | ||
272 | case CPU_ONLINE: | ||
273 | smp_call_function_single(hotcpu, __setup_broadcast_timer, | ||
274 | (void *)true, 1); | ||
275 | break; | ||
276 | case CPU_DOWN_PREPARE: | ||
277 | smp_call_function_single(hotcpu, __setup_broadcast_timer, | ||
278 | (void *)false, 1); | ||
279 | break; | ||
280 | } | ||
281 | return NOTIFY_OK; | ||
282 | } | ||
283 | |||
284 | static struct notifier_block __cpuinitdata setup_broadcast_notifier = { | ||
285 | .notifier_call = setup_broadcast_cpuhp_notify, | ||
286 | }; | ||
287 | |||
246 | /* | 288 | /* |
247 | * intel_idle_probe() | 289 | * intel_idle_probe() |
248 | */ | 290 | */ |
@@ -305,7 +347,11 @@ static int intel_idle_probe(void) | |||
305 | } | 347 | } |
306 | 348 | ||
307 | if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ | 349 | if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ |
308 | lapic_timer_reliable_states = 0xFFFFFFFF; | 350 | lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; |
351 | else { | ||
352 | smp_call_function(__setup_broadcast_timer, (void *)true, 1); | ||
353 | register_cpu_notifier(&setup_broadcast_notifier); | ||
354 | } | ||
309 | 355 | ||
310 | pr_debug(PREFIX "v" INTEL_IDLE_VERSION | 356 | pr_debug(PREFIX "v" INTEL_IDLE_VERSION |
311 | " model 0x%X\n", boot_cpu_data.x86_model); | 357 | " model 0x%X\n", boot_cpu_data.x86_model); |
@@ -403,6 +449,10 @@ static int __init intel_idle_init(void) | |||
403 | { | 449 | { |
404 | int retval; | 450 | int retval; |
405 | 451 | ||
452 | /* Do not load intel_idle at all for now if idle= is passed */ | ||
453 | if (boot_option_idle_override != IDLE_NO_OVERRIDE) | ||
454 | return -ENODEV; | ||
455 | |||
406 | retval = intel_idle_probe(); | 456 | retval = intel_idle_probe(); |
407 | if (retval) | 457 | if (retval) |
408 | return retval; | 458 | return retval; |
@@ -428,6 +478,11 @@ static void __exit intel_idle_exit(void) | |||
428 | intel_idle_cpuidle_devices_uninit(); | 478 | intel_idle_cpuidle_devices_uninit(); |
429 | cpuidle_unregister_driver(&intel_idle_driver); | 479 | cpuidle_unregister_driver(&intel_idle_driver); |
430 | 480 | ||
481 | if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { | ||
482 | smp_call_function(__setup_broadcast_timer, (void *)false, 1); | ||
483 | unregister_cpu_notifier(&setup_broadcast_notifier); | ||
484 | } | ||
485 | |||
431 | return; | 486 | return; |
432 | } | 487 | } |
433 | 488 | ||