]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/cpufreq/cpufreq-dt.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
[karo-tx-linux.git] / drivers / cpufreq / cpufreq-dt.c
index 6bbb8b913446bf4ce7131c4fb9448db2ac7721c3..23aaf40cf37f5b6b7e5416a0125e62d581864079 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/cpu.h>
 #include <linux/cpu_cooling.h>
 #include <linux/cpufreq.h>
+#include <linux/cpufreq-dt.h>
 #include <linux/cpumask.h>
 #include <linux/err.h>
 #include <linux/module.h>
@@ -146,8 +147,8 @@ try_again:
                        goto try_again;
                }
 
-               dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n",
-                        cpu, PTR_ERR(cpu_reg));
+               dev_dbg(cpu_dev, "no regulator for cpu%d: %ld\n",
+                       cpu, PTR_ERR(cpu_reg));
        }
 
        cpu_clk = clk_get(cpu_dev, NULL);
@@ -178,6 +179,7 @@ try_again:
 
 static int cpufreq_init(struct cpufreq_policy *policy)
 {
+       struct cpufreq_dt_platform_data *pd;
        struct cpufreq_frequency_table *freq_table;
        struct thermal_cooling_device *cdev;
        struct device_node *np;
@@ -185,6 +187,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
        struct device *cpu_dev;
        struct regulator *cpu_reg;
        struct clk *cpu_clk;
+       unsigned long min_uV = ~0, max_uV = 0;
        unsigned int transition_latency;
        int ret;
 
@@ -204,16 +207,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
        /* OPPs might be populated at runtime, don't check for error here */
        of_init_opp_table(cpu_dev);
 
-       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
-       if (ret) {
-               dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
-               goto out_put_node;
-       }
-
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                ret = -ENOMEM;
-               goto out_free_table;
+               goto out_put_node;
        }
 
        of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
@@ -222,30 +219,51 @@ static int cpufreq_init(struct cpufreq_policy *policy)
                transition_latency = CPUFREQ_ETERNAL;
 
        if (!IS_ERR(cpu_reg)) {
-               struct dev_pm_opp *opp;
-               unsigned long min_uV, max_uV;
-               int i;
+               unsigned long opp_freq = 0;
 
                /*
-                * OPP is maintained in order of increasing frequency, and
-                * freq_table initialised from OPP is therefore sorted in the
-                * same order.
+                * Disable any OPPs where the connected regulator isn't able to
+                * provide the specified voltage and record minimum and maximum
+                * voltage levels.
                 */
-               for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
-                       ;
-               rcu_read_lock();
-               opp = dev_pm_opp_find_freq_exact(cpu_dev,
-                               freq_table[0].frequency * 1000, true);
-               min_uV = dev_pm_opp_get_voltage(opp);
-               opp = dev_pm_opp_find_freq_exact(cpu_dev,
-                               freq_table[i-1].frequency * 1000, true);
-               max_uV = dev_pm_opp_get_voltage(opp);
-               rcu_read_unlock();
+               while (1) {
+                       struct dev_pm_opp *opp;
+                       unsigned long opp_uV, tol_uV;
+
+                       rcu_read_lock();
+                       opp = dev_pm_opp_find_freq_ceil(cpu_dev, &opp_freq);
+                       if (IS_ERR(opp)) {
+                               rcu_read_unlock();
+                               break;
+                       }
+                       opp_uV = dev_pm_opp_get_voltage(opp);
+                       rcu_read_unlock();
+
+                       tol_uV = opp_uV * priv->voltage_tolerance / 100;
+                       if (regulator_is_supported_voltage(cpu_reg, opp_uV,
+                                                          opp_uV + tol_uV)) {
+                               if (opp_uV < min_uV)
+                                       min_uV = opp_uV;
+                               if (opp_uV > max_uV)
+                                       max_uV = opp_uV;
+                       } else {
+                               dev_pm_opp_disable(cpu_dev, opp_freq);
+                       }
+
+                       opp_freq++;
+               }
+
                ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
                if (ret > 0)
                        transition_latency += ret * 1000;
        }
 
+       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+       if (ret) {
+               pr_err("failed to init cpufreq table: %d\n", ret);
+               goto out_free_priv;
+       }
+
        /*
         * For now, just loading the cooling device;
         * thermal DT code takes care of matching them.
@@ -265,9 +283,18 @@ static int cpufreq_init(struct cpufreq_policy *policy)
        policy->driver_data = priv;
 
        policy->clk = cpu_clk;
-       ret = cpufreq_generic_init(policy, freq_table, transition_latency);
-       if (ret)
+       ret = cpufreq_table_validate_and_show(policy, freq_table);
+       if (ret) {
+               dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
+                       ret);
                goto out_cooling_unregister;
+       }
+
+       policy->cpuinfo.transition_latency = transition_latency;
+
+       pd = cpufreq_get_driver_data();
+       if (!pd || !pd->independent_clocks)
+               cpumask_setall(policy->cpus);
 
        of_node_put(np);
 
@@ -275,9 +302,9 @@ static int cpufreq_init(struct cpufreq_policy *policy)
 
 out_cooling_unregister:
        cpufreq_cooling_unregister(priv->cdev);
-       kfree(priv);
-out_free_table:
        dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_free_priv:
+       kfree(priv);
 out_put_node:
        of_node_put(np);
 out_put_reg_clk:
@@ -335,6 +362,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
        if (!IS_ERR(cpu_reg))
                regulator_put(cpu_reg);
 
+       dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev);
+
        ret = cpufreq_register_driver(&dt_cpufreq_driver);
        if (ret)
                dev_err(cpu_dev, "failed register driver: %d\n", ret);