xref: /openbmc/linux/kernel/power/energy_model.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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