diff options
Diffstat (limited to 'drivers/cpufreq/powernv-cpufreq.c')
-rw-r--r-- | drivers/cpufreq/powernv-cpufreq.c | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 379c0837f5a9..2dfd4fdb5a52 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/cpufreq.h> | 26 | #include <linux/cpufreq.h> |
27 | #include <linux/smp.h> | 27 | #include <linux/smp.h> |
28 | #include <linux/of.h> | 28 | #include <linux/of.h> |
29 | #include <linux/reboot.h> | ||
29 | 30 | ||
30 | #include <asm/cputhreads.h> | 31 | #include <asm/cputhreads.h> |
31 | #include <asm/firmware.h> | 32 | #include <asm/firmware.h> |
@@ -35,6 +36,7 @@ | |||
35 | #define POWERNV_MAX_PSTATES 256 | 36 | #define POWERNV_MAX_PSTATES 256 |
36 | 37 | ||
37 | static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; | 38 | static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; |
39 | static bool rebooting; | ||
38 | 40 | ||
39 | /* | 41 | /* |
40 | * Note: The set of pstates consists of contiguous integers, the | 42 | * Note: The set of pstates consists of contiguous integers, the |
@@ -284,6 +286,15 @@ static void set_pstate(void *freq_data) | |||
284 | } | 286 | } |
285 | 287 | ||
286 | /* | 288 | /* |
289 | * get_nominal_index: Returns the index corresponding to the nominal | ||
290 | * pstate in the cpufreq table | ||
291 | */ | ||
292 | static inline unsigned int get_nominal_index(void) | ||
293 | { | ||
294 | return powernv_pstate_info.max - powernv_pstate_info.nominal; | ||
295 | } | ||
296 | |||
297 | /* | ||
287 | * powernv_cpufreq_target_index: Sets the frequency corresponding to | 298 | * powernv_cpufreq_target_index: Sets the frequency corresponding to |
288 | * the cpufreq table entry indexed by new_index on the cpus in the | 299 | * the cpufreq table entry indexed by new_index on the cpus in the |
289 | * mask policy->cpus | 300 | * mask policy->cpus |
@@ -293,6 +304,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, | |||
293 | { | 304 | { |
294 | struct powernv_smp_call_data freq_data; | 305 | struct powernv_smp_call_data freq_data; |
295 | 306 | ||
307 | if (unlikely(rebooting) && new_index != get_nominal_index()) | ||
308 | return 0; | ||
309 | |||
296 | freq_data.pstate_id = powernv_freqs[new_index].driver_data; | 310 | freq_data.pstate_id = powernv_freqs[new_index].driver_data; |
297 | 311 | ||
298 | /* | 312 | /* |
@@ -317,6 +331,33 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
317 | return cpufreq_table_validate_and_show(policy, powernv_freqs); | 331 | return cpufreq_table_validate_and_show(policy, powernv_freqs); |
318 | } | 332 | } |
319 | 333 | ||
334 | static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb, | ||
335 | unsigned long action, void *unused) | ||
336 | { | ||
337 | int cpu; | ||
338 | struct cpufreq_policy cpu_policy; | ||
339 | |||
340 | rebooting = true; | ||
341 | for_each_online_cpu(cpu) { | ||
342 | cpufreq_get_policy(&cpu_policy, cpu); | ||
343 | powernv_cpufreq_target_index(&cpu_policy, get_nominal_index()); | ||
344 | } | ||
345 | |||
346 | return NOTIFY_DONE; | ||
347 | } | ||
348 | |||
349 | static struct notifier_block powernv_cpufreq_reboot_nb = { | ||
350 | .notifier_call = powernv_cpufreq_reboot_notifier, | ||
351 | }; | ||
352 | |||
353 | static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy) | ||
354 | { | ||
355 | struct powernv_smp_call_data freq_data; | ||
356 | |||
357 | freq_data.pstate_id = powernv_pstate_info.min; | ||
358 | smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1); | ||
359 | } | ||
360 | |||
320 | static struct cpufreq_driver powernv_cpufreq_driver = { | 361 | static struct cpufreq_driver powernv_cpufreq_driver = { |
321 | .name = "powernv-cpufreq", | 362 | .name = "powernv-cpufreq", |
322 | .flags = CPUFREQ_CONST_LOOPS, | 363 | .flags = CPUFREQ_CONST_LOOPS, |
@@ -324,6 +365,7 @@ static struct cpufreq_driver powernv_cpufreq_driver = { | |||
324 | .verify = cpufreq_generic_frequency_table_verify, | 365 | .verify = cpufreq_generic_frequency_table_verify, |
325 | .target_index = powernv_cpufreq_target_index, | 366 | .target_index = powernv_cpufreq_target_index, |
326 | .get = powernv_cpufreq_get, | 367 | .get = powernv_cpufreq_get, |
368 | .stop_cpu = powernv_cpufreq_stop_cpu, | ||
327 | .attr = powernv_cpu_freq_attr, | 369 | .attr = powernv_cpu_freq_attr, |
328 | }; | 370 | }; |
329 | 371 | ||
@@ -342,12 +384,14 @@ static int __init powernv_cpufreq_init(void) | |||
342 | return rc; | 384 | return rc; |
343 | } | 385 | } |
344 | 386 | ||
387 | register_reboot_notifier(&powernv_cpufreq_reboot_nb); | ||
345 | return cpufreq_register_driver(&powernv_cpufreq_driver); | 388 | return cpufreq_register_driver(&powernv_cpufreq_driver); |
346 | } | 389 | } |
347 | module_init(powernv_cpufreq_init); | 390 | module_init(powernv_cpufreq_init); |
348 | 391 | ||
349 | static void __exit powernv_cpufreq_exit(void) | 392 | static void __exit powernv_cpufreq_exit(void) |
350 | { | 393 | { |
394 | unregister_reboot_notifier(&powernv_cpufreq_reboot_nb); | ||
351 | cpufreq_unregister_driver(&powernv_cpufreq_driver); | 395 | cpufreq_unregister_driver(&powernv_cpufreq_driver); |
352 | } | 396 | } |
353 | module_exit(powernv_cpufreq_exit); | 397 | module_exit(powernv_cpufreq_exit); |