diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-03 12:59:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-03 12:59:39 -0400 |
commit | 70c9f18ca8f3652c7dcf715e9f0d41c399fe130d (patch) | |
tree | 5824f0937025688aab07582838d1a0aea5a24a3f /drivers/cpufreq | |
parent | a0a4194c943bc64dd7b6e26cccb036cb26b81363 (diff) | |
parent | 6283e328fb8148a8a5753e95c04c16aaef2287c0 (diff) |
Merge branch 'next' of git://github.com/kernelslacker/cpufreq
* 'next' of git://github.com/kernelslacker/cpufreq:
[CPUFREQ] db8500: support all frequencies
[CPUFREQ] db8500: remove unneeded for loop iteration over freq_table
[CPUFREQ] ARM Exynos4210 PM/Suspend compatibility with different bootloaders
[CPUFREQ] ARM: ux500: send cpufreq notification for all cpus
[CPUFREQ] e_powersaver: Allow user to lower maximum voltage
[CPUFREQ] e_powersaver: Check BIOS limit for CPU frequency
[CPUFREQ] e_powersaver: Additional checks
[CPUFREQ] exynos4210: Show list of available frequencies
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/db8500-cpufreq.c | 36 | ||||
-rw-r--r-- | drivers/cpufreq/e_powersaver.c | 135 | ||||
-rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c | 129 |
3 files changed, 268 insertions, 32 deletions
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index 8e89dcf9d94d..edaa987621ea 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c | |||
@@ -18,24 +18,29 @@ | |||
18 | static struct cpufreq_frequency_table freq_table[] = { | 18 | static struct cpufreq_frequency_table freq_table[] = { |
19 | [0] = { | 19 | [0] = { |
20 | .index = 0, | 20 | .index = 0, |
21 | .frequency = 300000, | 21 | .frequency = 200000, |
22 | }, | 22 | }, |
23 | [1] = { | 23 | [1] = { |
24 | .index = 1, | 24 | .index = 1, |
25 | .frequency = 600000, | 25 | .frequency = 300000, |
26 | }, | 26 | }, |
27 | [2] = { | 27 | [2] = { |
28 | /* Used for MAX_OPP, if available */ | ||
29 | .index = 2, | 28 | .index = 2, |
30 | .frequency = CPUFREQ_TABLE_END, | 29 | .frequency = 600000, |
31 | }, | 30 | }, |
32 | [3] = { | 31 | [3] = { |
32 | /* Used for MAX_OPP, if available */ | ||
33 | .index = 3, | 33 | .index = 3, |
34 | .frequency = CPUFREQ_TABLE_END, | 34 | .frequency = CPUFREQ_TABLE_END, |
35 | }, | 35 | }, |
36 | [4] = { | ||
37 | .index = 4, | ||
38 | .frequency = CPUFREQ_TABLE_END, | ||
39 | }, | ||
36 | }; | 40 | }; |
37 | 41 | ||
38 | static enum arm_opp idx2opp[] = { | 42 | static enum arm_opp idx2opp[] = { |
43 | ARM_EXTCLK, | ||
39 | ARM_50_OPP, | 44 | ARM_50_OPP, |
40 | ARM_100_OPP, | 45 | ARM_100_OPP, |
41 | ARM_MAX_OPP | 46 | ARM_MAX_OPP |
@@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy, | |||
72 | 77 | ||
73 | freqs.old = policy->cur; | 78 | freqs.old = policy->cur; |
74 | freqs.new = freq_table[idx].frequency; | 79 | freqs.new = freq_table[idx].frequency; |
75 | freqs.cpu = policy->cpu; | ||
76 | 80 | ||
77 | if (freqs.old == freqs.new) | 81 | if (freqs.old == freqs.new) |
78 | return 0; | 82 | return 0; |
79 | 83 | ||
80 | /* pre-change notification */ | 84 | /* pre-change notification */ |
81 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | 85 | for_each_cpu(freqs.cpu, policy->cpus) |
86 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
82 | 87 | ||
83 | /* request the PRCM unit for opp change */ | 88 | /* request the PRCM unit for opp change */ |
84 | if (prcmu_set_arm_opp(idx2opp[idx])) { | 89 | if (prcmu_set_arm_opp(idx2opp[idx])) { |
@@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy, | |||
87 | } | 92 | } |
88 | 93 | ||
89 | /* post change notification */ | 94 | /* post change notification */ |
90 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 95 | for_each_cpu(freqs.cpu, policy->cpus) |
96 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
91 | 97 | ||
92 | return 0; | 98 | return 0; |
93 | } | 99 | } |
@@ -104,16 +110,18 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu) | |||
104 | static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) | 110 | static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) |
105 | { | 111 | { |
106 | int res; | 112 | int res; |
107 | int i; | ||
108 | 113 | ||
109 | BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); | 114 | BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); |
110 | 115 | ||
111 | if (cpu_is_u8500v2() && !prcmu_is_u8400()) { | 116 | if (!prcmu_is_u8400()) { |
112 | freq_table[0].frequency = 400000; | 117 | freq_table[1].frequency = 400000; |
113 | freq_table[1].frequency = 800000; | 118 | freq_table[2].frequency = 800000; |
114 | if (prcmu_has_arm_maxopp()) | 119 | if (prcmu_has_arm_maxopp()) |
115 | freq_table[2].frequency = 1000000; | 120 | freq_table[3].frequency = 1000000; |
116 | } | 121 | } |
122 | pr_info("db8500-cpufreq : Available frequencies:\n"); | ||
123 | while (freq_table[i].frequency != CPUFREQ_TABLE_END) | ||
124 | pr_info(" %d Mhz\n", freq_table[i++].frequency/1000); | ||
117 | 125 | ||
118 | /* get policy fields based on the table */ | 126 | /* get policy fields based on the table */ |
119 | res = cpufreq_frequency_table_cpuinfo(policy, freq_table); | 127 | res = cpufreq_frequency_table_cpuinfo(policy, freq_table); |
@@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) | |||
127 | policy->min = policy->cpuinfo.min_freq; | 135 | policy->min = policy->cpuinfo.min_freq; |
128 | policy->max = policy->cpuinfo.max_freq; | 136 | policy->max = policy->cpuinfo.max_freq; |
129 | policy->cur = db8500_cpufreq_getspeed(policy->cpu); | 137 | policy->cur = db8500_cpufreq_getspeed(policy->cpu); |
130 | |||
131 | for (i = 0; freq_table[i].frequency != policy->cur; i++) | ||
132 | ; | ||
133 | |||
134 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | 138 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; |
135 | 139 | ||
136 | /* | 140 | /* |
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 35a257dd4bb7..4bd6815d317b 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c | |||
@@ -19,6 +19,11 @@ | |||
19 | #include <asm/msr.h> | 19 | #include <asm/msr.h> |
20 | #include <asm/tsc.h> | 20 | #include <asm/tsc.h> |
21 | 21 | ||
22 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
23 | #include <linux/acpi.h> | ||
24 | #include <acpi/processor.h> | ||
25 | #endif | ||
26 | |||
22 | #define EPS_BRAND_C7M 0 | 27 | #define EPS_BRAND_C7M 0 |
23 | #define EPS_BRAND_C7 1 | 28 | #define EPS_BRAND_C7 1 |
24 | #define EPS_BRAND_EDEN 2 | 29 | #define EPS_BRAND_EDEN 2 |
@@ -27,11 +32,59 @@ | |||
27 | 32 | ||
28 | struct eps_cpu_data { | 33 | struct eps_cpu_data { |
29 | u32 fsb; | 34 | u32 fsb; |
35 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
36 | u32 bios_limit; | ||
37 | #endif | ||
30 | struct cpufreq_frequency_table freq_table[]; | 38 | struct cpufreq_frequency_table freq_table[]; |
31 | }; | 39 | }; |
32 | 40 | ||
33 | static struct eps_cpu_data *eps_cpu[NR_CPUS]; | 41 | static struct eps_cpu_data *eps_cpu[NR_CPUS]; |
34 | 42 | ||
43 | /* Module parameters */ | ||
44 | static int freq_failsafe_off; | ||
45 | static int voltage_failsafe_off; | ||
46 | static int set_max_voltage; | ||
47 | |||
48 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
49 | static int ignore_acpi_limit; | ||
50 | |||
51 | static struct acpi_processor_performance *eps_acpi_cpu_perf; | ||
52 | |||
53 | /* Minimum necessary to get acpi_processor_get_bios_limit() working */ | ||
54 | static int eps_acpi_init(void) | ||
55 | { | ||
56 | eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance), | ||
57 | GFP_KERNEL); | ||
58 | if (!eps_acpi_cpu_perf) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map, | ||
62 | GFP_KERNEL)) { | ||
63 | kfree(eps_acpi_cpu_perf); | ||
64 | eps_acpi_cpu_perf = NULL; | ||
65 | return -ENOMEM; | ||
66 | } | ||
67 | |||
68 | if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) { | ||
69 | free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map); | ||
70 | kfree(eps_acpi_cpu_perf); | ||
71 | eps_acpi_cpu_perf = NULL; | ||
72 | return -EIO; | ||
73 | } | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int eps_acpi_exit(struct cpufreq_policy *policy) | ||
78 | { | ||
79 | if (eps_acpi_cpu_perf) { | ||
80 | acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0); | ||
81 | free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map); | ||
82 | kfree(eps_acpi_cpu_perf); | ||
83 | eps_acpi_cpu_perf = NULL; | ||
84 | } | ||
85 | return 0; | ||
86 | } | ||
87 | #endif | ||
35 | 88 | ||
36 | static unsigned int eps_get(unsigned int cpu) | 89 | static unsigned int eps_get(unsigned int cpu) |
37 | { | 90 | { |
@@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) | |||
164 | int k, step, voltage; | 217 | int k, step, voltage; |
165 | int ret; | 218 | int ret; |
166 | int states; | 219 | int states; |
220 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
221 | unsigned int limit; | ||
222 | #endif | ||
167 | 223 | ||
168 | if (policy->cpu != 0) | 224 | if (policy->cpu != 0) |
169 | return -ENODEV; | 225 | return -ENODEV; |
@@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy) | |||
244 | return -EINVAL; | 300 | return -EINVAL; |
245 | if (current_voltage > 0x1f || max_voltage > 0x1f) | 301 | if (current_voltage > 0x1f || max_voltage > 0x1f) |
246 | return -EINVAL; | 302 | return -EINVAL; |
247 | if (max_voltage < min_voltage) | 303 | if (max_voltage < min_voltage |
304 | || current_voltage < min_voltage | ||
305 | || current_voltage > max_voltage) | ||
248 | return -EINVAL; | 306 | return -EINVAL; |
249 | 307 | ||
308 | /* Check for systems using underclocked CPU */ | ||
309 | if (!freq_failsafe_off && max_multiplier != current_multiplier) { | ||
310 | printk(KERN_INFO "eps: Your processor is running at different " | ||
311 | "frequency then its maximum. Aborting.\n"); | ||
312 | printk(KERN_INFO "eps: You can use freq_failsafe_off option " | ||
313 | "to disable this check.\n"); | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | if (!voltage_failsafe_off && max_voltage != current_voltage) { | ||
317 | printk(KERN_INFO "eps: Your processor is running at different " | ||
318 | "voltage then its maximum. Aborting.\n"); | ||
319 | printk(KERN_INFO "eps: You can use voltage_failsafe_off " | ||
320 | "option to disable this check.\n"); | ||
321 | return -EINVAL; | ||
322 | } | ||
323 | |||
250 | /* Calc FSB speed */ | 324 | /* Calc FSB speed */ |
251 | fsb = cpu_khz / current_multiplier; | 325 | fsb = cpu_khz / current_multiplier; |
326 | |||
327 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
328 | /* Check for ACPI processor speed limit */ | ||
329 | if (!ignore_acpi_limit && !eps_acpi_init()) { | ||
330 | if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) { | ||
331 | printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n", | ||
332 | limit/1000000, | ||
333 | (limit%1000000)/10000); | ||
334 | eps_acpi_exit(policy); | ||
335 | /* Check if max_multiplier is in BIOS limits */ | ||
336 | if (limit && max_multiplier * fsb > limit) { | ||
337 | printk(KERN_INFO "eps: Aborting.\n"); | ||
338 | return -EINVAL; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | #endif | ||
343 | |||
344 | /* Allow user to set lower maximum voltage then that reported | ||
345 | * by processor */ | ||
346 | if (brand == EPS_BRAND_C7M && set_max_voltage) { | ||
347 | u32 v; | ||
348 | |||
349 | /* Change mV to something hardware can use */ | ||
350 | v = (set_max_voltage - 700) / 16; | ||
351 | /* Check if voltage is within limits */ | ||
352 | if (v >= min_voltage && v <= max_voltage) { | ||
353 | printk(KERN_INFO "eps: Setting %dmV as maximum.\n", | ||
354 | v * 16 + 700); | ||
355 | max_voltage = v; | ||
356 | } | ||
357 | } | ||
358 | |||
252 | /* Calc number of p-states supported */ | 359 | /* Calc number of p-states supported */ |
253 | if (brand == EPS_BRAND_C7M) | 360 | if (brand == EPS_BRAND_C7M) |
254 | states = max_multiplier - min_multiplier + 1; | 361 | states = max_multiplier - min_multiplier + 1; |
@@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) | |||
265 | 372 | ||
266 | /* Copy basic values */ | 373 | /* Copy basic values */ |
267 | centaur->fsb = fsb; | 374 | centaur->fsb = fsb; |
375 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
376 | centaur->bios_limit = limit; | ||
377 | #endif | ||
268 | 378 | ||
269 | /* Fill frequency and MSR value table */ | 379 | /* Fill frequency and MSR value table */ |
270 | f_table = ¢aur->freq_table[0]; | 380 | f_table = ¢aur->freq_table[0]; |
@@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) | |||
303 | static int eps_cpu_exit(struct cpufreq_policy *policy) | 413 | static int eps_cpu_exit(struct cpufreq_policy *policy) |
304 | { | 414 | { |
305 | unsigned int cpu = policy->cpu; | 415 | unsigned int cpu = policy->cpu; |
306 | struct eps_cpu_data *centaur; | ||
307 | u32 lo, hi; | ||
308 | 416 | ||
309 | if (eps_cpu[cpu] == NULL) | ||
310 | return -ENODEV; | ||
311 | centaur = eps_cpu[cpu]; | ||
312 | |||
313 | /* Get max frequency */ | ||
314 | rdmsr(MSR_IA32_PERF_STATUS, lo, hi); | ||
315 | /* Set max frequency */ | ||
316 | eps_set_state(centaur, cpu, hi & 0xffff); | ||
317 | /* Bye */ | 417 | /* Bye */ |
318 | cpufreq_frequency_table_put_attr(policy->cpu); | 418 | cpufreq_frequency_table_put_attr(policy->cpu); |
319 | kfree(eps_cpu[cpu]); | 419 | kfree(eps_cpu[cpu]); |
@@ -359,6 +459,19 @@ static void __exit eps_exit(void) | |||
359 | cpufreq_unregister_driver(&eps_driver); | 459 | cpufreq_unregister_driver(&eps_driver); |
360 | } | 460 | } |
361 | 461 | ||
462 | /* Allow user to overclock his machine or to change frequency to higher after | ||
463 | * unloading module */ | ||
464 | module_param(freq_failsafe_off, int, 0644); | ||
465 | MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check"); | ||
466 | module_param(voltage_failsafe_off, int, 0644); | ||
467 | MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check"); | ||
468 | #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE | ||
469 | module_param(ignore_acpi_limit, int, 0644); | ||
470 | MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit"); | ||
471 | #endif | ||
472 | module_param(set_max_voltage, int, 0644); | ||
473 | MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only"); | ||
474 | |||
362 | MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>"); | 475 | MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>"); |
363 | MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's."); | 476 | MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's."); |
364 | MODULE_LICENSE("GPL"); | 477 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index b7c3a84c4cfa..ab9741fab92e 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/regulator/consumer.h> | 18 | #include <linux/regulator/consumer.h> |
19 | #include <linux/cpufreq.h> | 19 | #include <linux/cpufreq.h> |
20 | #include <linux/notifier.h> | ||
21 | #include <linux/suspend.h> | ||
20 | 22 | ||
21 | #include <mach/map.h> | 23 | #include <mach/map.h> |
22 | #include <mach/regs-clock.h> | 24 | #include <mach/regs-clock.h> |
@@ -36,6 +38,10 @@ static struct regulator *int_regulator; | |||
36 | static struct cpufreq_freqs freqs; | 38 | static struct cpufreq_freqs freqs; |
37 | static unsigned int memtype; | 39 | static unsigned int memtype; |
38 | 40 | ||
41 | static unsigned int locking_frequency; | ||
42 | static bool frequency_locked; | ||
43 | static DEFINE_MUTEX(cpufreq_lock); | ||
44 | |||
39 | enum exynos4_memory_type { | 45 | enum exynos4_memory_type { |
40 | DDR2 = 4, | 46 | DDR2 = 4, |
41 | LPDDR2, | 47 | LPDDR2, |
@@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy, | |||
405 | { | 411 | { |
406 | unsigned int index, old_index; | 412 | unsigned int index, old_index; |
407 | unsigned int arm_volt, int_volt; | 413 | unsigned int arm_volt, int_volt; |
414 | int err = -EINVAL; | ||
408 | 415 | ||
409 | freqs.old = exynos4_getspeed(policy->cpu); | 416 | freqs.old = exynos4_getspeed(policy->cpu); |
410 | 417 | ||
418 | mutex_lock(&cpufreq_lock); | ||
419 | |||
420 | if (frequency_locked && target_freq != locking_frequency) { | ||
421 | err = -EAGAIN; | ||
422 | goto out; | ||
423 | } | ||
424 | |||
411 | if (cpufreq_frequency_table_target(policy, exynos4_freq_table, | 425 | if (cpufreq_frequency_table_target(policy, exynos4_freq_table, |
412 | freqs.old, relation, &old_index)) | 426 | freqs.old, relation, &old_index)) |
413 | return -EINVAL; | 427 | goto out; |
414 | 428 | ||
415 | if (cpufreq_frequency_table_target(policy, exynos4_freq_table, | 429 | if (cpufreq_frequency_table_target(policy, exynos4_freq_table, |
416 | target_freq, relation, &index)) | 430 | target_freq, relation, &index)) |
417 | return -EINVAL; | 431 | goto out; |
432 | |||
433 | err = 0; | ||
418 | 434 | ||
419 | freqs.new = exynos4_freq_table[index].frequency; | 435 | freqs.new = exynos4_freq_table[index].frequency; |
420 | freqs.cpu = policy->cpu; | 436 | freqs.cpu = policy->cpu; |
421 | 437 | ||
422 | if (freqs.new == freqs.old) | 438 | if (freqs.new == freqs.old) |
423 | return 0; | 439 | goto out; |
424 | 440 | ||
425 | /* get the voltage value */ | 441 | /* get the voltage value */ |
426 | arm_volt = exynos4_volt_table[index].arm_volt; | 442 | arm_volt = exynos4_volt_table[index].arm_volt; |
@@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy, | |||
447 | 463 | ||
448 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 464 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
449 | 465 | ||
450 | return 0; | 466 | out: |
467 | mutex_unlock(&cpufreq_lock); | ||
468 | return err; | ||
451 | } | 469 | } |
452 | 470 | ||
453 | #ifdef CONFIG_PM | 471 | #ifdef CONFIG_PM |
472 | /* | ||
473 | * These suspend/resume are used as syscore_ops, it is already too | ||
474 | * late to set regulator voltages at this stage. | ||
475 | */ | ||
454 | static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy) | 476 | static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy) |
455 | { | 477 | { |
456 | return 0; | 478 | return 0; |
@@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy) | |||
462 | } | 484 | } |
463 | #endif | 485 | #endif |
464 | 486 | ||
487 | /** | ||
488 | * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume | ||
489 | * context | ||
490 | * @notifier | ||
491 | * @pm_event | ||
492 | * @v | ||
493 | * | ||
494 | * While frequency_locked == true, target() ignores every frequency but | ||
495 | * locking_frequency. The locking_frequency value is the initial frequency, | ||
496 | * which is set by the bootloader. In order to eliminate possible | ||
497 | * inconsistency in clock values, we save and restore frequencies during | ||
498 | * suspend and resume and block CPUFREQ activities. Note that the standard | ||
499 | * suspend/resume cannot be used as they are too deep (syscore_ops) for | ||
500 | * regulator actions. | ||
501 | */ | ||
502 | static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier, | ||
503 | unsigned long pm_event, void *v) | ||
504 | { | ||
505 | struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ | ||
506 | static unsigned int saved_frequency; | ||
507 | unsigned int temp; | ||
508 | |||
509 | mutex_lock(&cpufreq_lock); | ||
510 | switch (pm_event) { | ||
511 | case PM_SUSPEND_PREPARE: | ||
512 | if (frequency_locked) | ||
513 | goto out; | ||
514 | frequency_locked = true; | ||
515 | |||
516 | if (locking_frequency) { | ||
517 | saved_frequency = exynos4_getspeed(0); | ||
518 | |||
519 | mutex_unlock(&cpufreq_lock); | ||
520 | exynos4_target(policy, locking_frequency, | ||
521 | CPUFREQ_RELATION_H); | ||
522 | mutex_lock(&cpufreq_lock); | ||
523 | } | ||
524 | |||
525 | break; | ||
526 | case PM_POST_SUSPEND: | ||
527 | |||
528 | if (saved_frequency) { | ||
529 | /* | ||
530 | * While frequency_locked, only locking_frequency | ||
531 | * is valid for target(). In order to use | ||
532 | * saved_frequency while keeping frequency_locked, | ||
533 | * we temporarly overwrite locking_frequency. | ||
534 | */ | ||
535 | temp = locking_frequency; | ||
536 | locking_frequency = saved_frequency; | ||
537 | |||
538 | mutex_unlock(&cpufreq_lock); | ||
539 | exynos4_target(policy, locking_frequency, | ||
540 | CPUFREQ_RELATION_H); | ||
541 | mutex_lock(&cpufreq_lock); | ||
542 | |||
543 | locking_frequency = temp; | ||
544 | } | ||
545 | |||
546 | frequency_locked = false; | ||
547 | break; | ||
548 | } | ||
549 | out: | ||
550 | mutex_unlock(&cpufreq_lock); | ||
551 | |||
552 | return NOTIFY_OK; | ||
553 | } | ||
554 | |||
555 | static struct notifier_block exynos4_cpufreq_nb = { | ||
556 | .notifier_call = exynos4_cpufreq_pm_notifier, | ||
557 | }; | ||
558 | |||
465 | static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) | 559 | static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) |
466 | { | 560 | { |
561 | int ret; | ||
562 | |||
467 | policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu); | 563 | policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu); |
468 | 564 | ||
469 | cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); | 565 | cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); |
@@ -479,16 +575,35 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
479 | */ | 575 | */ |
480 | cpumask_setall(policy->cpus); | 576 | cpumask_setall(policy->cpus); |
481 | 577 | ||
482 | return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table); | 578 | ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table); |
579 | if (ret) | ||
580 | return ret; | ||
581 | |||
582 | cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); | ||
583 | |||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy) | ||
588 | { | ||
589 | cpufreq_frequency_table_put_attr(policy->cpu); | ||
590 | return 0; | ||
483 | } | 591 | } |
484 | 592 | ||
593 | static struct freq_attr *exynos4_cpufreq_attr[] = { | ||
594 | &cpufreq_freq_attr_scaling_available_freqs, | ||
595 | NULL, | ||
596 | }; | ||
597 | |||
485 | static struct cpufreq_driver exynos4_driver = { | 598 | static struct cpufreq_driver exynos4_driver = { |
486 | .flags = CPUFREQ_STICKY, | 599 | .flags = CPUFREQ_STICKY, |
487 | .verify = exynos4_verify_speed, | 600 | .verify = exynos4_verify_speed, |
488 | .target = exynos4_target, | 601 | .target = exynos4_target, |
489 | .get = exynos4_getspeed, | 602 | .get = exynos4_getspeed, |
490 | .init = exynos4_cpufreq_cpu_init, | 603 | .init = exynos4_cpufreq_cpu_init, |
604 | .exit = exynos4_cpufreq_cpu_exit, | ||
491 | .name = "exynos4_cpufreq", | 605 | .name = "exynos4_cpufreq", |
606 | .attr = exynos4_cpufreq_attr, | ||
492 | #ifdef CONFIG_PM | 607 | #ifdef CONFIG_PM |
493 | .suspend = exynos4_cpufreq_suspend, | 608 | .suspend = exynos4_cpufreq_suspend, |
494 | .resume = exynos4_cpufreq_resume, | 609 | .resume = exynos4_cpufreq_resume, |
@@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void) | |||
501 | if (IS_ERR(cpu_clk)) | 616 | if (IS_ERR(cpu_clk)) |
502 | return PTR_ERR(cpu_clk); | 617 | return PTR_ERR(cpu_clk); |
503 | 618 | ||
619 | locking_frequency = exynos4_getspeed(0); | ||
620 | |||
504 | moutcore = clk_get(NULL, "moutcore"); | 621 | moutcore = clk_get(NULL, "moutcore"); |
505 | if (IS_ERR(moutcore)) | 622 | if (IS_ERR(moutcore)) |
506 | goto out; | 623 | goto out; |
@@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void) | |||
540 | printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); | 657 | printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); |
541 | } | 658 | } |
542 | 659 | ||
660 | register_pm_notifier(&exynos4_cpufreq_nb); | ||
661 | |||
543 | return cpufreq_register_driver(&exynos4_driver); | 662 | return cpufreq_register_driver(&exynos4_driver); |
544 | 663 | ||
545 | out: | 664 | out: |