127871f7aSQuentin Perret // SPDX-License-Identifier: GPL-2.0
227871f7aSQuentin Perret /*
31bc138c6SLukasz Luba * Energy Model of devices
427871f7aSQuentin Perret *
5c8ed9953SVincent Donnefort * Copyright (c) 2018-2021, Arm ltd.
627871f7aSQuentin Perret * Written by: Quentin Perret, Arm ltd.
71bc138c6SLukasz Luba * Improvements provided by: Lukasz Luba, Arm ltd.
827871f7aSQuentin Perret */
927871f7aSQuentin Perret
1027871f7aSQuentin Perret #define pr_fmt(fmt) "energy_model: " fmt
1127871f7aSQuentin Perret
1227871f7aSQuentin Perret #include <linux/cpu.h>
13e458716aSVincent Donnefort #include <linux/cpufreq.h>
1427871f7aSQuentin Perret #include <linux/cpumask.h>
159cac42d0SQuentin Perret #include <linux/debugfs.h>
1627871f7aSQuentin Perret #include <linux/energy_model.h>
1727871f7aSQuentin Perret #include <linux/sched/topology.h>
1827871f7aSQuentin Perret #include <linux/slab.h>
1927871f7aSQuentin Perret
2027871f7aSQuentin Perret /*
2127871f7aSQuentin Perret * Mutex serializing the registrations of performance domains and letting
2227871f7aSQuentin Perret * callbacks defined by drivers sleep.
2327871f7aSQuentin Perret */
2427871f7aSQuentin Perret static DEFINE_MUTEX(em_pd_mutex);
2527871f7aSQuentin Perret
_is_cpu_device(struct device * dev)261bc138c6SLukasz Luba static bool _is_cpu_device(struct device *dev)
271bc138c6SLukasz Luba {
281bc138c6SLukasz Luba return (dev->bus == &cpu_subsys);
291bc138c6SLukasz Luba }
301bc138c6SLukasz Luba
319cac42d0SQuentin Perret #ifdef CONFIG_DEBUG_FS
329cac42d0SQuentin Perret static struct dentry *rootdir;
339cac42d0SQuentin Perret
em_debug_create_ps(struct em_perf_state * ps,struct dentry * pd)34521b512bSLukasz Luba static void em_debug_create_ps(struct em_perf_state *ps, struct dentry *pd)
359cac42d0SQuentin Perret {
369cac42d0SQuentin Perret struct dentry *d;
379cac42d0SQuentin Perret char name[24];
389cac42d0SQuentin Perret
39521b512bSLukasz Luba snprintf(name, sizeof(name), "ps:%lu", ps->frequency);
409cac42d0SQuentin Perret
41521b512bSLukasz Luba /* Create per-ps directory */
429cac42d0SQuentin Perret d = debugfs_create_dir(name, pd);
43521b512bSLukasz Luba debugfs_create_ulong("frequency", 0444, d, &ps->frequency);
44521b512bSLukasz Luba debugfs_create_ulong("power", 0444, d, &ps->power);
45521b512bSLukasz Luba debugfs_create_ulong("cost", 0444, d, &ps->cost);
46c8ed9953SVincent Donnefort debugfs_create_ulong("inefficient", 0444, d, &ps->flags);
479cac42d0SQuentin Perret }
489cac42d0SQuentin Perret
em_debug_cpus_show(struct seq_file * s,void * unused)499cac42d0SQuentin Perret static int em_debug_cpus_show(struct seq_file *s, void *unused)
509cac42d0SQuentin Perret {
519cac42d0SQuentin Perret seq_printf(s, "%*pbl\n", cpumask_pr_args(to_cpumask(s->private)));
529cac42d0SQuentin Perret
539cac42d0SQuentin Perret return 0;
549cac42d0SQuentin Perret }
559cac42d0SQuentin Perret DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
569cac42d0SQuentin Perret
em_debug_flags_show(struct seq_file * s,void * unused)5716857482SLukasz Luba static int em_debug_flags_show(struct seq_file *s, void *unused)
58c250d50fSLukasz Luba {
59c250d50fSLukasz Luba struct em_perf_domain *pd = s->private;
60c250d50fSLukasz Luba
6116857482SLukasz Luba seq_printf(s, "%#lx\n", pd->flags);
62c250d50fSLukasz Luba
63c250d50fSLukasz Luba return 0;
64c250d50fSLukasz Luba }
6516857482SLukasz Luba DEFINE_SHOW_ATTRIBUTE(em_debug_flags);
668354eb9eSVincent Donnefort
em_debug_create_pd(struct device * dev)671bc138c6SLukasz Luba static void em_debug_create_pd(struct device *dev)
689cac42d0SQuentin Perret {
699cac42d0SQuentin Perret struct dentry *d;
709cac42d0SQuentin Perret int i;
719cac42d0SQuentin Perret
729cac42d0SQuentin Perret /* Create the directory of the performance domain */
731bc138c6SLukasz Luba d = debugfs_create_dir(dev_name(dev), rootdir);
749cac42d0SQuentin Perret
751bc138c6SLukasz Luba if (_is_cpu_device(dev))
761bc138c6SLukasz Luba debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
771bc138c6SLukasz Luba &em_debug_cpus_fops);
789cac42d0SQuentin Perret
7916857482SLukasz Luba debugfs_create_file("flags", 0444, d, dev->em_pd,
8016857482SLukasz Luba &em_debug_flags_fops);
81c250d50fSLukasz Luba
82521b512bSLukasz Luba /* Create a sub-directory for each performance state */
831bc138c6SLukasz Luba for (i = 0; i < dev->em_pd->nr_perf_states; i++)
841bc138c6SLukasz Luba em_debug_create_ps(&dev->em_pd->table[i], d);
851bc138c6SLukasz Luba
861bc138c6SLukasz Luba }
871bc138c6SLukasz Luba
em_debug_remove_pd(struct device * dev)881bc138c6SLukasz Luba static void em_debug_remove_pd(struct device *dev)
891bc138c6SLukasz Luba {
90*a0e8c13cSGreg Kroah-Hartman debugfs_lookup_and_remove(dev_name(dev), rootdir);
919cac42d0SQuentin Perret }
929cac42d0SQuentin Perret
em_debug_init(void)939cac42d0SQuentin Perret static int __init em_debug_init(void)
949cac42d0SQuentin Perret {
959cac42d0SQuentin Perret /* Create /sys/kernel/debug/energy_model directory */
969cac42d0SQuentin Perret rootdir = debugfs_create_dir("energy_model", NULL);
979cac42d0SQuentin Perret
989cac42d0SQuentin Perret return 0;
999cac42d0SQuentin Perret }
100fb9d62b2SLukasz Luba fs_initcall(em_debug_init);
1019cac42d0SQuentin Perret #else /* CONFIG_DEBUG_FS */
em_debug_create_pd(struct device * dev)1021bc138c6SLukasz Luba static void em_debug_create_pd(struct device *dev) {}
em_debug_remove_pd(struct device * dev)1031bc138c6SLukasz Luba static void em_debug_remove_pd(struct device *dev) {}
1049cac42d0SQuentin Perret #endif
1051bc138c6SLukasz Luba
em_create_perf_table(struct device * dev,struct em_perf_domain * pd,int nr_states,struct em_data_callback * cb,unsigned long flags)1061bc138c6SLukasz Luba static int em_create_perf_table(struct device *dev, struct em_perf_domain *pd,
10791362463SLukasz Luba int nr_states, struct em_data_callback *cb,
10891362463SLukasz Luba unsigned long flags)
10927871f7aSQuentin Perret {
110aa1a4326SVincent Donnefort unsigned long power, freq, prev_freq = 0, prev_cost = ULONG_MAX;
111521b512bSLukasz Luba struct em_perf_state *table;
1121bc138c6SLukasz Luba int i, ret;
11327871f7aSQuentin Perret u64 fmax;
11427871f7aSQuentin Perret
11527871f7aSQuentin Perret table = kcalloc(nr_states, sizeof(*table), GFP_KERNEL);
11627871f7aSQuentin Perret if (!table)
1171bc138c6SLukasz Luba return -ENOMEM;
11827871f7aSQuentin Perret
119521b512bSLukasz Luba /* Build the list of performance states for this performance domain */
12027871f7aSQuentin Perret for (i = 0, freq = 0; i < nr_states; i++, freq++) {
12127871f7aSQuentin Perret /*
12227871f7aSQuentin Perret * active_power() is a driver callback which ceils 'freq' to
1231bc138c6SLukasz Luba * lowest performance state of 'dev' above 'freq' and updates
12427871f7aSQuentin Perret * 'power' and 'freq' accordingly.
12527871f7aSQuentin Perret */
12675a3a99aSLukasz Luba ret = cb->active_power(dev, &power, &freq);
12727871f7aSQuentin Perret if (ret) {
1281bc138c6SLukasz Luba dev_err(dev, "EM: invalid perf. state: %d\n",
1291bc138c6SLukasz Luba ret);
130521b512bSLukasz Luba goto free_ps_table;
13127871f7aSQuentin Perret }
13227871f7aSQuentin Perret
13327871f7aSQuentin Perret /*
13427871f7aSQuentin Perret * We expect the driver callback to increase the frequency for
135521b512bSLukasz Luba * higher performance states.
13627871f7aSQuentin Perret */
13727871f7aSQuentin Perret if (freq <= prev_freq) {
1381bc138c6SLukasz Luba dev_err(dev, "EM: non-increasing freq: %lu\n",
1391bc138c6SLukasz Luba freq);
140521b512bSLukasz Luba goto free_ps_table;
14127871f7aSQuentin Perret }
14227871f7aSQuentin Perret
14327871f7aSQuentin Perret /*
14427871f7aSQuentin Perret * The power returned by active_state() is expected to be
145ae6ccaa6SLukasz Luba * positive and be in range.
14627871f7aSQuentin Perret */
1477d9895c7SLukasz Luba if (!power || power > EM_MAX_POWER) {
1481bc138c6SLukasz Luba dev_err(dev, "EM: invalid power: %lu\n",
1491bc138c6SLukasz Luba power);
150521b512bSLukasz Luba goto free_ps_table;
15127871f7aSQuentin Perret }
15227871f7aSQuentin Perret
15327871f7aSQuentin Perret table[i].power = power;
15427871f7aSQuentin Perret table[i].frequency = prev_freq = freq;
15527871f7aSQuentin Perret }
15627871f7aSQuentin Perret
157521b512bSLukasz Luba /* Compute the cost of each performance state. */
15827871f7aSQuentin Perret fmax = (u64) table[nr_states - 1].frequency;
159aa1a4326SVincent Donnefort for (i = nr_states - 1; i >= 0; i--) {
16091362463SLukasz Luba unsigned long power_res, cost;
1617fcc17d0SLukasz Luba
16291362463SLukasz Luba if (flags & EM_PERF_DOMAIN_ARTIFICIAL) {
16391362463SLukasz Luba ret = cb->get_cost(dev, table[i].frequency, &cost);
16491362463SLukasz Luba if (ret || !cost || cost > EM_MAX_POWER) {
16591362463SLukasz Luba dev_err(dev, "EM: invalid cost %lu %d\n",
16691362463SLukasz Luba cost, ret);
16791362463SLukasz Luba goto free_ps_table;
16891362463SLukasz Luba }
16991362463SLukasz Luba } else {
170ae6ccaa6SLukasz Luba power_res = table[i].power;
17191362463SLukasz Luba cost = div64_u64(fmax * power_res, table[i].frequency);
17291362463SLukasz Luba }
17391362463SLukasz Luba
17491362463SLukasz Luba table[i].cost = cost;
17591362463SLukasz Luba
176aa1a4326SVincent Donnefort if (table[i].cost >= prev_cost) {
177c8ed9953SVincent Donnefort table[i].flags = EM_PERF_STATE_INEFFICIENT;
178aa1a4326SVincent Donnefort dev_dbg(dev, "EM: OPP:%lu is inefficient\n",
179aa1a4326SVincent Donnefort table[i].frequency);
180aa1a4326SVincent Donnefort } else {
181aa1a4326SVincent Donnefort prev_cost = table[i].cost;
182aa1a4326SVincent Donnefort }
18327871f7aSQuentin Perret }
18427871f7aSQuentin Perret
18527871f7aSQuentin Perret pd->table = table;
186521b512bSLukasz Luba pd->nr_perf_states = nr_states;
18727871f7aSQuentin Perret
1881bc138c6SLukasz Luba return 0;
18927871f7aSQuentin Perret
190521b512bSLukasz Luba free_ps_table:
19127871f7aSQuentin Perret kfree(table);
1921bc138c6SLukasz Luba return -EINVAL;
19327871f7aSQuentin Perret }
19427871f7aSQuentin Perret
em_create_pd(struct device * dev,int nr_states,struct em_data_callback * cb,cpumask_t * cpus,unsigned long flags)1951bc138c6SLukasz Luba static int em_create_pd(struct device *dev, int nr_states,
19691362463SLukasz Luba struct em_data_callback *cb, cpumask_t *cpus,
19791362463SLukasz Luba unsigned long flags)
1981bc138c6SLukasz Luba {
1991bc138c6SLukasz Luba struct em_perf_domain *pd;
2001bc138c6SLukasz Luba struct device *cpu_dev;
201ae6ccaa6SLukasz Luba int cpu, ret, num_cpus;
2021bc138c6SLukasz Luba
2031bc138c6SLukasz Luba if (_is_cpu_device(dev)) {
204ae6ccaa6SLukasz Luba num_cpus = cpumask_weight(cpus);
205ae6ccaa6SLukasz Luba
206ae6ccaa6SLukasz Luba /* Prevent max possible energy calculation to not overflow */
207ae6ccaa6SLukasz Luba if (num_cpus > EM_MAX_NUM_CPUS) {
208ae6ccaa6SLukasz Luba dev_err(dev, "EM: too many CPUs, overflow possible\n");
209ae6ccaa6SLukasz Luba return -EINVAL;
210ae6ccaa6SLukasz Luba }
211ae6ccaa6SLukasz Luba
2121bc138c6SLukasz Luba pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL);
2131bc138c6SLukasz Luba if (!pd)
2141bc138c6SLukasz Luba return -ENOMEM;
2151bc138c6SLukasz Luba
2161bc138c6SLukasz Luba cpumask_copy(em_span_cpus(pd), cpus);
2171bc138c6SLukasz Luba } else {
2181bc138c6SLukasz Luba pd = kzalloc(sizeof(*pd), GFP_KERNEL);
2191bc138c6SLukasz Luba if (!pd)
2201bc138c6SLukasz Luba return -ENOMEM;
2211bc138c6SLukasz Luba }
2221bc138c6SLukasz Luba
22391362463SLukasz Luba ret = em_create_perf_table(dev, pd, nr_states, cb, flags);
2241bc138c6SLukasz Luba if (ret) {
2251bc138c6SLukasz Luba kfree(pd);
2261bc138c6SLukasz Luba return ret;
2271bc138c6SLukasz Luba }
2281bc138c6SLukasz Luba
2291bc138c6SLukasz Luba if (_is_cpu_device(dev))
2301bc138c6SLukasz Luba for_each_cpu(cpu, cpus) {
2311bc138c6SLukasz Luba cpu_dev = get_cpu_device(cpu);
2321bc138c6SLukasz Luba cpu_dev->em_pd = pd;
2331bc138c6SLukasz Luba }
2341bc138c6SLukasz Luba
2351bc138c6SLukasz Luba dev->em_pd = pd;
2361bc138c6SLukasz Luba
2371bc138c6SLukasz Luba return 0;
2381bc138c6SLukasz Luba }
2391bc138c6SLukasz Luba
em_cpufreq_update_efficiencies(struct device * dev)240e458716aSVincent Donnefort static void em_cpufreq_update_efficiencies(struct device *dev)
241e458716aSVincent Donnefort {
242e458716aSVincent Donnefort struct em_perf_domain *pd = dev->em_pd;
243e458716aSVincent Donnefort struct em_perf_state *table;
244e458716aSVincent Donnefort struct cpufreq_policy *policy;
245e458716aSVincent Donnefort int found = 0;
246e458716aSVincent Donnefort int i;
247e458716aSVincent Donnefort
248e458716aSVincent Donnefort if (!_is_cpu_device(dev) || !pd)
249e458716aSVincent Donnefort return;
250e458716aSVincent Donnefort
251e458716aSVincent Donnefort policy = cpufreq_cpu_get(cpumask_first(em_span_cpus(pd)));
252e458716aSVincent Donnefort if (!policy) {
253e458716aSVincent Donnefort dev_warn(dev, "EM: Access to CPUFreq policy failed");
254e458716aSVincent Donnefort return;
255e458716aSVincent Donnefort }
256e458716aSVincent Donnefort
257e458716aSVincent Donnefort table = pd->table;
258e458716aSVincent Donnefort
259e458716aSVincent Donnefort for (i = 0; i < pd->nr_perf_states; i++) {
260e458716aSVincent Donnefort if (!(table[i].flags & EM_PERF_STATE_INEFFICIENT))
261e458716aSVincent Donnefort continue;
262e458716aSVincent Donnefort
263e458716aSVincent Donnefort if (!cpufreq_table_set_inefficient(policy, table[i].frequency))
264e458716aSVincent Donnefort found++;
265e458716aSVincent Donnefort }
266e458716aSVincent Donnefort
267c9d8923bSPierre Gondois cpufreq_cpu_put(policy);
268c9d8923bSPierre Gondois
269e458716aSVincent Donnefort if (!found)
270e458716aSVincent Donnefort return;
271e458716aSVincent Donnefort
272e458716aSVincent Donnefort /*
273e458716aSVincent Donnefort * Efficiencies have been installed in CPUFreq, inefficient frequencies
274e458716aSVincent Donnefort * will be skipped. The EM can do the same.
275e458716aSVincent Donnefort */
276e458716aSVincent Donnefort pd->flags |= EM_PERF_DOMAIN_SKIP_INEFFICIENCIES;
277e458716aSVincent Donnefort }
278e458716aSVincent Donnefort
2791bc138c6SLukasz Luba /**
2801bc138c6SLukasz Luba * em_pd_get() - Return the performance domain for a device
2811bc138c6SLukasz Luba * @dev : Device to find the performance domain for
2821bc138c6SLukasz Luba *
2831bc138c6SLukasz Luba * Returns the performance domain to which @dev belongs, or NULL if it doesn't
2841bc138c6SLukasz Luba * exist.
2851bc138c6SLukasz Luba */
em_pd_get(struct device * dev)2861bc138c6SLukasz Luba struct em_perf_domain *em_pd_get(struct device *dev)
2871bc138c6SLukasz Luba {
2881bc138c6SLukasz Luba if (IS_ERR_OR_NULL(dev))
2891bc138c6SLukasz Luba return NULL;
2901bc138c6SLukasz Luba
2911bc138c6SLukasz Luba return dev->em_pd;
2921bc138c6SLukasz Luba }
2931bc138c6SLukasz Luba EXPORT_SYMBOL_GPL(em_pd_get);
2941bc138c6SLukasz Luba
29527871f7aSQuentin Perret /**
29627871f7aSQuentin Perret * em_cpu_get() - Return the performance domain for a CPU
29727871f7aSQuentin Perret * @cpu : CPU to find the performance domain for
29827871f7aSQuentin Perret *
2991bc138c6SLukasz Luba * Returns the performance domain to which @cpu belongs, or NULL if it doesn't
30027871f7aSQuentin Perret * exist.
30127871f7aSQuentin Perret */
em_cpu_get(int cpu)30227871f7aSQuentin Perret struct em_perf_domain *em_cpu_get(int cpu)
30327871f7aSQuentin Perret {
3041bc138c6SLukasz Luba struct device *cpu_dev;
3051bc138c6SLukasz Luba
3061bc138c6SLukasz Luba cpu_dev = get_cpu_device(cpu);
3071bc138c6SLukasz Luba if (!cpu_dev)
3081bc138c6SLukasz Luba return NULL;
3091bc138c6SLukasz Luba
3101bc138c6SLukasz Luba return em_pd_get(cpu_dev);
31127871f7aSQuentin Perret }
31227871f7aSQuentin Perret EXPORT_SYMBOL_GPL(em_cpu_get);
31327871f7aSQuentin Perret
31427871f7aSQuentin Perret /**
3157d9895c7SLukasz Luba * em_dev_register_perf_domain() - Register the Energy Model (EM) for a device
3167d9895c7SLukasz Luba * @dev : Device for which the EM is to register
317521b512bSLukasz Luba * @nr_states : Number of performance states to register
31827871f7aSQuentin Perret * @cb : Callback functions providing the data of the Energy Model
3191bc138c6SLukasz Luba * @cpus : Pointer to cpumask_t, which in case of a CPU device is
3207d9895c7SLukasz Luba * obligatory. It can be taken from i.e. 'policy->cpus'. For other
3217d9895c7SLukasz Luba * type of devices this should be set to NULL.
322ae6ccaa6SLukasz Luba * @microwatts : Flag indicating that the power values are in micro-Watts or
323c250d50fSLukasz Luba * in some other scale. It must be set properly.
32427871f7aSQuentin Perret *
32527871f7aSQuentin Perret * Create Energy Model tables for a performance domain using the callbacks
32627871f7aSQuentin Perret * defined in cb.
32727871f7aSQuentin Perret *
328ae6ccaa6SLukasz Luba * The @microwatts is important to set with correct value. Some kernel
329c250d50fSLukasz Luba * sub-systems might rely on this flag and check if all devices in the EM are
330c250d50fSLukasz Luba * using the same scale.
331c250d50fSLukasz Luba *
33227871f7aSQuentin Perret * If multiple clients register the same performance domain, all but the first
33327871f7aSQuentin Perret * registration will be ignored.
33427871f7aSQuentin Perret *
33527871f7aSQuentin Perret * Return 0 on success
33627871f7aSQuentin Perret */
em_dev_register_perf_domain(struct device * dev,unsigned int nr_states,struct em_data_callback * cb,cpumask_t * cpus,bool microwatts)3377d9895c7SLukasz Luba int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
338c250d50fSLukasz Luba struct em_data_callback *cb, cpumask_t *cpus,
339ae6ccaa6SLukasz Luba bool microwatts)
34027871f7aSQuentin Perret {
34127871f7aSQuentin Perret unsigned long cap, prev_cap = 0;
34291362463SLukasz Luba unsigned long flags = 0;
3431bc138c6SLukasz Luba int cpu, ret;
34427871f7aSQuentin Perret
3451bc138c6SLukasz Luba if (!dev || !nr_states || !cb)
34627871f7aSQuentin Perret return -EINVAL;
34727871f7aSQuentin Perret
34827871f7aSQuentin Perret /*
34927871f7aSQuentin Perret * Use a mutex to serialize the registration of performance domains and
35027871f7aSQuentin Perret * let the driver-defined callback functions sleep.
35127871f7aSQuentin Perret */
35227871f7aSQuentin Perret mutex_lock(&em_pd_mutex);
35327871f7aSQuentin Perret
3541bc138c6SLukasz Luba if (dev->em_pd) {
35527871f7aSQuentin Perret ret = -EEXIST;
35627871f7aSQuentin Perret goto unlock;
35727871f7aSQuentin Perret }
35827871f7aSQuentin Perret
3591bc138c6SLukasz Luba if (_is_cpu_device(dev)) {
3601bc138c6SLukasz Luba if (!cpus) {
3611bc138c6SLukasz Luba dev_err(dev, "EM: invalid CPU mask\n");
3621bc138c6SLukasz Luba ret = -EINVAL;
3631bc138c6SLukasz Luba goto unlock;
3641bc138c6SLukasz Luba }
3651bc138c6SLukasz Luba
3661bc138c6SLukasz Luba for_each_cpu(cpu, cpus) {
3671bc138c6SLukasz Luba if (em_cpu_get(cpu)) {
3681bc138c6SLukasz Luba dev_err(dev, "EM: exists for CPU%d\n", cpu);
3691bc138c6SLukasz Luba ret = -EEXIST;
3701bc138c6SLukasz Luba goto unlock;
3711bc138c6SLukasz Luba }
37227871f7aSQuentin Perret /*
3731bc138c6SLukasz Luba * All CPUs of a domain must have the same
3741bc138c6SLukasz Luba * micro-architecture since they all share the same
3751bc138c6SLukasz Luba * table.
37627871f7aSQuentin Perret */
3778ec59c0fSVincent Guittot cap = arch_scale_cpu_capacity(cpu);
37827871f7aSQuentin Perret if (prev_cap && prev_cap != cap) {
3791bc138c6SLukasz Luba dev_err(dev, "EM: CPUs of %*pbl must have the same capacity\n",
3801bc138c6SLukasz Luba cpumask_pr_args(cpus));
3811bc138c6SLukasz Luba
38227871f7aSQuentin Perret ret = -EINVAL;
38327871f7aSQuentin Perret goto unlock;
38427871f7aSQuentin Perret }
38527871f7aSQuentin Perret prev_cap = cap;
38627871f7aSQuentin Perret }
3871bc138c6SLukasz Luba }
38827871f7aSQuentin Perret
389ae6ccaa6SLukasz Luba if (microwatts)
390ae6ccaa6SLukasz Luba flags |= EM_PERF_DOMAIN_MICROWATTS;
39191362463SLukasz Luba else if (cb->get_cost)
39291362463SLukasz Luba flags |= EM_PERF_DOMAIN_ARTIFICIAL;
39391362463SLukasz Luba
39491362463SLukasz Luba ret = em_create_pd(dev, nr_states, cb, cpus, flags);
3951bc138c6SLukasz Luba if (ret)
39627871f7aSQuentin Perret goto unlock;
39727871f7aSQuentin Perret
39891362463SLukasz Luba dev->em_pd->flags |= flags;
399c250d50fSLukasz Luba
400e458716aSVincent Donnefort em_cpufreq_update_efficiencies(dev);
401e458716aSVincent Donnefort
4021bc138c6SLukasz Luba em_debug_create_pd(dev);
4031bc138c6SLukasz Luba dev_info(dev, "EM: created perf domain\n");
40427871f7aSQuentin Perret
40527871f7aSQuentin Perret unlock:
40627871f7aSQuentin Perret mutex_unlock(&em_pd_mutex);
40727871f7aSQuentin Perret return ret;
40827871f7aSQuentin Perret }
4097d9895c7SLukasz Luba EXPORT_SYMBOL_GPL(em_dev_register_perf_domain);
4107d9895c7SLukasz Luba
4117d9895c7SLukasz Luba /**
4121bc138c6SLukasz Luba * em_dev_unregister_perf_domain() - Unregister Energy Model (EM) for a device
4131bc138c6SLukasz Luba * @dev : Device for which the EM is registered
4141bc138c6SLukasz Luba *
4151bc138c6SLukasz Luba * Unregister the EM for the specified @dev (but not a CPU device).
4161bc138c6SLukasz Luba */
em_dev_unregister_perf_domain(struct device * dev)4171bc138c6SLukasz Luba void em_dev_unregister_perf_domain(struct device *dev)
4181bc138c6SLukasz Luba {
4191bc138c6SLukasz Luba if (IS_ERR_OR_NULL(dev) || !dev->em_pd)
4201bc138c6SLukasz Luba return;
4211bc138c6SLukasz Luba
4221bc138c6SLukasz Luba if (_is_cpu_device(dev))
4231bc138c6SLukasz Luba return;
4241bc138c6SLukasz Luba
4251bc138c6SLukasz Luba /*
4261bc138c6SLukasz Luba * The mutex separates all register/unregister requests and protects
4271bc138c6SLukasz Luba * from potential clean-up/setup issues in the debugfs directories.
4281bc138c6SLukasz Luba * The debugfs directory name is the same as device's name.
4291bc138c6SLukasz Luba */
4301bc138c6SLukasz Luba mutex_lock(&em_pd_mutex);
4311bc138c6SLukasz Luba em_debug_remove_pd(dev);
4321bc138c6SLukasz Luba
4331bc138c6SLukasz Luba kfree(dev->em_pd->table);
4341bc138c6SLukasz Luba kfree(dev->em_pd);
4351bc138c6SLukasz Luba dev->em_pd = NULL;
4361bc138c6SLukasz Luba mutex_unlock(&em_pd_mutex);
4371bc138c6SLukasz Luba }
4381bc138c6SLukasz Luba EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain);
439