xref: /linux/drivers/resctrl/mpam_devices.c (revision 01fb4b8224726aa0f2170b63e4685cf0eec85d8d)
1f04046f2SJames Morse // SPDX-License-Identifier: GPL-2.0
2f04046f2SJames Morse // Copyright (C) 2025 Arm Ltd.
3f04046f2SJames Morse 
4f04046f2SJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
5f04046f2SJames Morse 
6f04046f2SJames Morse #include <linux/acpi.h>
7f04046f2SJames Morse #include <linux/arm_mpam.h>
8f04046f2SJames Morse #include <linux/cacheinfo.h>
9f04046f2SJames Morse #include <linux/cpumask.h>
10f04046f2SJames Morse #include <linux/device.h>
11f04046f2SJames Morse #include <linux/errno.h>
12f04046f2SJames Morse #include <linux/gfp.h>
13f04046f2SJames Morse #include <linux/list.h>
14f04046f2SJames Morse #include <linux/lockdep.h>
15f04046f2SJames Morse #include <linux/mutex.h>
16f04046f2SJames Morse #include <linux/platform_device.h>
17f04046f2SJames Morse #include <linux/printk.h>
18f04046f2SJames Morse #include <linux/srcu.h>
19f04046f2SJames Morse #include <linux/types.h>
20f04046f2SJames Morse 
21f04046f2SJames Morse #include "mpam_internal.h"
22f04046f2SJames Morse 
23f04046f2SJames Morse /*
24f04046f2SJames Morse  * mpam_list_lock protects the SRCU lists when writing. Once the
25f04046f2SJames Morse  * mpam_enabled key is enabled these lists are read-only,
26f04046f2SJames Morse  * unless the error interrupt disables the driver.
27f04046f2SJames Morse  */
28f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock);
29f04046f2SJames Morse static LIST_HEAD(mpam_all_msc);
30f04046f2SJames Morse 
31f04046f2SJames Morse struct srcu_struct mpam_srcu;
32f04046f2SJames Morse 
33f04046f2SJames Morse /*
34f04046f2SJames Morse  * Number of MSCs that have been probed. Once all MSCs have been probed MPAM
35f04046f2SJames Morse  * can be enabled.
36f04046f2SJames Morse  */
37f04046f2SJames Morse static atomic_t mpam_num_msc;
38f04046f2SJames Morse 
39f04046f2SJames Morse /*
40*01fb4b82SJames Morse  * An MSC is a physical container for controls and monitors, each identified by
41*01fb4b82SJames Morse  * their RIS index. These share a base-address, interrupts and some MMIO
42*01fb4b82SJames Morse  * registers. A vMSC is a virtual container for RIS in an MSC that control or
43*01fb4b82SJames Morse  * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but
44*01fb4b82SJames Morse  * not all RIS in an MSC share a vMSC.
45*01fb4b82SJames Morse  *
46*01fb4b82SJames Morse  * Components are a group of vMSC that control or monitor the same thing but
47*01fb4b82SJames Morse  * are from different MSC, so have different base-address, interrupts etc.
48*01fb4b82SJames Morse  * Classes are the set components of the same type.
49*01fb4b82SJames Morse  *
50*01fb4b82SJames Morse  * The features of a vMSC is the union of the RIS it contains.
51*01fb4b82SJames Morse  * The features of a Class and Component are the common subset of the vMSC
52*01fb4b82SJames Morse  * they contain.
53*01fb4b82SJames Morse  *
54*01fb4b82SJames Morse  * e.g. The system cache may have bandwidth controls on multiple interfaces,
55*01fb4b82SJames Morse  * for regulating traffic from devices independently of traffic from CPUs.
56*01fb4b82SJames Morse  * If these are two RIS in one MSC, they will be treated as controlling
57*01fb4b82SJames Morse  * different things, and will not share a vMSC/component/class.
58*01fb4b82SJames Morse  *
59*01fb4b82SJames Morse  * e.g. The L2 may have one MSC and two RIS, one for cache-controls another
60*01fb4b82SJames Morse  * for bandwidth. These two RIS are members of the same vMSC.
61*01fb4b82SJames Morse  *
62*01fb4b82SJames Morse  * e.g. The set of RIS that make up the L2 are grouped as a component. These
63*01fb4b82SJames Morse  * are sometimes termed slices. They should be configured the same, as if there
64*01fb4b82SJames Morse  * were only one.
65*01fb4b82SJames Morse  *
66*01fb4b82SJames Morse  * e.g. The SoC probably has more than one L2, each attached to a distinct set
67*01fb4b82SJames Morse  * of CPUs. All the L2 components are grouped as a class.
68*01fb4b82SJames Morse  *
69*01fb4b82SJames Morse  * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list,
70*01fb4b82SJames Morse  * then linked via struct mpam_ris to a vmsc, component and class.
71*01fb4b82SJames Morse  * The same MSC may exist under different class->component->vmsc paths, but the
72*01fb4b82SJames Morse  * RIS index will be unique.
73*01fb4b82SJames Morse  */
74*01fb4b82SJames Morse LIST_HEAD(mpam_classes);
75*01fb4b82SJames Morse 
76*01fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */
77*01fb4b82SJames Morse static LLIST_HEAD(mpam_garbage);
78*01fb4b82SJames Morse 
79*01fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage)
80*01fb4b82SJames Morse {
81*01fb4b82SJames Morse 	init_llist_node(&garbage->llist);
82*01fb4b82SJames Morse }
83*01fb4b82SJames Morse 
84*01fb4b82SJames Morse #define add_to_garbage(x)				\
85*01fb4b82SJames Morse do {							\
86*01fb4b82SJames Morse 	__typeof__(x) _x = (x);				\
87*01fb4b82SJames Morse 	_x->garbage.to_free = _x;			\
88*01fb4b82SJames Morse 	llist_add(&_x->garbage.llist, &mpam_garbage);	\
89*01fb4b82SJames Morse } while (0)
90*01fb4b82SJames Morse 
91*01fb4b82SJames Morse static void mpam_free_garbage(void)
92*01fb4b82SJames Morse {
93*01fb4b82SJames Morse 	struct mpam_garbage *iter, *tmp;
94*01fb4b82SJames Morse 	struct llist_node *to_free = llist_del_all(&mpam_garbage);
95*01fb4b82SJames Morse 
96*01fb4b82SJames Morse 	if (!to_free)
97*01fb4b82SJames Morse 		return;
98*01fb4b82SJames Morse 
99*01fb4b82SJames Morse 	synchronize_srcu(&mpam_srcu);
100*01fb4b82SJames Morse 
101*01fb4b82SJames Morse 	llist_for_each_entry_safe(iter, tmp, to_free, llist) {
102*01fb4b82SJames Morse 		if (iter->pdev)
103*01fb4b82SJames Morse 			devm_kfree(&iter->pdev->dev, iter->to_free);
104*01fb4b82SJames Morse 		else
105*01fb4b82SJames Morse 			kfree(iter->to_free);
106*01fb4b82SJames Morse 	}
107*01fb4b82SJames Morse }
108*01fb4b82SJames Morse 
109*01fb4b82SJames Morse static struct mpam_class *
110*01fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type)
111*01fb4b82SJames Morse {
112*01fb4b82SJames Morse 	struct mpam_class *class;
113*01fb4b82SJames Morse 
114*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
115*01fb4b82SJames Morse 
116*01fb4b82SJames Morse 	class = kzalloc(sizeof(*class), GFP_KERNEL);
117*01fb4b82SJames Morse 	if (!class)
118*01fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
119*01fb4b82SJames Morse 	init_garbage(&class->garbage);
120*01fb4b82SJames Morse 
121*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->components);
122*01fb4b82SJames Morse 	/* Affinity is updated when ris are added */
123*01fb4b82SJames Morse 	class->level = level_idx;
124*01fb4b82SJames Morse 	class->type = type;
125*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->classes_list);
126*01fb4b82SJames Morse 
127*01fb4b82SJames Morse 	list_add_rcu(&class->classes_list, &mpam_classes);
128*01fb4b82SJames Morse 
129*01fb4b82SJames Morse 	return class;
130*01fb4b82SJames Morse }
131*01fb4b82SJames Morse 
132*01fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class)
133*01fb4b82SJames Morse {
134*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
135*01fb4b82SJames Morse 
136*01fb4b82SJames Morse 	list_del_rcu(&class->classes_list);
137*01fb4b82SJames Morse 	add_to_garbage(class);
138*01fb4b82SJames Morse }
139*01fb4b82SJames Morse 
140*01fb4b82SJames Morse static struct mpam_class *
141*01fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type)
142*01fb4b82SJames Morse {
143*01fb4b82SJames Morse 	struct mpam_class *class;
144*01fb4b82SJames Morse 
145*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
146*01fb4b82SJames Morse 
147*01fb4b82SJames Morse 	list_for_each_entry(class, &mpam_classes, classes_list) {
148*01fb4b82SJames Morse 		if (class->type == type && class->level == level_idx)
149*01fb4b82SJames Morse 			return class;
150*01fb4b82SJames Morse 	}
151*01fb4b82SJames Morse 
152*01fb4b82SJames Morse 	return mpam_class_alloc(level_idx, type);
153*01fb4b82SJames Morse }
154*01fb4b82SJames Morse 
155*01fb4b82SJames Morse static struct mpam_component *
156*01fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id)
157*01fb4b82SJames Morse {
158*01fb4b82SJames Morse 	struct mpam_component *comp;
159*01fb4b82SJames Morse 
160*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
161*01fb4b82SJames Morse 
162*01fb4b82SJames Morse 	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
163*01fb4b82SJames Morse 	if (!comp)
164*01fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
165*01fb4b82SJames Morse 	init_garbage(&comp->garbage);
166*01fb4b82SJames Morse 
167*01fb4b82SJames Morse 	comp->comp_id = id;
168*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->vmsc);
169*01fb4b82SJames Morse 	/* Affinity is updated when RIS are added */
170*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->class_list);
171*01fb4b82SJames Morse 	comp->class = class;
172*01fb4b82SJames Morse 
173*01fb4b82SJames Morse 	list_add_rcu(&comp->class_list, &class->components);
174*01fb4b82SJames Morse 
175*01fb4b82SJames Morse 	return comp;
176*01fb4b82SJames Morse }
177*01fb4b82SJames Morse 
178*01fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp)
179*01fb4b82SJames Morse {
180*01fb4b82SJames Morse 	struct mpam_class *class = comp->class;
181*01fb4b82SJames Morse 
182*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
183*01fb4b82SJames Morse 
184*01fb4b82SJames Morse 	list_del_rcu(&comp->class_list);
185*01fb4b82SJames Morse 	add_to_garbage(comp);
186*01fb4b82SJames Morse 
187*01fb4b82SJames Morse 	if (list_empty(&class->components))
188*01fb4b82SJames Morse 		mpam_class_destroy(class);
189*01fb4b82SJames Morse }
190*01fb4b82SJames Morse 
191*01fb4b82SJames Morse static struct mpam_component *
192*01fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id)
193*01fb4b82SJames Morse {
194*01fb4b82SJames Morse 	struct mpam_component *comp;
195*01fb4b82SJames Morse 
196*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
197*01fb4b82SJames Morse 
198*01fb4b82SJames Morse 	list_for_each_entry(comp, &class->components, class_list) {
199*01fb4b82SJames Morse 		if (comp->comp_id == id)
200*01fb4b82SJames Morse 			return comp;
201*01fb4b82SJames Morse 	}
202*01fb4b82SJames Morse 
203*01fb4b82SJames Morse 	return mpam_component_alloc(class, id);
204*01fb4b82SJames Morse }
205*01fb4b82SJames Morse 
206*01fb4b82SJames Morse static struct mpam_vmsc *
207*01fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc)
208*01fb4b82SJames Morse {
209*01fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
210*01fb4b82SJames Morse 
211*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
212*01fb4b82SJames Morse 
213*01fb4b82SJames Morse 	vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL);
214*01fb4b82SJames Morse 	if (!vmsc)
215*01fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
216*01fb4b82SJames Morse 	init_garbage(&vmsc->garbage);
217*01fb4b82SJames Morse 
218*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->ris);
219*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->comp_list);
220*01fb4b82SJames Morse 	vmsc->comp = comp;
221*01fb4b82SJames Morse 	vmsc->msc = msc;
222*01fb4b82SJames Morse 
223*01fb4b82SJames Morse 	list_add_rcu(&vmsc->comp_list, &comp->vmsc);
224*01fb4b82SJames Morse 
225*01fb4b82SJames Morse 	return vmsc;
226*01fb4b82SJames Morse }
227*01fb4b82SJames Morse 
228*01fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc)
229*01fb4b82SJames Morse {
230*01fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
231*01fb4b82SJames Morse 
232*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
233*01fb4b82SJames Morse 
234*01fb4b82SJames Morse 	list_del_rcu(&vmsc->comp_list);
235*01fb4b82SJames Morse 	add_to_garbage(vmsc);
236*01fb4b82SJames Morse 
237*01fb4b82SJames Morse 	if (list_empty(&comp->vmsc))
238*01fb4b82SJames Morse 		mpam_component_destroy(comp);
239*01fb4b82SJames Morse }
240*01fb4b82SJames Morse 
241*01fb4b82SJames Morse static struct mpam_vmsc *
242*01fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc)
243*01fb4b82SJames Morse {
244*01fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
245*01fb4b82SJames Morse 
246*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
247*01fb4b82SJames Morse 
248*01fb4b82SJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
249*01fb4b82SJames Morse 		if (vmsc->msc->id == msc->id)
250*01fb4b82SJames Morse 			return vmsc;
251*01fb4b82SJames Morse 	}
252*01fb4b82SJames Morse 
253*01fb4b82SJames Morse 	return mpam_vmsc_alloc(comp, msc);
254*01fb4b82SJames Morse }
255*01fb4b82SJames Morse 
256*01fb4b82SJames Morse /*
257*01fb4b82SJames Morse  * The cacheinfo structures are only populated when CPUs are online.
258*01fb4b82SJames Morse  * This helper walks the acpi tables to include offline CPUs too.
259*01fb4b82SJames Morse  */
260*01fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level,
261*01fb4b82SJames Morse 				   cpumask_t *affinity)
262*01fb4b82SJames Morse {
263*01fb4b82SJames Morse 	return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity);
264*01fb4b82SJames Morse }
265*01fb4b82SJames Morse 
266*01fb4b82SJames Morse /*
267*01fb4b82SJames Morse  * cpumask_of_node() only knows about online CPUs. This can't tell us whether
268*01fb4b82SJames Morse  * a class is represented on all possible CPUs.
269*01fb4b82SJames Morse  */
270*01fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity)
271*01fb4b82SJames Morse {
272*01fb4b82SJames Morse 	int cpu;
273*01fb4b82SJames Morse 
274*01fb4b82SJames Morse 	for_each_possible_cpu(cpu) {
275*01fb4b82SJames Morse 		if (node_id == cpu_to_node(cpu))
276*01fb4b82SJames Morse 			cpumask_set_cpu(cpu, affinity);
277*01fb4b82SJames Morse 	}
278*01fb4b82SJames Morse }
279*01fb4b82SJames Morse 
280*01fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity,
281*01fb4b82SJames Morse 				 enum mpam_class_types type,
282*01fb4b82SJames Morse 				 struct mpam_class *class,
283*01fb4b82SJames Morse 				 struct mpam_component *comp)
284*01fb4b82SJames Morse {
285*01fb4b82SJames Morse 	int err;
286*01fb4b82SJames Morse 
287*01fb4b82SJames Morse 	switch (type) {
288*01fb4b82SJames Morse 	case MPAM_CLASS_CACHE:
289*01fb4b82SJames Morse 		err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level,
290*01fb4b82SJames Morse 						     affinity);
291*01fb4b82SJames Morse 		if (err) {
292*01fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev,
293*01fb4b82SJames Morse 				      "Failed to determine CPU affinity\n");
294*01fb4b82SJames Morse 			return err;
295*01fb4b82SJames Morse 		}
296*01fb4b82SJames Morse 
297*01fb4b82SJames Morse 		if (cpumask_empty(affinity))
298*01fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n");
299*01fb4b82SJames Morse 
300*01fb4b82SJames Morse 		break;
301*01fb4b82SJames Morse 	case MPAM_CLASS_MEMORY:
302*01fb4b82SJames Morse 		get_cpumask_from_node_id(comp->comp_id, affinity);
303*01fb4b82SJames Morse 		/* affinity may be empty for CPU-less memory nodes */
304*01fb4b82SJames Morse 		break;
305*01fb4b82SJames Morse 	case MPAM_CLASS_UNKNOWN:
306*01fb4b82SJames Morse 		return 0;
307*01fb4b82SJames Morse 	}
308*01fb4b82SJames Morse 
309*01fb4b82SJames Morse 	cpumask_and(affinity, affinity, &msc->accessibility);
310*01fb4b82SJames Morse 
311*01fb4b82SJames Morse 	return 0;
312*01fb4b82SJames Morse }
313*01fb4b82SJames Morse 
314*01fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx,
315*01fb4b82SJames Morse 				  enum mpam_class_types type, u8 class_id,
316*01fb4b82SJames Morse 				  int component_id)
317*01fb4b82SJames Morse {
318*01fb4b82SJames Morse 	int err;
319*01fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
320*01fb4b82SJames Morse 	struct mpam_msc_ris *ris;
321*01fb4b82SJames Morse 	struct mpam_class *class;
322*01fb4b82SJames Morse 	struct mpam_component *comp;
323*01fb4b82SJames Morse 	struct platform_device *pdev = msc->pdev;
324*01fb4b82SJames Morse 
325*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
326*01fb4b82SJames Morse 
327*01fb4b82SJames Morse 	if (ris_idx > MPAM_MSC_MAX_NUM_RIS)
328*01fb4b82SJames Morse 		return -EINVAL;
329*01fb4b82SJames Morse 
330*01fb4b82SJames Morse 	if (test_and_set_bit(ris_idx, &msc->ris_idxs))
331*01fb4b82SJames Morse 		return -EBUSY;
332*01fb4b82SJames Morse 
333*01fb4b82SJames Morse 	ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL);
334*01fb4b82SJames Morse 	if (!ris)
335*01fb4b82SJames Morse 		return -ENOMEM;
336*01fb4b82SJames Morse 	init_garbage(&ris->garbage);
337*01fb4b82SJames Morse 	ris->garbage.pdev = pdev;
338*01fb4b82SJames Morse 
339*01fb4b82SJames Morse 	class = mpam_class_find(class_id, type);
340*01fb4b82SJames Morse 	if (IS_ERR(class))
341*01fb4b82SJames Morse 		return PTR_ERR(class);
342*01fb4b82SJames Morse 
343*01fb4b82SJames Morse 	comp = mpam_component_find(class, component_id);
344*01fb4b82SJames Morse 	if (IS_ERR(comp)) {
345*01fb4b82SJames Morse 		if (list_empty(&class->components))
346*01fb4b82SJames Morse 			mpam_class_destroy(class);
347*01fb4b82SJames Morse 		return PTR_ERR(comp);
348*01fb4b82SJames Morse 	}
349*01fb4b82SJames Morse 
350*01fb4b82SJames Morse 	vmsc = mpam_vmsc_find(comp, msc);
351*01fb4b82SJames Morse 	if (IS_ERR(vmsc)) {
352*01fb4b82SJames Morse 		if (list_empty(&comp->vmsc))
353*01fb4b82SJames Morse 			mpam_component_destroy(comp);
354*01fb4b82SJames Morse 		return PTR_ERR(vmsc);
355*01fb4b82SJames Morse 	}
356*01fb4b82SJames Morse 
357*01fb4b82SJames Morse 	err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp);
358*01fb4b82SJames Morse 	if (err) {
359*01fb4b82SJames Morse 		if (list_empty(&vmsc->ris))
360*01fb4b82SJames Morse 			mpam_vmsc_destroy(vmsc);
361*01fb4b82SJames Morse 		return err;
362*01fb4b82SJames Morse 	}
363*01fb4b82SJames Morse 
364*01fb4b82SJames Morse 	ris->ris_idx = ris_idx;
365*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->msc_list);
366*01fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->vmsc_list);
367*01fb4b82SJames Morse 	ris->vmsc = vmsc;
368*01fb4b82SJames Morse 
369*01fb4b82SJames Morse 	cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity);
370*01fb4b82SJames Morse 	cpumask_or(&class->affinity, &class->affinity, &ris->affinity);
371*01fb4b82SJames Morse 	list_add_rcu(&ris->vmsc_list, &vmsc->ris);
372*01fb4b82SJames Morse 	list_add_rcu(&ris->msc_list, &msc->ris);
373*01fb4b82SJames Morse 
374*01fb4b82SJames Morse 	return 0;
375*01fb4b82SJames Morse }
376*01fb4b82SJames Morse 
377*01fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris)
378*01fb4b82SJames Morse {
379*01fb4b82SJames Morse 	struct mpam_vmsc *vmsc = ris->vmsc;
380*01fb4b82SJames Morse 	struct mpam_msc *msc = vmsc->msc;
381*01fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
382*01fb4b82SJames Morse 	struct mpam_class *class = comp->class;
383*01fb4b82SJames Morse 
384*01fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
385*01fb4b82SJames Morse 
386*01fb4b82SJames Morse 	/*
387*01fb4b82SJames Morse 	 * It is assumed affinities don't overlap. If they do the class becomes
388*01fb4b82SJames Morse 	 * unusable immediately.
389*01fb4b82SJames Morse 	 */
390*01fb4b82SJames Morse 	cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity);
391*01fb4b82SJames Morse 	cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity);
392*01fb4b82SJames Morse 	clear_bit(ris->ris_idx, &msc->ris_idxs);
393*01fb4b82SJames Morse 	list_del_rcu(&ris->msc_list);
394*01fb4b82SJames Morse 	list_del_rcu(&ris->vmsc_list);
395*01fb4b82SJames Morse 	add_to_garbage(ris);
396*01fb4b82SJames Morse 
397*01fb4b82SJames Morse 	if (list_empty(&vmsc->ris))
398*01fb4b82SJames Morse 		mpam_vmsc_destroy(vmsc);
399*01fb4b82SJames Morse }
400*01fb4b82SJames Morse 
401*01fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
402*01fb4b82SJames Morse 		    enum mpam_class_types type, u8 class_id, int component_id)
403*01fb4b82SJames Morse {
404*01fb4b82SJames Morse 	int err;
405*01fb4b82SJames Morse 
406*01fb4b82SJames Morse 	mutex_lock(&mpam_list_lock);
407*01fb4b82SJames Morse 	err = mpam_ris_create_locked(msc, ris_idx, type, class_id,
408*01fb4b82SJames Morse 				     component_id);
409*01fb4b82SJames Morse 	mutex_unlock(&mpam_list_lock);
410*01fb4b82SJames Morse 	if (err)
411*01fb4b82SJames Morse 		mpam_free_garbage();
412*01fb4b82SJames Morse 
413*01fb4b82SJames Morse 	return err;
414*01fb4b82SJames Morse }
415*01fb4b82SJames Morse 
416*01fb4b82SJames Morse /*
417f04046f2SJames Morse  * An MSC can control traffic from a set of CPUs, but may only be accessible
418f04046f2SJames Morse  * from a (hopefully wider) set of CPUs. The common reason for this is power
419f04046f2SJames Morse  * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the
420f04046f2SJames Morse  * corresponding cache may also be powered off. By making accesses from
421f04046f2SJames Morse  * one of those CPUs, we ensure we don't access a cache that's powered off.
422f04046f2SJames Morse  */
423f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc)
424f04046f2SJames Morse {
425f04046f2SJames Morse 	u32 affinity_id;
426f04046f2SJames Morse 	int err;
427f04046f2SJames Morse 
428f04046f2SJames Morse 	err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity",
429f04046f2SJames Morse 				       &affinity_id);
430f04046f2SJames Morse 	if (err)
431f04046f2SJames Morse 		cpumask_copy(&msc->accessibility, cpu_possible_mask);
432f04046f2SJames Morse 	else
433f04046f2SJames Morse 		acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility);
434f04046f2SJames Morse }
435f04046f2SJames Morse 
436*01fb4b82SJames Morse /*
437*01fb4b82SJames Morse  * There are two ways of reaching a struct mpam_msc_ris. Via the
438*01fb4b82SJames Morse  * class->component->vmsc->ris, or via the msc.
439*01fb4b82SJames Morse  * When destroying the msc, the other side needs unlinking and cleaning up too.
440*01fb4b82SJames Morse  */
441f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc)
442f04046f2SJames Morse {
443f04046f2SJames Morse 	struct platform_device *pdev = msc->pdev;
444*01fb4b82SJames Morse 	struct mpam_msc_ris *ris, *tmp;
445f04046f2SJames Morse 
446f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
447f04046f2SJames Morse 
448*01fb4b82SJames Morse 	list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list)
449*01fb4b82SJames Morse 		mpam_ris_destroy(ris);
450*01fb4b82SJames Morse 
451f04046f2SJames Morse 	list_del_rcu(&msc->all_msc_list);
452f04046f2SJames Morse 	platform_set_drvdata(pdev, NULL);
453*01fb4b82SJames Morse 
454*01fb4b82SJames Morse 	add_to_garbage(msc);
455f04046f2SJames Morse }
456f04046f2SJames Morse 
457f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev)
458f04046f2SJames Morse {
459f04046f2SJames Morse 	struct mpam_msc *msc = platform_get_drvdata(pdev);
460f04046f2SJames Morse 
461f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
462f04046f2SJames Morse 	mpam_msc_destroy(msc);
463f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
464f04046f2SJames Morse 
465*01fb4b82SJames Morse 	mpam_free_garbage();
466f04046f2SJames Morse }
467f04046f2SJames Morse 
468f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
469f04046f2SJames Morse {
470f04046f2SJames Morse 	int err;
471f04046f2SJames Morse 	u32 tmp;
472f04046f2SJames Morse 	struct mpam_msc *msc;
473f04046f2SJames Morse 	struct resource *msc_res;
474f04046f2SJames Morse 	struct device *dev = &pdev->dev;
475f04046f2SJames Morse 
476f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
477f04046f2SJames Morse 
478f04046f2SJames Morse 	msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL);
479f04046f2SJames Morse 	if (!msc)
480f04046f2SJames Morse 		return ERR_PTR(-ENOMEM);
481*01fb4b82SJames Morse 	init_garbage(&msc->garbage);
482*01fb4b82SJames Morse 	msc->garbage.pdev = pdev;
483f04046f2SJames Morse 
484f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->probe_lock);
485f04046f2SJames Morse 	if (err)
486f04046f2SJames Morse 		return ERR_PTR(err);
487f04046f2SJames Morse 
488f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->part_sel_lock);
489f04046f2SJames Morse 	if (err)
490f04046f2SJames Morse 		return ERR_PTR(err);
491f04046f2SJames Morse 
492f04046f2SJames Morse 	msc->id = pdev->id;
493f04046f2SJames Morse 	msc->pdev = pdev;
494f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->all_msc_list);
495f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->ris);
496f04046f2SJames Morse 
497f04046f2SJames Morse 	update_msc_accessibility(msc);
498f04046f2SJames Morse 	if (cpumask_empty(&msc->accessibility)) {
499f04046f2SJames Morse 		dev_err_once(dev, "MSC is not accessible from any CPU!");
500f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
501f04046f2SJames Morse 	}
502f04046f2SJames Morse 
503f04046f2SJames Morse 	if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp))
504f04046f2SJames Morse 		msc->iface = MPAM_IFACE_MMIO;
505f04046f2SJames Morse 	else
506f04046f2SJames Morse 		msc->iface = MPAM_IFACE_PCC;
507f04046f2SJames Morse 
508f04046f2SJames Morse 	if (msc->iface == MPAM_IFACE_MMIO) {
509f04046f2SJames Morse 		void __iomem *io;
510f04046f2SJames Morse 
511f04046f2SJames Morse 		io = devm_platform_get_and_ioremap_resource(pdev, 0,
512f04046f2SJames Morse 							    &msc_res);
513f04046f2SJames Morse 		if (IS_ERR(io)) {
514f04046f2SJames Morse 			dev_err_once(dev, "Failed to map MSC base address\n");
515f04046f2SJames Morse 			return ERR_CAST(io);
516f04046f2SJames Morse 		}
517f04046f2SJames Morse 		msc->mapped_hwpage_sz = msc_res->end - msc_res->start;
518f04046f2SJames Morse 		msc->mapped_hwpage = io;
519f04046f2SJames Morse 	} else {
520f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
521f04046f2SJames Morse 	}
522f04046f2SJames Morse 
523f04046f2SJames Morse 	list_add_rcu(&msc->all_msc_list, &mpam_all_msc);
524f04046f2SJames Morse 	platform_set_drvdata(pdev, msc);
525f04046f2SJames Morse 
526f04046f2SJames Morse 	return msc;
527f04046f2SJames Morse }
528f04046f2SJames Morse 
529f04046f2SJames Morse static int fw_num_msc;
530f04046f2SJames Morse 
531f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev)
532f04046f2SJames Morse {
533f04046f2SJames Morse 	int err;
534f04046f2SJames Morse 	struct mpam_msc *msc = NULL;
535f04046f2SJames Morse 	void *plat_data = pdev->dev.platform_data;
536f04046f2SJames Morse 
537f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
538f04046f2SJames Morse 	msc = do_mpam_msc_drv_probe(pdev);
539f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
540f04046f2SJames Morse 
541f04046f2SJames Morse 	if (IS_ERR(msc))
542f04046f2SJames Morse 		return PTR_ERR(msc);
543f04046f2SJames Morse 
544f04046f2SJames Morse 	/* Create RIS entries described by firmware */
545f04046f2SJames Morse 	err = acpi_mpam_parse_resources(msc, plat_data);
546f04046f2SJames Morse 	if (err) {
547f04046f2SJames Morse 		mpam_msc_drv_remove(pdev);
548f04046f2SJames Morse 		return err;
549f04046f2SJames Morse 	}
550f04046f2SJames Morse 
551f04046f2SJames Morse 	if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc)
552f04046f2SJames Morse 		pr_info("Discovered all MSCs\n");
553f04046f2SJames Morse 
554f04046f2SJames Morse 	return 0;
555f04046f2SJames Morse }
556f04046f2SJames Morse 
557f04046f2SJames Morse static struct platform_driver mpam_msc_driver = {
558f04046f2SJames Morse 	.driver = {
559f04046f2SJames Morse 		.name = "mpam_msc",
560f04046f2SJames Morse 	},
561f04046f2SJames Morse 	.probe = mpam_msc_drv_probe,
562f04046f2SJames Morse 	.remove = mpam_msc_drv_remove,
563f04046f2SJames Morse };
564f04046f2SJames Morse 
565f04046f2SJames Morse static int __init mpam_msc_driver_init(void)
566f04046f2SJames Morse {
567f04046f2SJames Morse 	if (!system_supports_mpam())
568f04046f2SJames Morse 		return -EOPNOTSUPP;
569f04046f2SJames Morse 
570f04046f2SJames Morse 	init_srcu_struct(&mpam_srcu);
571f04046f2SJames Morse 
572f04046f2SJames Morse 	fw_num_msc = acpi_mpam_count_msc();
573f04046f2SJames Morse 	if (fw_num_msc <= 0) {
574f04046f2SJames Morse 		pr_err("No MSC devices found in firmware\n");
575f04046f2SJames Morse 		return -EINVAL;
576f04046f2SJames Morse 	}
577f04046f2SJames Morse 
578f04046f2SJames Morse 	return platform_driver_register(&mpam_msc_driver);
579f04046f2SJames Morse }
580f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init);
581