media: v4l2-compat-ioctl32: Copy v4l2_window->global_alpha
[pandora-kernel.git] / drivers / cpufreq / exynos4210-cpufreq.c
index b7c3a84..ab9741f 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/slab.h>
 #include <linux/regulator/consumer.h>
 #include <linux/cpufreq.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
 
 #include <mach/map.h>
 #include <mach/regs-clock.h>
@@ -36,6 +38,10 @@ static struct regulator *int_regulator;
 static struct cpufreq_freqs freqs;
 static unsigned int memtype;
 
+static unsigned int locking_frequency;
+static bool frequency_locked;
+static DEFINE_MUTEX(cpufreq_lock);
+
 enum exynos4_memory_type {
        DDR2 = 4,
        LPDDR2,
@@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy,
 {
        unsigned int index, old_index;
        unsigned int arm_volt, int_volt;
+       int err = -EINVAL;
 
        freqs.old = exynos4_getspeed(policy->cpu);
 
+       mutex_lock(&cpufreq_lock);
+
+       if (frequency_locked && target_freq != locking_frequency) {
+               err = -EAGAIN;
+               goto out;
+       }
+
        if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
                                           freqs.old, relation, &old_index))
-               return -EINVAL;
+               goto out;
 
        if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
                                           target_freq, relation, &index))
-               return -EINVAL;
+               goto out;
+
+       err = 0;
 
        freqs.new = exynos4_freq_table[index].frequency;
        freqs.cpu = policy->cpu;
 
        if (freqs.new == freqs.old)
-               return 0;
+               goto out;
 
        /* get the voltage value */
        arm_volt = exynos4_volt_table[index].arm_volt;
@@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy,
 
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
-       return 0;
+out:
+       mutex_unlock(&cpufreq_lock);
+       return err;
 }
 
 #ifdef CONFIG_PM
+/*
+ * These suspend/resume are used as syscore_ops, it is already too
+ * late to set regulator voltages at this stage.
+ */
 static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
 {
        return 0;
@@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
 }
 #endif
 
+/**
+ * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
+ *                     context
+ * @notifier
+ * @pm_event
+ * @v
+ *
+ * While frequency_locked == true, target() ignores every frequency but
+ * locking_frequency. The locking_frequency value is the initial frequency,
+ * which is set by the bootloader. In order to eliminate possible
+ * inconsistency in clock values, we save and restore frequencies during
+ * suspend and resume and block CPUFREQ activities. Note that the standard
+ * suspend/resume cannot be used as they are too deep (syscore_ops) for
+ * regulator actions.
+ */
+static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier,
+                                      unsigned long pm_event, void *v)
+{
+       struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
+       static unsigned int saved_frequency;
+       unsigned int temp;
+
+       mutex_lock(&cpufreq_lock);
+       switch (pm_event) {
+       case PM_SUSPEND_PREPARE:
+               if (frequency_locked)
+                       goto out;
+               frequency_locked = true;
+
+               if (locking_frequency) {
+                       saved_frequency = exynos4_getspeed(0);
+
+                       mutex_unlock(&cpufreq_lock);
+                       exynos4_target(policy, locking_frequency,
+                                      CPUFREQ_RELATION_H);
+                       mutex_lock(&cpufreq_lock);
+               }
+
+               break;
+       case PM_POST_SUSPEND:
+
+               if (saved_frequency) {
+                       /*
+                        * While frequency_locked, only locking_frequency
+                        * is valid for target(). In order to use
+                        * saved_frequency while keeping frequency_locked,
+                        * we temporarly overwrite locking_frequency.
+                        */
+                       temp = locking_frequency;
+                       locking_frequency = saved_frequency;
+
+                       mutex_unlock(&cpufreq_lock);
+                       exynos4_target(policy, locking_frequency,
+                                      CPUFREQ_RELATION_H);
+                       mutex_lock(&cpufreq_lock);
+
+                       locking_frequency = temp;
+               }
+
+               frequency_locked = false;
+               break;
+       }
+out:
+       mutex_unlock(&cpufreq_lock);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block exynos4_cpufreq_nb = {
+       .notifier_call = exynos4_cpufreq_pm_notifier,
+};
+
 static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
+       int ret;
+
        policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
 
        cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
@@ -479,16 +575,35 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
         */
        cpumask_setall(policy->cpus);
 
-       return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
+       ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
+       if (ret)
+               return ret;
+
+       cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
+
+       return 0;
+}
+
+static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+       cpufreq_frequency_table_put_attr(policy->cpu);
+       return 0;
 }
 
+static struct freq_attr *exynos4_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
 static struct cpufreq_driver exynos4_driver = {
        .flags          = CPUFREQ_STICKY,
        .verify         = exynos4_verify_speed,
        .target         = exynos4_target,
        .get            = exynos4_getspeed,
        .init           = exynos4_cpufreq_cpu_init,
+       .exit           = exynos4_cpufreq_cpu_exit,
        .name           = "exynos4_cpufreq",
+       .attr           = exynos4_cpufreq_attr,
 #ifdef CONFIG_PM
        .suspend        = exynos4_cpufreq_suspend,
        .resume         = exynos4_cpufreq_resume,
@@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void)
        if (IS_ERR(cpu_clk))
                return PTR_ERR(cpu_clk);
 
+       locking_frequency = exynos4_getspeed(0);
+
        moutcore = clk_get(NULL, "moutcore");
        if (IS_ERR(moutcore))
                goto out;
@@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void)
                printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
        }
 
+       register_pm_notifier(&exynos4_cpufreq_nb);
+
        return cpufreq_register_driver(&exynos4_driver);
 
 out: