diff options
| -rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c | 106 |
1 files changed, 102 insertions, 4 deletions
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 6f887573ce94..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,6 +484,78 @@ 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 | { |
| 467 | int ret; | 561 | int ret; |
| @@ -522,6 +616,8 @@ static int __init exynos4_cpufreq_init(void) | |||
| 522 | if (IS_ERR(cpu_clk)) | 616 | if (IS_ERR(cpu_clk)) |
| 523 | return PTR_ERR(cpu_clk); | 617 | return PTR_ERR(cpu_clk); |
| 524 | 618 | ||
| 619 | locking_frequency = exynos4_getspeed(0); | ||
| 620 | |||
| 525 | moutcore = clk_get(NULL, "moutcore"); | 621 | moutcore = clk_get(NULL, "moutcore"); |
| 526 | if (IS_ERR(moutcore)) | 622 | if (IS_ERR(moutcore)) |
| 527 | goto out; | 623 | goto out; |
| @@ -561,6 +657,8 @@ static int __init exynos4_cpufreq_init(void) | |||
| 561 | printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); | 657 | printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); |
| 562 | } | 658 | } |
| 563 | 659 | ||
| 660 | register_pm_notifier(&exynos4_cpufreq_nb); | ||
| 661 | |||
| 564 | return cpufreq_register_driver(&exynos4_driver); | 662 | return cpufreq_register_driver(&exynos4_driver); |
| 565 | 663 | ||
| 566 | out: | 664 | out: |
