aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorQuentin Perret <quentin.perret@arm.com>2019-02-04 06:09:52 -0500
committerViresh Kumar <viresh.kumar@linaro.org>2019-02-06 23:25:12 -0500
commit3c429851f9986103ce99c5747a795c6a6c572310 (patch)
tree81d91b876700dba706ad39454076f604cdc7b1c8 /drivers
parent2516d670052d5c3eb3253e5c815ff85bf0daf35a (diff)
cpufreq: scmi: Register an Energy Model
The Energy Model (EM) framework provides an API to register the active power of CPUs. Call this API from the scmi-cpufreq driver by using the power costs obtained from firmware. This is done to ensure interested subsystems (the task scheduler, for example) can make use of the EM when available. Signed-off-by: Quentin Perret <quentin.perret@arm.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpufreq/scmi-cpufreq.c39
1 files changed, 36 insertions, 3 deletions
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
index 50b1551ba894..44449ff8ad47 100644
--- a/drivers/cpufreq/scmi-cpufreq.c
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -12,6 +12,7 @@
12#include <linux/cpufreq.h> 12#include <linux/cpufreq.h>
13#include <linux/cpumask.h> 13#include <linux/cpumask.h>
14#include <linux/cpu_cooling.h> 14#include <linux/cpu_cooling.h>
15#include <linux/energy_model.h>
15#include <linux/export.h> 16#include <linux/export.h>
16#include <linux/module.h> 17#include <linux/module.h>
17#include <linux/pm_opp.h> 18#include <linux/pm_opp.h>
@@ -103,13 +104,42 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
103 return 0; 104 return 0;
104} 105}
105 106
107static int __maybe_unused
108scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu)
109{
110 struct device *cpu_dev = get_cpu_device(cpu);
111 unsigned long Hz;
112 int ret, domain;
113
114 if (!cpu_dev) {
115 pr_err("failed to get cpu%d device\n", cpu);
116 return -ENODEV;
117 }
118
119 domain = handle->perf_ops->device_domain_id(cpu_dev);
120 if (domain < 0)
121 return domain;
122
123 /* Get the power cost of the performance domain. */
124 Hz = *KHz * 1000;
125 ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power);
126 if (ret)
127 return ret;
128
129 /* The EM framework specifies the frequency in KHz. */
130 *KHz = Hz / 1000;
131
132 return 0;
133}
134
106static int scmi_cpufreq_init(struct cpufreq_policy *policy) 135static int scmi_cpufreq_init(struct cpufreq_policy *policy)
107{ 136{
108 int ret; 137 int ret, nr_opp;
109 unsigned int latency; 138 unsigned int latency;
110 struct device *cpu_dev; 139 struct device *cpu_dev;
111 struct scmi_data *priv; 140 struct scmi_data *priv;
112 struct cpufreq_frequency_table *freq_table; 141 struct cpufreq_frequency_table *freq_table;
142 struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
113 143
114 cpu_dev = get_cpu_device(policy->cpu); 144 cpu_dev = get_cpu_device(policy->cpu);
115 if (!cpu_dev) { 145 if (!cpu_dev) {
@@ -136,8 +166,8 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
136 return ret; 166 return ret;
137 } 167 }
138 168
139 ret = dev_pm_opp_get_opp_count(cpu_dev); 169 nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
140 if (ret <= 0) { 170 if (nr_opp <= 0) {
141 dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); 171 dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
142 ret = -EPROBE_DEFER; 172 ret = -EPROBE_DEFER;
143 goto out_free_opp; 173 goto out_free_opp;
@@ -171,6 +201,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
171 policy->cpuinfo.transition_latency = latency; 201 policy->cpuinfo.transition_latency = latency;
172 202
173 policy->fast_switch_possible = true; 203 policy->fast_switch_possible = true;
204
205 em_register_perf_domain(policy->cpus, nr_opp, &em_cb);
206
174 return 0; 207 return 0;
175 208
176out_free_priv: 209out_free_priv: