]>
git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/powercap/dtpm_cpu.c
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 #include <linux/cpumask.h>
18 #include <linux/cpufreq.h>
19 #include <linux/cpuhotplug.h>
20 #include <linux/dtpm.h>
21 #include <linux/energy_model.h>
22 #include <linux/pm_qos.h>
23 #include <linux/slab.h>
24 #include <linux/units.h>
26 static struct dtpm
*__parent
;
28 static DEFINE_PER_CPU(struct dtpm
*, dtpm_per_cpu
);
31 struct freq_qos_request qos_req
;
36 * When a new CPU is inserted at hotplug or boot time, add the power
37 * contribution and update the dtpm tree.
39 static int power_add(struct dtpm
*dtpm
, struct em_perf_domain
*em
)
41 u64 power_min
, power_max
;
43 power_min
= em
->table
[0].power
;
44 power_min
*= MICROWATT_PER_MILLIWATT
;
45 power_min
+= dtpm
->power_min
;
47 power_max
= em
->table
[em
->nr_perf_states
- 1].power
;
48 power_max
*= MICROWATT_PER_MILLIWATT
;
49 power_max
+= dtpm
->power_max
;
51 return dtpm_update_power(dtpm
, power_min
, power_max
);
55 * When a CPU is unplugged, remove its power contribution from the
58 static int power_sub(struct dtpm
*dtpm
, struct em_perf_domain
*em
)
60 u64 power_min
, power_max
;
62 power_min
= em
->table
[0].power
;
63 power_min
*= MICROWATT_PER_MILLIWATT
;
64 power_min
= dtpm
->power_min
- power_min
;
66 power_max
= em
->table
[em
->nr_perf_states
- 1].power
;
67 power_max
*= MICROWATT_PER_MILLIWATT
;
68 power_max
= dtpm
->power_max
- power_max
;
70 return dtpm_update_power(dtpm
, power_min
, power_max
);
73 static u64
set_pd_power_limit(struct dtpm
*dtpm
, u64 power_limit
)
75 struct dtpm_cpu
*dtpm_cpu
= dtpm
->private;
76 struct em_perf_domain
*pd
;
82 pd
= em_cpu_get(dtpm_cpu
->cpu
);
84 cpumask_and(&cpus
, cpu_online_mask
, to_cpumask(pd
->cpus
));
86 nr_cpus
= cpumask_weight(&cpus
);
88 for (i
= 0; i
< pd
->nr_perf_states
; i
++) {
90 power
= pd
->table
[i
].power
* MICROWATT_PER_MILLIWATT
* nr_cpus
;
92 if (power
> power_limit
)
96 freq
= pd
->table
[i
- 1].frequency
;
98 freq_qos_update_request(&dtpm_cpu
->qos_req
, freq
);
100 power_limit
= pd
->table
[i
- 1].power
*
101 MICROWATT_PER_MILLIWATT
* nr_cpus
;
106 static u64
get_pd_power_uw(struct dtpm
*dtpm
)
108 struct dtpm_cpu
*dtpm_cpu
= dtpm
->private;
109 struct em_perf_domain
*pd
;
114 pd
= em_cpu_get(dtpm_cpu
->cpu
);
115 freq
= cpufreq_quick_get(dtpm_cpu
->cpu
);
116 cpumask_and(&cpus
, cpu_online_mask
, to_cpumask(pd
->cpus
));
117 nr_cpus
= cpumask_weight(&cpus
);
119 for (i
= 0; i
< pd
->nr_perf_states
; i
++) {
121 if (pd
->table
[i
].frequency
< freq
)
124 return pd
->table
[i
].power
*
125 MICROWATT_PER_MILLIWATT
* nr_cpus
;
131 static void pd_release(struct dtpm
*dtpm
)
133 struct dtpm_cpu
*dtpm_cpu
= dtpm
->private;
135 if (freq_qos_request_active(&dtpm_cpu
->qos_req
))
136 freq_qos_remove_request(&dtpm_cpu
->qos_req
);
141 static struct dtpm_ops dtpm_ops
= {
142 .set_power_uw
= set_pd_power_limit
,
143 .get_power_uw
= get_pd_power_uw
,
144 .release
= pd_release
,
147 static int cpuhp_dtpm_cpu_offline(unsigned int cpu
)
149 struct cpufreq_policy
*policy
;
150 struct em_perf_domain
*pd
;
153 policy
= cpufreq_cpu_get(cpu
);
158 pd
= em_cpu_get(cpu
);
162 dtpm
= per_cpu(dtpm_per_cpu
, cpu
);
166 if (cpumask_weight(policy
->cpus
) != 1)
169 for_each_cpu(cpu
, policy
->related_cpus
)
170 per_cpu(dtpm_per_cpu
, cpu
) = NULL
;
172 dtpm_unregister(dtpm
);
177 static int cpuhp_dtpm_cpu_online(unsigned int cpu
)
180 struct dtpm_cpu
*dtpm_cpu
;
181 struct cpufreq_policy
*policy
;
182 struct em_perf_domain
*pd
;
183 char name
[CPUFREQ_NAME_LEN
];
186 policy
= cpufreq_cpu_get(cpu
);
191 pd
= em_cpu_get(cpu
);
195 dtpm
= per_cpu(dtpm_per_cpu
, cpu
);
197 return power_add(dtpm
, pd
);
199 dtpm
= dtpm_alloc(&dtpm_ops
);
203 dtpm_cpu
= kzalloc(sizeof(*dtpm_cpu
), GFP_KERNEL
);
207 dtpm
->private = dtpm_cpu
;
210 for_each_cpu(cpu
, policy
->related_cpus
)
211 per_cpu(dtpm_per_cpu
, cpu
) = dtpm
;
213 sprintf(name
, "cpu%d", dtpm_cpu
->cpu
);
215 ret
= dtpm_register(name
, dtpm
, __parent
);
217 goto out_kfree_dtpm_cpu
;
219 ret
= power_add(dtpm
, pd
);
221 goto out_dtpm_unregister
;
223 ret
= freq_qos_add_request(&policy
->constraints
,
224 &dtpm_cpu
->qos_req
, FREQ_QOS_MAX
,
225 pd
->table
[pd
->nr_perf_states
- 1].frequency
);
235 dtpm_unregister(dtpm
);
240 for_each_cpu(cpu
, policy
->related_cpus
)
241 per_cpu(dtpm_per_cpu
, cpu
) = NULL
;
249 int dtpm_register_cpu(struct dtpm
*parent
)
253 return cpuhp_setup_state(CPUHP_AP_DTPM_CPU_ONLINE
,
255 cpuhp_dtpm_cpu_online
,
256 cpuhp_dtpm_cpu_offline
);