HID: logitech: fix Dual Action gamepad support
[pandora-kernel.git] / drivers / cpufreq / omap-cpufreq.c
index 9f5d636..d9d78ba 100644 (file)
@@ -38,6 +38,9 @@
 
 #include <mach/hardware.h>
 
+/* OPP tolerance in percentage */
+#define        OPP_TOLERANCE   4
+
 #ifdef CONFIG_SMP
 struct lpj_info {
        unsigned long   ref;
@@ -54,6 +57,7 @@ static struct clk *mpu_clk;
 static char *mpu_clk_name;
 static struct device *mpu_dev;
 static struct regulator *mpu_reg;
+static unsigned long freq_max, volt_max;
 
 static int omap_verify_speed(struct cpufreq_policy *policy)
 {
@@ -134,7 +138,7 @@ static int omap_target(struct cpufreq_policy *policy,
 
        /* scaling up?  scale voltage before frequency */
        if (mpu_reg && (freqs.new > freqs.old)) {
-               r = regulator_set_voltage(mpu_reg, volt, volt);
+               r = regulator_set_voltage(mpu_reg, volt, volt_max);
                if (r < 0) {
                        dev_warn(mpu_dev, "%s: unable to scale voltage up.\n",
                                 __func__);
@@ -147,7 +151,7 @@ static int omap_target(struct cpufreq_policy *policy,
 
        /* scaling down?  scale voltage after frequency */
        if (mpu_reg && (freqs.new < freqs.old)) {
-               r = regulator_set_voltage(mpu_reg, volt, volt);
+               r = regulator_set_voltage(mpu_reg, volt, volt_max);
                if (r < 0) {
                        dev_warn(mpu_dev, "%s: unable to scale voltage down.\n",
                                 __func__);
@@ -200,6 +204,107 @@ static inline void freq_table_free(void)
                opp_free_cpufreq_table(mpu_dev, &freq_table);
 }
 
+/* force-update hack */
+static struct notifier_block omap_freq_nb;
+static struct cpufreq_policy *omap_freq_policy;
+
+static void check_max_freq(unsigned long freq)
+{
+       unsigned long volt;
+       struct opp *opp;
+
+       freq *= 1000;
+
+       if (freq <= freq_max)
+               return;
+
+       opp = opp_find_freq_ceil(mpu_dev, &freq);
+       if (IS_ERR(opp)) {
+               dev_err(mpu_dev, "%s: unable to find MPU OPP for %ld\n",
+                               __func__, freq);
+               return;
+       }
+
+       volt = opp_get_voltage(opp);
+       volt += volt * OPP_TOLERANCE / 100;
+
+       if (volt > volt_max) {
+               volt_max = volt;
+               freq_max = freq;
+       }
+}
+
+static int freq_notifier_call(struct notifier_block *nb, unsigned long type,
+                             void *devp)
+{
+       static DEFINE_SPINLOCK(lock);
+       struct cpufreq_frequency_table *new_freq_table, *old_freq_table;
+       unsigned long flags;
+       int ret;
+
+       ret = opp_init_cpufreq_table(mpu_dev, &new_freq_table);
+       if (ret) {
+               dev_err(mpu_dev, "%s: failed to create cpufreq_table: %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       /* FIXME: use proper locks instead of these hacks */
+       spin_lock_irqsave(&lock, flags);
+       old_freq_table = freq_table;
+       freq_table = new_freq_table;
+       spin_unlock_irqrestore(&lock, flags);
+       msleep(1);
+       opp_free_cpufreq_table(mpu_dev, &old_freq_table);
+
+       if (omap_freq_policy == NULL) {
+               dev_err(mpu_dev, "%s: omap_freq_policy is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       cpufreq_frequency_table_get_attr(freq_table, omap_freq_policy->cpu);
+
+       ret = cpufreq_frequency_table_cpuinfo(omap_freq_policy, freq_table);
+       if (ret)
+               dev_err(mpu_dev, "%s: cpufreq_frequency_table_cpuinfo: %d\n",
+                       __func__, ret);
+       omap_freq_policy->user_policy.min = omap_freq_policy->cpuinfo.min_freq;
+       omap_freq_policy->user_policy.max = omap_freq_policy->cpuinfo.max_freq;
+
+       check_max_freq(omap_freq_policy->cpuinfo.max_freq);
+
+       return ret;
+}
+
+static void freq_register_opp_notifier(struct device *dev,
+                                      struct cpufreq_policy *policy)
+{
+       struct srcu_notifier_head *nh = opp_get_notifier(dev);
+       int ret;
+
+       omap_freq_policy = policy;
+
+       if (IS_ERR(nh)) {
+               ret = PTR_ERR(nh);
+               goto out;
+       }
+       omap_freq_nb.notifier_call = freq_notifier_call;
+       ret = srcu_notifier_chain_register(nh, &omap_freq_nb);
+out:
+       if (ret != 0)
+               dev_err(mpu_dev, "%s: failed to register notifier: %d\n",
+                               __func__, ret);
+}
+
+static void freq_unregister_opp_notifier(struct device *dev)
+{
+       struct srcu_notifier_head *nh = opp_get_notifier(dev);
+
+       if (IS_ERR(nh))
+               return;
+       srcu_notifier_chain_unregister(nh, &omap_freq_nb);
+}
+
 static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
 {
        int result = 0;
@@ -234,6 +339,8 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
        policy->max = policy->cpuinfo.max_freq;
        policy->cur = omap_getspeed(policy->cpu);
 
+       check_max_freq(policy->cpuinfo.max_freq);
+
        /*
         * On OMAP SMP configuartion, both processors share the voltage
         * and clock. So both CPUs needs to be scaled together and hence
@@ -249,6 +356,8 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
        /* FIXME: what's the actual transition time? */
        policy->cpuinfo.transition_latency = 300 * 1000;
 
+       freq_register_opp_notifier(mpu_dev, policy);
+
        return 0;
 
 fail_table:
@@ -260,6 +369,7 @@ fail_ck:
 
 static int omap_cpu_exit(struct cpufreq_policy *policy)
 {
+       freq_unregister_opp_notifier(mpu_dev);
        freq_table_free();
        clk_put(mpu_clk);
        return 0;