cpufreq: arm_big_little: Register an Energy Model
authorDietmar Eggemann <dietmar.eggemann@arm.com>
Fri, 1 Jun 2018 13:27:24 +0000 (14:27 +0100)
committerDouglas RAILLARD <douglas.raillard@arm.com>
Tue, 14 Aug 2018 15:32:33 +0000 (16:32 +0100)
The Energy Model framework provides an API to register the active power
of CPUs. This commit calls this API from the scpi-cpufreq driver which
can rely on the power estimation helper provided by PM_OPP.

Todo: Check if driver can handle -EPROBE_DEFER and if the call to
dev_pm_opp_get_opp_count() id realy necessary.

Change-Id: Ia808262ef6c9f2cc7819a83e8eb2f602454edfa3
Signed-off-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
[ Removed the dependency on dev_pm_opp_of_estimate_power() ]
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
drivers/cpufreq/arm_big_little.c

index cf62a1f64dd71d457ae45e40e7ec84d1ec35e23a..e53cc0da715079ba00a4a1ede7734b182f044fd5 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/cpufreq.h>
 #include <linux/cpumask.h>
 #include <linux/cpu_cooling.h>
+#include <linux/energy_model.h>
 #include <linux/export.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
@@ -453,9 +454,48 @@ put_clusters:
        return ret;
 }
 
+static int of_est_power(unsigned long *mW, unsigned long *KHz, int cpu)
+{
+       unsigned long mV, Hz, MHz;
+       struct device *cpu_dev;
+       struct dev_pm_opp *opp;
+       struct device_node *np;
+       u32 cap;
+       u64 tmp;
+
+       cpu_dev = get_cpu_device(cpu);
+       if (!cpu_dev)
+               return -ENODEV;
+
+       np = of_node_get(cpu_dev->of_node);
+       if (!np)
+               return -EINVAL;
+
+       if (of_property_read_u32(np, "dynamic-power-coefficient", &cap))
+               return -EINVAL;
+
+       Hz = *KHz * 1000;
+       opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz);
+       if (IS_ERR(opp))
+               return -EINVAL;
+
+       mV = dev_pm_opp_get_voltage(opp) / 1000;
+       dev_pm_opp_put(opp);
+
+       MHz = Hz / 1000000;
+       tmp = (u64)cap * mV * mV * MHz;
+       do_div(tmp, 1000000000);
+
+       *mW = (unsigned long)tmp;
+       *KHz = Hz / 1000;
+
+       return 0;
+}
+
 /* Per-CPU initialization */
 static int bL_cpufreq_init(struct cpufreq_policy *policy)
 {
+       struct em_data_callback em_cb = EM_DATA_CB(of_est_power);
        u32 cur_cluster = cpu_to_cluster(policy->cpu);
        struct device *cpu_dev;
        int ret;
@@ -487,6 +527,14 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
        policy->cpuinfo.transition_latency =
                                arm_bL_ops->get_transition_latency(cpu_dev);
 
+       ret = dev_pm_opp_get_opp_count(cpu_dev);
+       if (ret <= 0) {
+               dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
+               return -EPROBE_DEFER;
+       }
+
+       em_register_freq_domain(policy->cpus, ret, &em_cb);
+
        if (is_bL_switching_enabled())
                per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);