1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2020 Linaro Limited
5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
7 * The DTPM CPU is based on the energy model. It hooks the CPU in the
8 * DTPM tree which in turns update the power number by propagating the
9 * power number from the CPU energy model information to the parents.
11 * The association between the power and the performance state, allows
12 * to set the power of the CPU at the OPP granularity.
14 * The CPU hotplug is supported and the power numbers will be updated
15 * if a CPU is hot plugged / unplugged.
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 #include <linux/cpumask.h>
20 #include <linux/cpufreq.h>
21 #include <linux/cpuhotplug.h>
22 #include <linux/dtpm.h>
23 #include <linux/energy_model.h>
25 #include <linux/pm_qos.h>
26 #include <linux/slab.h>
30 struct freq_qos_request qos_req
;
34 static DEFINE_PER_CPU(struct dtpm_cpu
*, dtpm_per_cpu
);
36 static struct dtpm_cpu
*to_dtpm_cpu(struct dtpm
*dtpm
)
38 return container_of(dtpm
, struct dtpm_cpu
, dtpm
);
41 static u64
set_pd_power_limit(struct dtpm
*dtpm
, u64 power_limit
)
43 struct dtpm_cpu
*dtpm_cpu
= to_dtpm_cpu(dtpm
);
44 struct em_perf_domain
*pd
= em_cpu_get(dtpm_cpu
->cpu
);
45 struct em_perf_state
*table
;
50 nr_cpus
= cpumask_weight_and(cpu_online_mask
, to_cpumask(pd
->cpus
));
53 table
= em_perf_state_from_pd(pd
);
54 for (i
= 0; i
< pd
->nr_perf_states
; i
++) {
56 power
= table
[i
].power
* nr_cpus
;
58 if (power
> power_limit
)
62 freq
= table
[i
- 1].frequency
;
63 power_limit
= table
[i
- 1].power
* nr_cpus
;
66 freq_qos_update_request(&dtpm_cpu
->qos_req
, freq
);
71 static u64
scale_pd_power_uw(struct cpumask
*pd_mask
, u64 power
)
73 unsigned long max
, sum_util
= 0;
77 * The capacity is the same for all CPUs belonging to
78 * the same perf domain.
80 max
= arch_scale_cpu_capacity(cpumask_first(pd_mask
));
82 for_each_cpu_and(cpu
, pd_mask
, cpu_online_mask
)
83 sum_util
+= sched_cpu_util(cpu
);
85 return (power
* ((sum_util
<< 10) / max
)) >> 10;
88 static u64
get_pd_power_uw(struct dtpm
*dtpm
)
90 struct dtpm_cpu
*dtpm_cpu
= to_dtpm_cpu(dtpm
);
91 struct em_perf_state
*table
;
92 struct em_perf_domain
*pd
;
93 struct cpumask
*pd_mask
;
98 pd
= em_cpu_get(dtpm_cpu
->cpu
);
100 pd_mask
= em_span_cpus(pd
);
102 freq
= cpufreq_quick_get(dtpm_cpu
->cpu
);
105 table
= em_perf_state_from_pd(pd
);
106 for (i
= 0; i
< pd
->nr_perf_states
; i
++) {
108 if (table
[i
].frequency
< freq
)
111 power
= scale_pd_power_uw(pd_mask
, table
[i
].power
);
119 static int update_pd_power_uw(struct dtpm
*dtpm
)
121 struct dtpm_cpu
*dtpm_cpu
= to_dtpm_cpu(dtpm
);
122 struct em_perf_domain
*em
= em_cpu_get(dtpm_cpu
->cpu
);
123 struct em_perf_state
*table
;
126 nr_cpus
= cpumask_weight_and(cpu_online_mask
, to_cpumask(em
->cpus
));
129 table
= em_perf_state_from_pd(em
);
131 dtpm
->power_min
= table
[0].power
;
132 dtpm
->power_min
*= nr_cpus
;
134 dtpm
->power_max
= table
[em
->nr_perf_states
- 1].power
;
135 dtpm
->power_max
*= nr_cpus
;
142 static void pd_release(struct dtpm
*dtpm
)
144 struct dtpm_cpu
*dtpm_cpu
= to_dtpm_cpu(dtpm
);
145 struct cpufreq_policy
*policy
;
147 if (freq_qos_request_active(&dtpm_cpu
->qos_req
))
148 freq_qos_remove_request(&dtpm_cpu
->qos_req
);
150 policy
= cpufreq_cpu_get(dtpm_cpu
->cpu
);
152 for_each_cpu(dtpm_cpu
->cpu
, policy
->related_cpus
)
153 per_cpu(dtpm_per_cpu
, dtpm_cpu
->cpu
) = NULL
;
155 cpufreq_cpu_put(policy
);
161 static struct dtpm_ops dtpm_ops
= {
162 .set_power_uw
= set_pd_power_limit
,
163 .get_power_uw
= get_pd_power_uw
,
164 .update_power_uw
= update_pd_power_uw
,
165 .release
= pd_release
,
168 static int cpuhp_dtpm_cpu_offline(unsigned int cpu
)
170 struct dtpm_cpu
*dtpm_cpu
;
172 dtpm_cpu
= per_cpu(dtpm_per_cpu
, cpu
);
174 dtpm_update_power(&dtpm_cpu
->dtpm
);
179 static int cpuhp_dtpm_cpu_online(unsigned int cpu
)
181 struct dtpm_cpu
*dtpm_cpu
;
183 dtpm_cpu
= per_cpu(dtpm_per_cpu
, cpu
);
185 return dtpm_update_power(&dtpm_cpu
->dtpm
);
190 static int __dtpm_cpu_setup(int cpu
, struct dtpm
*parent
)
192 struct dtpm_cpu
*dtpm_cpu
;
193 struct cpufreq_policy
*policy
;
194 struct em_perf_state
*table
;
195 struct em_perf_domain
*pd
;
196 char name
[CPUFREQ_NAME_LEN
];
199 dtpm_cpu
= per_cpu(dtpm_per_cpu
, cpu
);
203 policy
= cpufreq_cpu_get(cpu
);
207 pd
= em_cpu_get(cpu
);
208 if (!pd
|| em_is_artificial(pd
)) {
213 dtpm_cpu
= kzalloc(sizeof(*dtpm_cpu
), GFP_KERNEL
);
219 dtpm_init(&dtpm_cpu
->dtpm
, &dtpm_ops
);
222 for_each_cpu(cpu
, policy
->related_cpus
)
223 per_cpu(dtpm_per_cpu
, cpu
) = dtpm_cpu
;
225 snprintf(name
, sizeof(name
), "cpu%d-cpufreq", dtpm_cpu
->cpu
);
227 ret
= dtpm_register(name
, &dtpm_cpu
->dtpm
, parent
);
229 goto out_kfree_dtpm_cpu
;
232 table
= em_perf_state_from_pd(pd
);
233 ret
= freq_qos_add_request(&policy
->constraints
,
234 &dtpm_cpu
->qos_req
, FREQ_QOS_MAX
,
235 table
[pd
->nr_perf_states
- 1].frequency
);
238 goto out_dtpm_unregister
;
240 cpufreq_cpu_put(policy
);
244 dtpm_unregister(&dtpm_cpu
->dtpm
);
248 for_each_cpu(cpu
, policy
->related_cpus
)
249 per_cpu(dtpm_per_cpu
, cpu
) = NULL
;
253 cpufreq_cpu_put(policy
);
257 static int dtpm_cpu_setup(struct dtpm
*dtpm
, struct device_node
*np
)
261 cpu
= of_cpu_node_to_id(np
);
265 return __dtpm_cpu_setup(cpu
, dtpm
);
268 static int dtpm_cpu_init(void)
273 * The callbacks at CPU hotplug time are calling
274 * dtpm_update_power() which in turns calls update_pd_power().
276 * The function update_pd_power() uses the online mask to
277 * figure out the power consumption limits.
279 * At CPUHP_AP_ONLINE_DYN, the CPU is present in the CPU
280 * online mask when the cpuhp_dtpm_cpu_online function is
281 * called, but the CPU is still in the online mask for the
282 * tear down callback. So the power can not be updated when
283 * the CPU is unplugged.
285 * At CPUHP_AP_DTPM_CPU_DEAD, the situation is the opposite as
286 * above. The CPU online mask is not up to date when the CPU
289 * For this reason, we need to call the online and offline
290 * callbacks at different moments when the CPU online mask is
291 * consistent with the power numbers we want to update.
293 ret
= cpuhp_setup_state(CPUHP_AP_DTPM_CPU_DEAD
, "dtpm_cpu:offline",
294 NULL
, cpuhp_dtpm_cpu_offline
);
298 ret
= cpuhp_setup_state(CPUHP_AP_ONLINE_DYN
, "dtpm_cpu:online",
299 cpuhp_dtpm_cpu_online
, NULL
);
306 static void dtpm_cpu_exit(void)
308 cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN
);
309 cpuhp_remove_state_nocalls(CPUHP_AP_DTPM_CPU_DEAD
);
312 struct dtpm_subsys_ops dtpm_cpu_ops
= {
313 .name
= KBUILD_MODNAME
,
314 .init
= dtpm_cpu_init
,
315 .exit
= dtpm_cpu_exit
,
316 .setup
= dtpm_cpu_setup
,