diff options
| -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 8e89dcf9d94..edaa987621e 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 35a257dd4bb..4bd6815d317 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 b7c3a84c4cf..ab9741fab92 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: |
