xref: /linux/drivers/resctrl/mpam_devices.c (revision 3bd04fe7d807bbdcfe75b29ca82fae4e2d7dc524)
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>
78f8d0ac1SJames Morse #include <linux/atomic.h>
8f04046f2SJames Morse #include <linux/arm_mpam.h>
9bd221f9fSJames Morse #include <linux/bitfield.h>
10f188a36cSJames Morse #include <linux/bitmap.h>
11f04046f2SJames Morse #include <linux/cacheinfo.h>
128f8d0ac1SJames Morse #include <linux/cpu.h>
13f04046f2SJames Morse #include <linux/cpumask.h>
14f04046f2SJames Morse #include <linux/device.h>
15f04046f2SJames Morse #include <linux/errno.h>
16f04046f2SJames Morse #include <linux/gfp.h>
17f04046f2SJames Morse #include <linux/list.h>
18f04046f2SJames Morse #include <linux/lockdep.h>
19f04046f2SJames Morse #include <linux/mutex.h>
20f04046f2SJames Morse #include <linux/platform_device.h>
21f04046f2SJames Morse #include <linux/printk.h>
22f04046f2SJames Morse #include <linux/srcu.h>
23d02beb06SJames Morse #include <linux/spinlock.h>
24f04046f2SJames Morse #include <linux/types.h>
258f8d0ac1SJames Morse #include <linux/workqueue.h>
26f04046f2SJames Morse 
27f04046f2SJames Morse #include "mpam_internal.h"
28f04046f2SJames Morse 
29f04046f2SJames Morse /*
30f04046f2SJames Morse  * mpam_list_lock protects the SRCU lists when writing. Once the
31f04046f2SJames Morse  * mpam_enabled key is enabled these lists are read-only,
32f04046f2SJames Morse  * unless the error interrupt disables the driver.
33f04046f2SJames Morse  */
34f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock);
35f04046f2SJames Morse static LIST_HEAD(mpam_all_msc);
36f04046f2SJames Morse 
37f04046f2SJames Morse struct srcu_struct mpam_srcu;
38f04046f2SJames Morse 
39f04046f2SJames Morse /*
40f04046f2SJames Morse  * Number of MSCs that have been probed. Once all MSCs have been probed MPAM
41f04046f2SJames Morse  * can be enabled.
42f04046f2SJames Morse  */
43f04046f2SJames Morse static atomic_t mpam_num_msc;
44f04046f2SJames Morse 
458f8d0ac1SJames Morse static int mpam_cpuhp_state;
468f8d0ac1SJames Morse static DEFINE_MUTEX(mpam_cpuhp_state_lock);
478f8d0ac1SJames Morse 
488f8d0ac1SJames Morse /*
49bd221f9fSJames Morse  * The smallest common values for any CPU or MSC in the system.
50bd221f9fSJames Morse  * Generating traffic outside this range will result in screaming interrupts.
51bd221f9fSJames Morse  */
52bd221f9fSJames Morse u16 mpam_partid_max;
53bd221f9fSJames Morse u8 mpam_pmg_max;
54bd221f9fSJames Morse static bool partid_max_init, partid_max_published;
55bd221f9fSJames Morse static DEFINE_SPINLOCK(partid_max_lock);
56bd221f9fSJames Morse 
57bd221f9fSJames Morse /*
588f8d0ac1SJames Morse  * mpam is enabled once all devices have been probed from CPU online callbacks,
598f8d0ac1SJames Morse  * scheduled via this work_struct. If access to an MSC depends on a CPU that
608f8d0ac1SJames Morse  * was not brought online at boot, this can happen surprisingly late.
618f8d0ac1SJames Morse  */
628f8d0ac1SJames Morse static DECLARE_WORK(mpam_enable_work, &mpam_enable);
638f8d0ac1SJames Morse 
648f8d0ac1SJames Morse /*
658f8d0ac1SJames Morse  * All mpam error interrupts indicate a software bug. On receipt, disable the
668f8d0ac1SJames Morse  * driver.
678f8d0ac1SJames Morse  */
688f8d0ac1SJames Morse static DECLARE_WORK(mpam_broken_work, &mpam_disable);
698f8d0ac1SJames Morse 
708f8d0ac1SJames Morse /* When mpam is disabled, the printed reason to aid debugging */
718f8d0ac1SJames Morse static char *mpam_disable_reason;
728f8d0ac1SJames Morse 
73f04046f2SJames Morse /*
7401fb4b82SJames Morse  * An MSC is a physical container for controls and monitors, each identified by
7501fb4b82SJames Morse  * their RIS index. These share a base-address, interrupts and some MMIO
7601fb4b82SJames Morse  * registers. A vMSC is a virtual container for RIS in an MSC that control or
7701fb4b82SJames Morse  * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but
7801fb4b82SJames Morse  * not all RIS in an MSC share a vMSC.
7901fb4b82SJames Morse  *
8001fb4b82SJames Morse  * Components are a group of vMSC that control or monitor the same thing but
8101fb4b82SJames Morse  * are from different MSC, so have different base-address, interrupts etc.
8201fb4b82SJames Morse  * Classes are the set components of the same type.
8301fb4b82SJames Morse  *
8401fb4b82SJames Morse  * The features of a vMSC is the union of the RIS it contains.
8501fb4b82SJames Morse  * The features of a Class and Component are the common subset of the vMSC
8601fb4b82SJames Morse  * they contain.
8701fb4b82SJames Morse  *
8801fb4b82SJames Morse  * e.g. The system cache may have bandwidth controls on multiple interfaces,
8901fb4b82SJames Morse  * for regulating traffic from devices independently of traffic from CPUs.
9001fb4b82SJames Morse  * If these are two RIS in one MSC, they will be treated as controlling
9101fb4b82SJames Morse  * different things, and will not share a vMSC/component/class.
9201fb4b82SJames Morse  *
9301fb4b82SJames Morse  * e.g. The L2 may have one MSC and two RIS, one for cache-controls another
9401fb4b82SJames Morse  * for bandwidth. These two RIS are members of the same vMSC.
9501fb4b82SJames Morse  *
9601fb4b82SJames Morse  * e.g. The set of RIS that make up the L2 are grouped as a component. These
9701fb4b82SJames Morse  * are sometimes termed slices. They should be configured the same, as if there
9801fb4b82SJames Morse  * were only one.
9901fb4b82SJames Morse  *
10001fb4b82SJames Morse  * e.g. The SoC probably has more than one L2, each attached to a distinct set
10101fb4b82SJames Morse  * of CPUs. All the L2 components are grouped as a class.
10201fb4b82SJames Morse  *
10301fb4b82SJames Morse  * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list,
10401fb4b82SJames Morse  * then linked via struct mpam_ris to a vmsc, component and class.
10501fb4b82SJames Morse  * The same MSC may exist under different class->component->vmsc paths, but the
10601fb4b82SJames Morse  * RIS index will be unique.
10701fb4b82SJames Morse  */
10801fb4b82SJames Morse LIST_HEAD(mpam_classes);
10901fb4b82SJames Morse 
11001fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */
11101fb4b82SJames Morse static LLIST_HEAD(mpam_garbage);
11201fb4b82SJames Morse 
11301fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage)
11401fb4b82SJames Morse {
11501fb4b82SJames Morse 	init_llist_node(&garbage->llist);
11601fb4b82SJames Morse }
11701fb4b82SJames Morse 
11801fb4b82SJames Morse #define add_to_garbage(x)				\
11901fb4b82SJames Morse do {							\
12001fb4b82SJames Morse 	__typeof__(x) _x = (x);				\
12101fb4b82SJames Morse 	_x->garbage.to_free = _x;			\
12201fb4b82SJames Morse 	llist_add(&_x->garbage.llist, &mpam_garbage);	\
12301fb4b82SJames Morse } while (0)
12401fb4b82SJames Morse 
12501fb4b82SJames Morse static void mpam_free_garbage(void)
12601fb4b82SJames Morse {
12701fb4b82SJames Morse 	struct mpam_garbage *iter, *tmp;
12801fb4b82SJames Morse 	struct llist_node *to_free = llist_del_all(&mpam_garbage);
12901fb4b82SJames Morse 
13001fb4b82SJames Morse 	if (!to_free)
13101fb4b82SJames Morse 		return;
13201fb4b82SJames Morse 
13301fb4b82SJames Morse 	synchronize_srcu(&mpam_srcu);
13401fb4b82SJames Morse 
13501fb4b82SJames Morse 	llist_for_each_entry_safe(iter, tmp, to_free, llist) {
13601fb4b82SJames Morse 		if (iter->pdev)
13701fb4b82SJames Morse 			devm_kfree(&iter->pdev->dev, iter->to_free);
13801fb4b82SJames Morse 		else
13901fb4b82SJames Morse 			kfree(iter->to_free);
14001fb4b82SJames Morse 	}
14101fb4b82SJames Morse }
14201fb4b82SJames Morse 
1438f8d0ac1SJames Morse static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg)
1448f8d0ac1SJames Morse {
1458f8d0ac1SJames Morse 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
1468f8d0ac1SJames Morse 
1478f8d0ac1SJames Morse 	return readl_relaxed(msc->mapped_hwpage + reg);
1488f8d0ac1SJames Morse }
1498f8d0ac1SJames Morse 
1508f8d0ac1SJames Morse static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg)
1518f8d0ac1SJames Morse {
1528f8d0ac1SJames Morse 	lockdep_assert_held_once(&msc->part_sel_lock);
1538f8d0ac1SJames Morse 	return __mpam_read_reg(msc, reg);
1548f8d0ac1SJames Morse }
1558f8d0ac1SJames Morse 
1568f8d0ac1SJames Morse #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg)
1578f8d0ac1SJames Morse 
158bd221f9fSJames Morse static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val)
159bd221f9fSJames Morse {
160bd221f9fSJames Morse 	WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz);
161bd221f9fSJames Morse 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
162bd221f9fSJames Morse 
163bd221f9fSJames Morse 	writel_relaxed(val, msc->mapped_hwpage + reg);
164bd221f9fSJames Morse }
165bd221f9fSJames Morse 
166bd221f9fSJames Morse static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
167bd221f9fSJames Morse {
168bd221f9fSJames Morse 	lockdep_assert_held_once(&msc->part_sel_lock);
169bd221f9fSJames Morse 	__mpam_write_reg(msc, reg, val);
170bd221f9fSJames Morse }
171bd221f9fSJames Morse 
172bd221f9fSJames Morse #define mpam_write_partsel_reg(msc, reg, val)  _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val)
173bd221f9fSJames Morse 
1748c90dc68SJames Morse static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg)
1758c90dc68SJames Morse {
1768c90dc68SJames Morse 	mpam_mon_sel_lock_held(msc);
1778c90dc68SJames Morse 	return __mpam_read_reg(msc, reg);
1788c90dc68SJames Morse }
1798c90dc68SJames Morse 
1808c90dc68SJames Morse #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg)
1818c90dc68SJames Morse 
1828c90dc68SJames Morse static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
1838c90dc68SJames Morse {
1848c90dc68SJames Morse 	mpam_mon_sel_lock_held(msc);
1858c90dc68SJames Morse 	__mpam_write_reg(msc, reg, val);
1868c90dc68SJames Morse }
1878c90dc68SJames Morse 
1888c90dc68SJames Morse #define mpam_write_monsel_reg(msc, reg, val)   _mpam_write_monsel_reg(msc, MSMON_##reg, val)
1898c90dc68SJames Morse 
190bd221f9fSJames Morse static u64 mpam_msc_read_idr(struct mpam_msc *msc)
191bd221f9fSJames Morse {
192bd221f9fSJames Morse 	u64 idr_high = 0, idr_low;
193bd221f9fSJames Morse 
194bd221f9fSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
195bd221f9fSJames Morse 
196bd221f9fSJames Morse 	idr_low = mpam_read_partsel_reg(msc, IDR);
197bd221f9fSJames Morse 	if (FIELD_GET(MPAMF_IDR_EXT, idr_low))
198bd221f9fSJames Morse 		idr_high = mpam_read_partsel_reg(msc, IDR + 4);
199bd221f9fSJames Morse 
200bd221f9fSJames Morse 	return (idr_high << 32) | idr_low;
201bd221f9fSJames Morse }
202bd221f9fSJames Morse 
203bd221f9fSJames Morse static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc)
204bd221f9fSJames Morse {
205bd221f9fSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
206bd221f9fSJames Morse 
207bd221f9fSJames Morse 	mpam_write_partsel_reg(msc, PART_SEL, partsel);
208bd221f9fSJames Morse }
209bd221f9fSJames Morse 
210bd221f9fSJames Morse static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc)
211bd221f9fSJames Morse {
212bd221f9fSJames Morse 	u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) |
213bd221f9fSJames Morse 		      FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid);
214bd221f9fSJames Morse 
215bd221f9fSJames Morse 	__mpam_part_sel_raw(partsel, msc);
216bd221f9fSJames Morse }
217bd221f9fSJames Morse 
218bd221f9fSJames Morse int mpam_register_requestor(u16 partid_max, u8 pmg_max)
219bd221f9fSJames Morse {
220bd221f9fSJames Morse 	guard(spinlock)(&partid_max_lock);
221bd221f9fSJames Morse 	if (!partid_max_init) {
222bd221f9fSJames Morse 		mpam_partid_max = partid_max;
223bd221f9fSJames Morse 		mpam_pmg_max = pmg_max;
224bd221f9fSJames Morse 		partid_max_init = true;
225bd221f9fSJames Morse 	} else if (!partid_max_published) {
226bd221f9fSJames Morse 		mpam_partid_max = min(mpam_partid_max, partid_max);
227bd221f9fSJames Morse 		mpam_pmg_max = min(mpam_pmg_max, pmg_max);
228bd221f9fSJames Morse 	} else {
229bd221f9fSJames Morse 		/* New requestors can't lower the values */
230bd221f9fSJames Morse 		if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max)
231bd221f9fSJames Morse 			return -EBUSY;
232bd221f9fSJames Morse 	}
233bd221f9fSJames Morse 
234bd221f9fSJames Morse 	return 0;
235bd221f9fSJames Morse }
236bd221f9fSJames Morse EXPORT_SYMBOL(mpam_register_requestor);
237bd221f9fSJames Morse 
23801fb4b82SJames Morse static struct mpam_class *
23901fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type)
24001fb4b82SJames Morse {
24101fb4b82SJames Morse 	struct mpam_class *class;
24201fb4b82SJames Morse 
24301fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
24401fb4b82SJames Morse 
24501fb4b82SJames Morse 	class = kzalloc(sizeof(*class), GFP_KERNEL);
24601fb4b82SJames Morse 	if (!class)
24701fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
24801fb4b82SJames Morse 	init_garbage(&class->garbage);
24901fb4b82SJames Morse 
25001fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->components);
25101fb4b82SJames Morse 	/* Affinity is updated when ris are added */
25201fb4b82SJames Morse 	class->level = level_idx;
25301fb4b82SJames Morse 	class->type = type;
25401fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->classes_list);
25501fb4b82SJames Morse 
25601fb4b82SJames Morse 	list_add_rcu(&class->classes_list, &mpam_classes);
25701fb4b82SJames Morse 
25801fb4b82SJames Morse 	return class;
25901fb4b82SJames Morse }
26001fb4b82SJames Morse 
26101fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class)
26201fb4b82SJames Morse {
26301fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
26401fb4b82SJames Morse 
26501fb4b82SJames Morse 	list_del_rcu(&class->classes_list);
26601fb4b82SJames Morse 	add_to_garbage(class);
26701fb4b82SJames Morse }
26801fb4b82SJames Morse 
26901fb4b82SJames Morse static struct mpam_class *
27001fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type)
27101fb4b82SJames Morse {
27201fb4b82SJames Morse 	struct mpam_class *class;
27301fb4b82SJames Morse 
27401fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
27501fb4b82SJames Morse 
27601fb4b82SJames Morse 	list_for_each_entry(class, &mpam_classes, classes_list) {
27701fb4b82SJames Morse 		if (class->type == type && class->level == level_idx)
27801fb4b82SJames Morse 			return class;
27901fb4b82SJames Morse 	}
28001fb4b82SJames Morse 
28101fb4b82SJames Morse 	return mpam_class_alloc(level_idx, type);
28201fb4b82SJames Morse }
28301fb4b82SJames Morse 
28401fb4b82SJames Morse static struct mpam_component *
28501fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id)
28601fb4b82SJames Morse {
28701fb4b82SJames Morse 	struct mpam_component *comp;
28801fb4b82SJames Morse 
28901fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
29001fb4b82SJames Morse 
29101fb4b82SJames Morse 	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
29201fb4b82SJames Morse 	if (!comp)
29301fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
29401fb4b82SJames Morse 	init_garbage(&comp->garbage);
29501fb4b82SJames Morse 
29601fb4b82SJames Morse 	comp->comp_id = id;
29701fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->vmsc);
29801fb4b82SJames Morse 	/* Affinity is updated when RIS are added */
29901fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->class_list);
30001fb4b82SJames Morse 	comp->class = class;
30101fb4b82SJames Morse 
30201fb4b82SJames Morse 	list_add_rcu(&comp->class_list, &class->components);
30301fb4b82SJames Morse 
30401fb4b82SJames Morse 	return comp;
30501fb4b82SJames Morse }
30601fb4b82SJames Morse 
30701fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp)
30801fb4b82SJames Morse {
30901fb4b82SJames Morse 	struct mpam_class *class = comp->class;
31001fb4b82SJames Morse 
31101fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
31201fb4b82SJames Morse 
31301fb4b82SJames Morse 	list_del_rcu(&comp->class_list);
31401fb4b82SJames Morse 	add_to_garbage(comp);
31501fb4b82SJames Morse 
31601fb4b82SJames Morse 	if (list_empty(&class->components))
31701fb4b82SJames Morse 		mpam_class_destroy(class);
31801fb4b82SJames Morse }
31901fb4b82SJames Morse 
32001fb4b82SJames Morse static struct mpam_component *
32101fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id)
32201fb4b82SJames Morse {
32301fb4b82SJames Morse 	struct mpam_component *comp;
32401fb4b82SJames Morse 
32501fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
32601fb4b82SJames Morse 
32701fb4b82SJames Morse 	list_for_each_entry(comp, &class->components, class_list) {
32801fb4b82SJames Morse 		if (comp->comp_id == id)
32901fb4b82SJames Morse 			return comp;
33001fb4b82SJames Morse 	}
33101fb4b82SJames Morse 
33201fb4b82SJames Morse 	return mpam_component_alloc(class, id);
33301fb4b82SJames Morse }
33401fb4b82SJames Morse 
33501fb4b82SJames Morse static struct mpam_vmsc *
33601fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc)
33701fb4b82SJames Morse {
33801fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
33901fb4b82SJames Morse 
34001fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
34101fb4b82SJames Morse 
34201fb4b82SJames Morse 	vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL);
34301fb4b82SJames Morse 	if (!vmsc)
34401fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
34501fb4b82SJames Morse 	init_garbage(&vmsc->garbage);
34601fb4b82SJames Morse 
34701fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->ris);
34801fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->comp_list);
34901fb4b82SJames Morse 	vmsc->comp = comp;
35001fb4b82SJames Morse 	vmsc->msc = msc;
35101fb4b82SJames Morse 
35201fb4b82SJames Morse 	list_add_rcu(&vmsc->comp_list, &comp->vmsc);
35301fb4b82SJames Morse 
35401fb4b82SJames Morse 	return vmsc;
35501fb4b82SJames Morse }
35601fb4b82SJames Morse 
35701fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc)
35801fb4b82SJames Morse {
35901fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
36001fb4b82SJames Morse 
36101fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
36201fb4b82SJames Morse 
36301fb4b82SJames Morse 	list_del_rcu(&vmsc->comp_list);
36401fb4b82SJames Morse 	add_to_garbage(vmsc);
36501fb4b82SJames Morse 
36601fb4b82SJames Morse 	if (list_empty(&comp->vmsc))
36701fb4b82SJames Morse 		mpam_component_destroy(comp);
36801fb4b82SJames Morse }
36901fb4b82SJames Morse 
37001fb4b82SJames Morse static struct mpam_vmsc *
37101fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc)
37201fb4b82SJames Morse {
37301fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
37401fb4b82SJames Morse 
37501fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
37601fb4b82SJames Morse 
37701fb4b82SJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
37801fb4b82SJames Morse 		if (vmsc->msc->id == msc->id)
37901fb4b82SJames Morse 			return vmsc;
38001fb4b82SJames Morse 	}
38101fb4b82SJames Morse 
38201fb4b82SJames Morse 	return mpam_vmsc_alloc(comp, msc);
38301fb4b82SJames Morse }
38401fb4b82SJames Morse 
38501fb4b82SJames Morse /*
38601fb4b82SJames Morse  * The cacheinfo structures are only populated when CPUs are online.
38701fb4b82SJames Morse  * This helper walks the acpi tables to include offline CPUs too.
38801fb4b82SJames Morse  */
38901fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level,
39001fb4b82SJames Morse 				   cpumask_t *affinity)
39101fb4b82SJames Morse {
39201fb4b82SJames Morse 	return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity);
39301fb4b82SJames Morse }
39401fb4b82SJames Morse 
39501fb4b82SJames Morse /*
39601fb4b82SJames Morse  * cpumask_of_node() only knows about online CPUs. This can't tell us whether
39701fb4b82SJames Morse  * a class is represented on all possible CPUs.
39801fb4b82SJames Morse  */
39901fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity)
40001fb4b82SJames Morse {
40101fb4b82SJames Morse 	int cpu;
40201fb4b82SJames Morse 
40301fb4b82SJames Morse 	for_each_possible_cpu(cpu) {
40401fb4b82SJames Morse 		if (node_id == cpu_to_node(cpu))
40501fb4b82SJames Morse 			cpumask_set_cpu(cpu, affinity);
40601fb4b82SJames Morse 	}
40701fb4b82SJames Morse }
40801fb4b82SJames Morse 
40901fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity,
41001fb4b82SJames Morse 				 enum mpam_class_types type,
41101fb4b82SJames Morse 				 struct mpam_class *class,
41201fb4b82SJames Morse 				 struct mpam_component *comp)
41301fb4b82SJames Morse {
41401fb4b82SJames Morse 	int err;
41501fb4b82SJames Morse 
41601fb4b82SJames Morse 	switch (type) {
41701fb4b82SJames Morse 	case MPAM_CLASS_CACHE:
41801fb4b82SJames Morse 		err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level,
41901fb4b82SJames Morse 						     affinity);
42001fb4b82SJames Morse 		if (err) {
42101fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev,
42201fb4b82SJames Morse 				      "Failed to determine CPU affinity\n");
42301fb4b82SJames Morse 			return err;
42401fb4b82SJames Morse 		}
42501fb4b82SJames Morse 
42601fb4b82SJames Morse 		if (cpumask_empty(affinity))
42701fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n");
42801fb4b82SJames Morse 
42901fb4b82SJames Morse 		break;
43001fb4b82SJames Morse 	case MPAM_CLASS_MEMORY:
43101fb4b82SJames Morse 		get_cpumask_from_node_id(comp->comp_id, affinity);
43201fb4b82SJames Morse 		/* affinity may be empty for CPU-less memory nodes */
43301fb4b82SJames Morse 		break;
43401fb4b82SJames Morse 	case MPAM_CLASS_UNKNOWN:
43501fb4b82SJames Morse 		return 0;
43601fb4b82SJames Morse 	}
43701fb4b82SJames Morse 
43801fb4b82SJames Morse 	cpumask_and(affinity, affinity, &msc->accessibility);
43901fb4b82SJames Morse 
44001fb4b82SJames Morse 	return 0;
44101fb4b82SJames Morse }
44201fb4b82SJames Morse 
44301fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx,
44401fb4b82SJames Morse 				  enum mpam_class_types type, u8 class_id,
44501fb4b82SJames Morse 				  int component_id)
44601fb4b82SJames Morse {
44701fb4b82SJames Morse 	int err;
44801fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
44901fb4b82SJames Morse 	struct mpam_msc_ris *ris;
45001fb4b82SJames Morse 	struct mpam_class *class;
45101fb4b82SJames Morse 	struct mpam_component *comp;
45201fb4b82SJames Morse 	struct platform_device *pdev = msc->pdev;
45301fb4b82SJames Morse 
45401fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
45501fb4b82SJames Morse 
45601fb4b82SJames Morse 	if (ris_idx > MPAM_MSC_MAX_NUM_RIS)
45701fb4b82SJames Morse 		return -EINVAL;
45801fb4b82SJames Morse 
45901fb4b82SJames Morse 	if (test_and_set_bit(ris_idx, &msc->ris_idxs))
46001fb4b82SJames Morse 		return -EBUSY;
46101fb4b82SJames Morse 
46201fb4b82SJames Morse 	ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL);
46301fb4b82SJames Morse 	if (!ris)
46401fb4b82SJames Morse 		return -ENOMEM;
46501fb4b82SJames Morse 	init_garbage(&ris->garbage);
46601fb4b82SJames Morse 	ris->garbage.pdev = pdev;
46701fb4b82SJames Morse 
46801fb4b82SJames Morse 	class = mpam_class_find(class_id, type);
46901fb4b82SJames Morse 	if (IS_ERR(class))
47001fb4b82SJames Morse 		return PTR_ERR(class);
47101fb4b82SJames Morse 
47201fb4b82SJames Morse 	comp = mpam_component_find(class, component_id);
47301fb4b82SJames Morse 	if (IS_ERR(comp)) {
47401fb4b82SJames Morse 		if (list_empty(&class->components))
47501fb4b82SJames Morse 			mpam_class_destroy(class);
47601fb4b82SJames Morse 		return PTR_ERR(comp);
47701fb4b82SJames Morse 	}
47801fb4b82SJames Morse 
47901fb4b82SJames Morse 	vmsc = mpam_vmsc_find(comp, msc);
48001fb4b82SJames Morse 	if (IS_ERR(vmsc)) {
48101fb4b82SJames Morse 		if (list_empty(&comp->vmsc))
48201fb4b82SJames Morse 			mpam_component_destroy(comp);
48301fb4b82SJames Morse 		return PTR_ERR(vmsc);
48401fb4b82SJames Morse 	}
48501fb4b82SJames Morse 
48601fb4b82SJames Morse 	err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp);
48701fb4b82SJames Morse 	if (err) {
48801fb4b82SJames Morse 		if (list_empty(&vmsc->ris))
48901fb4b82SJames Morse 			mpam_vmsc_destroy(vmsc);
49001fb4b82SJames Morse 		return err;
49101fb4b82SJames Morse 	}
49201fb4b82SJames Morse 
49301fb4b82SJames Morse 	ris->ris_idx = ris_idx;
49401fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->msc_list);
49501fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->vmsc_list);
49601fb4b82SJames Morse 	ris->vmsc = vmsc;
49701fb4b82SJames Morse 
49801fb4b82SJames Morse 	cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity);
49901fb4b82SJames Morse 	cpumask_or(&class->affinity, &class->affinity, &ris->affinity);
50001fb4b82SJames Morse 	list_add_rcu(&ris->vmsc_list, &vmsc->ris);
50101fb4b82SJames Morse 	list_add_rcu(&ris->msc_list, &msc->ris);
50201fb4b82SJames Morse 
50301fb4b82SJames Morse 	return 0;
50401fb4b82SJames Morse }
50501fb4b82SJames Morse 
50601fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris)
50701fb4b82SJames Morse {
50801fb4b82SJames Morse 	struct mpam_vmsc *vmsc = ris->vmsc;
50901fb4b82SJames Morse 	struct mpam_msc *msc = vmsc->msc;
51001fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
51101fb4b82SJames Morse 	struct mpam_class *class = comp->class;
51201fb4b82SJames Morse 
51301fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
51401fb4b82SJames Morse 
51501fb4b82SJames Morse 	/*
51601fb4b82SJames Morse 	 * It is assumed affinities don't overlap. If they do the class becomes
51701fb4b82SJames Morse 	 * unusable immediately.
51801fb4b82SJames Morse 	 */
51901fb4b82SJames Morse 	cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity);
52001fb4b82SJames Morse 	cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity);
52101fb4b82SJames Morse 	clear_bit(ris->ris_idx, &msc->ris_idxs);
52201fb4b82SJames Morse 	list_del_rcu(&ris->msc_list);
52301fb4b82SJames Morse 	list_del_rcu(&ris->vmsc_list);
52401fb4b82SJames Morse 	add_to_garbage(ris);
52501fb4b82SJames Morse 
52601fb4b82SJames Morse 	if (list_empty(&vmsc->ris))
52701fb4b82SJames Morse 		mpam_vmsc_destroy(vmsc);
52801fb4b82SJames Morse }
52901fb4b82SJames Morse 
53001fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
53101fb4b82SJames Morse 		    enum mpam_class_types type, u8 class_id, int component_id)
53201fb4b82SJames Morse {
53301fb4b82SJames Morse 	int err;
53401fb4b82SJames Morse 
53501fb4b82SJames Morse 	mutex_lock(&mpam_list_lock);
53601fb4b82SJames Morse 	err = mpam_ris_create_locked(msc, ris_idx, type, class_id,
53701fb4b82SJames Morse 				     component_id);
53801fb4b82SJames Morse 	mutex_unlock(&mpam_list_lock);
53901fb4b82SJames Morse 	if (err)
54001fb4b82SJames Morse 		mpam_free_garbage();
54101fb4b82SJames Morse 
54201fb4b82SJames Morse 	return err;
54301fb4b82SJames Morse }
54401fb4b82SJames Morse 
545bd221f9fSJames Morse static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc,
546bd221f9fSJames Morse 						   u8 ris_idx)
547bd221f9fSJames Morse {
548bd221f9fSJames Morse 	int err;
549bd221f9fSJames Morse 	struct mpam_msc_ris *ris;
550bd221f9fSJames Morse 
551bd221f9fSJames Morse 	lockdep_assert_held(&mpam_list_lock);
552bd221f9fSJames Morse 
553bd221f9fSJames Morse 	if (!test_bit(ris_idx, &msc->ris_idxs)) {
554bd221f9fSJames Morse 		err = mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN,
555bd221f9fSJames Morse 					     0, 0);
556bd221f9fSJames Morse 		if (err)
557bd221f9fSJames Morse 			return ERR_PTR(err);
558bd221f9fSJames Morse 	}
559bd221f9fSJames Morse 
560bd221f9fSJames Morse 	list_for_each_entry(ris, &msc->ris, msc_list) {
561bd221f9fSJames Morse 		if (ris->ris_idx == ris_idx)
562bd221f9fSJames Morse 			return ris;
563bd221f9fSJames Morse 	}
564bd221f9fSJames Morse 
565bd221f9fSJames Morse 	return ERR_PTR(-ENOENT);
566bd221f9fSJames Morse }
567bd221f9fSJames Morse 
5688c90dc68SJames Morse /*
5698c90dc68SJames Morse  * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour
5708c90dc68SJames Morse  * of NRDY, software can use this bit for any purpose" - so hardware might not
5718c90dc68SJames Morse  * implement this - but it isn't RES0.
5728c90dc68SJames Morse  *
5738c90dc68SJames Morse  * Try and see what values stick in this bit. If we can write either value,
5748c90dc68SJames Morse  * its probably not implemented by hardware.
5758c90dc68SJames Morse  */
5768c90dc68SJames Morse static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg)
5778c90dc68SJames Morse {
5788c90dc68SJames Morse 	u32 now;
5798c90dc68SJames Morse 	u64 mon_sel;
5808c90dc68SJames Morse 	bool can_set, can_clear;
5818c90dc68SJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
5828c90dc68SJames Morse 
5838c90dc68SJames Morse 	if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
5848c90dc68SJames Morse 		return false;
5858c90dc68SJames Morse 
5868c90dc68SJames Morse 	mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) |
5878c90dc68SJames Morse 		  FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
5888c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, mon_sel);
5898c90dc68SJames Morse 
5908c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY);
5918c90dc68SJames Morse 	now = _mpam_read_monsel_reg(msc, mon_reg);
5928c90dc68SJames Morse 	can_set = now & MSMON___NRDY;
5938c90dc68SJames Morse 
5948c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, 0);
5958c90dc68SJames Morse 	now = _mpam_read_monsel_reg(msc, mon_reg);
5968c90dc68SJames Morse 	can_clear = !(now & MSMON___NRDY);
5978c90dc68SJames Morse 	mpam_mon_sel_unlock(msc);
5988c90dc68SJames Morse 
5998c90dc68SJames Morse 	return (!can_set || !can_clear);
6008c90dc68SJames Morse }
6018c90dc68SJames Morse 
6028c90dc68SJames Morse #define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg)			\
6038c90dc68SJames Morse 	_mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg)
6048c90dc68SJames Morse 
6058c90dc68SJames Morse static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
6068c90dc68SJames Morse {
6078c90dc68SJames Morse 	int err;
6088c90dc68SJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
6098c90dc68SJames Morse 	struct device *dev = &msc->pdev->dev;
6108c90dc68SJames Morse 	struct mpam_props *props = &ris->props;
6118c90dc68SJames Morse 
6128c90dc68SJames Morse 	lockdep_assert_held(&msc->probe_lock);
6138c90dc68SJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
6148c90dc68SJames Morse 
6158c90dc68SJames Morse 	/* Cache Portion partitioning */
6168c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) {
6178c90dc68SJames Morse 		u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR);
6188c90dc68SJames Morse 
6198c90dc68SJames Morse 		props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features);
6208c90dc68SJames Morse 		if (props->cpbm_wd)
6218c90dc68SJames Morse 			mpam_set_feature(mpam_feat_cpor_part, props);
6228c90dc68SJames Morse 	}
6238c90dc68SJames Morse 
6248c90dc68SJames Morse 	/* Memory bandwidth partitioning */
6258c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) {
6268c90dc68SJames Morse 		u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR);
6278c90dc68SJames Morse 
6288c90dc68SJames Morse 		/* portion bitmap resolution */
6298c90dc68SJames Morse 		props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features);
6308c90dc68SJames Morse 		if (props->mbw_pbm_bits &&
6318c90dc68SJames Morse 		    FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features))
6328c90dc68SJames Morse 			mpam_set_feature(mpam_feat_mbw_part, props);
6338c90dc68SJames Morse 
6348c90dc68SJames Morse 		props->bwa_wd = FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features);
6358c90dc68SJames Morse 		if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features))
6368c90dc68SJames Morse 			mpam_set_feature(mpam_feat_mbw_max, props);
6378c90dc68SJames Morse 	}
6388c90dc68SJames Morse 
6398c90dc68SJames Morse 	/* Performance Monitoring */
6408c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) {
6418c90dc68SJames Morse 		u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR);
6428c90dc68SJames Morse 
6438c90dc68SJames Morse 		/*
6448c90dc68SJames Morse 		 * If the firmware max-nrdy-us property is missing, the
6458c90dc68SJames Morse 		 * CSU counters can't be used. Should we wait forever?
6468c90dc68SJames Morse 		 */
6478c90dc68SJames Morse 		err = device_property_read_u32(&msc->pdev->dev,
6488c90dc68SJames Morse 					       "arm,not-ready-us",
6498c90dc68SJames Morse 					       &msc->nrdy_usec);
6508c90dc68SJames Morse 
6518c90dc68SJames Morse 		if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) {
6528c90dc68SJames Morse 			u32 csumonidr;
6538c90dc68SJames Morse 
6548c90dc68SJames Morse 			csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR);
6558c90dc68SJames Morse 			props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr);
6568c90dc68SJames Morse 			if (props->num_csu_mon) {
6578c90dc68SJames Morse 				bool hw_managed;
6588c90dc68SJames Morse 
6598c90dc68SJames Morse 				mpam_set_feature(mpam_feat_msmon_csu, props);
6608c90dc68SJames Morse 
6618c90dc68SJames Morse 				/* Is NRDY hardware managed? */
6628c90dc68SJames Morse 				hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU);
6638c90dc68SJames Morse 				if (hw_managed)
6648c90dc68SJames Morse 					mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props);
6658c90dc68SJames Morse 			}
6668c90dc68SJames Morse 
6678c90dc68SJames Morse 			/*
6688c90dc68SJames Morse 			 * Accept the missing firmware property if NRDY appears
6698c90dc68SJames Morse 			 * un-implemented.
6708c90dc68SJames Morse 			 */
6718c90dc68SJames Morse 			if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props))
6728c90dc68SJames Morse 				dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware.");
6738c90dc68SJames Morse 		}
6748c90dc68SJames Morse 		if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) {
6758c90dc68SJames Morse 			bool hw_managed;
6768c90dc68SJames Morse 			u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR);
6778c90dc68SJames Morse 
6788c90dc68SJames Morse 			props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr);
6798c90dc68SJames Morse 			if (props->num_mbwu_mon)
6808c90dc68SJames Morse 				mpam_set_feature(mpam_feat_msmon_mbwu, props);
6818c90dc68SJames Morse 
6828c90dc68SJames Morse 			/* Is NRDY hardware managed? */
6838c90dc68SJames Morse 			hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU);
6848c90dc68SJames Morse 			if (hw_managed)
6858c90dc68SJames Morse 				mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props);
6868c90dc68SJames Morse 
6878c90dc68SJames Morse 			/*
6888c90dc68SJames Morse 			 * Don't warn about any missing firmware property for
6898c90dc68SJames Morse 			 * MBWU NRDY - it doesn't make any sense!
6908c90dc68SJames Morse 			 */
6918c90dc68SJames Morse 		}
6928c90dc68SJames Morse 	}
6938c90dc68SJames Morse }
6948c90dc68SJames Morse 
6958f8d0ac1SJames Morse static int mpam_msc_hw_probe(struct mpam_msc *msc)
6968f8d0ac1SJames Morse {
6978f8d0ac1SJames Morse 	u64 idr;
698bd221f9fSJames Morse 	u16 partid_max;
699bd221f9fSJames Morse 	u8 ris_idx, pmg_max;
700bd221f9fSJames Morse 	struct mpam_msc_ris *ris;
7018f8d0ac1SJames Morse 	struct device *dev = &msc->pdev->dev;
7028f8d0ac1SJames Morse 
7038f8d0ac1SJames Morse 	lockdep_assert_held(&msc->probe_lock);
7048f8d0ac1SJames Morse 
7058f8d0ac1SJames Morse 	idr = __mpam_read_reg(msc, MPAMF_AIDR);
7068f8d0ac1SJames Morse 	if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) {
7078f8d0ac1SJames Morse 		dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n");
7088f8d0ac1SJames Morse 		return -EIO;
7098f8d0ac1SJames Morse 	}
7108f8d0ac1SJames Morse 
711bd221f9fSJames Morse 	/* Grab an IDR value to find out how many RIS there are */
712bd221f9fSJames Morse 	mutex_lock(&msc->part_sel_lock);
713bd221f9fSJames Morse 	idr = mpam_msc_read_idr(msc);
714bd221f9fSJames Morse 	mutex_unlock(&msc->part_sel_lock);
715bd221f9fSJames Morse 
716bd221f9fSJames Morse 	msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr);
717bd221f9fSJames Morse 
718bd221f9fSJames Morse 	/* Use these values so partid/pmg always starts with a valid value */
719bd221f9fSJames Morse 	msc->partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
720bd221f9fSJames Morse 	msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
721bd221f9fSJames Morse 
722bd221f9fSJames Morse 	for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) {
723bd221f9fSJames Morse 		mutex_lock(&msc->part_sel_lock);
724bd221f9fSJames Morse 		__mpam_part_sel(ris_idx, 0, msc);
725bd221f9fSJames Morse 		idr = mpam_msc_read_idr(msc);
726bd221f9fSJames Morse 		mutex_unlock(&msc->part_sel_lock);
727bd221f9fSJames Morse 
728bd221f9fSJames Morse 		partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
729bd221f9fSJames Morse 		pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
730bd221f9fSJames Morse 		msc->partid_max = min(msc->partid_max, partid_max);
731bd221f9fSJames Morse 		msc->pmg_max = min(msc->pmg_max, pmg_max);
732bd221f9fSJames Morse 
733bd221f9fSJames Morse 		mutex_lock(&mpam_list_lock);
734bd221f9fSJames Morse 		ris = mpam_get_or_create_ris(msc, ris_idx);
735bd221f9fSJames Morse 		mutex_unlock(&mpam_list_lock);
736bd221f9fSJames Morse 		if (IS_ERR(ris))
737bd221f9fSJames Morse 			return PTR_ERR(ris);
7388c90dc68SJames Morse 		ris->idr = idr;
7398c90dc68SJames Morse 
7408c90dc68SJames Morse 		mutex_lock(&msc->part_sel_lock);
7418c90dc68SJames Morse 		__mpam_part_sel(ris_idx, 0, msc);
7428c90dc68SJames Morse 		mpam_ris_hw_probe(ris);
7438c90dc68SJames Morse 		mutex_unlock(&msc->part_sel_lock);
744bd221f9fSJames Morse 	}
745bd221f9fSJames Morse 
746bd221f9fSJames Morse 	spin_lock(&partid_max_lock);
747bd221f9fSJames Morse 	mpam_partid_max = min(mpam_partid_max, msc->partid_max);
748bd221f9fSJames Morse 	mpam_pmg_max = min(mpam_pmg_max, msc->pmg_max);
749bd221f9fSJames Morse 	spin_unlock(&partid_max_lock);
750bd221f9fSJames Morse 
7518f8d0ac1SJames Morse 	msc->probed = true;
7528f8d0ac1SJames Morse 
7538f8d0ac1SJames Morse 	return 0;
7548f8d0ac1SJames Morse }
7558f8d0ac1SJames Morse 
756f188a36cSJames Morse static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
757f188a36cSJames Morse {
758f188a36cSJames Morse 	u32 num_words, msb;
759f188a36cSJames Morse 	u32 bm = ~0;
760f188a36cSJames Morse 	int i;
761f188a36cSJames Morse 
762f188a36cSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
763f188a36cSJames Morse 
764f188a36cSJames Morse 	if (wd == 0)
765f188a36cSJames Morse 		return;
766f188a36cSJames Morse 
767f188a36cSJames Morse 	/*
768f188a36cSJames Morse 	 * Write all ~0 to all but the last 32bit-word, which may
769f188a36cSJames Morse 	 * have fewer bits...
770f188a36cSJames Morse 	 */
771f188a36cSJames Morse 	num_words = DIV_ROUND_UP(wd, 32);
772f188a36cSJames Morse 	for (i = 0; i < num_words - 1; i++, reg += sizeof(bm))
773f188a36cSJames Morse 		__mpam_write_reg(msc, reg, bm);
774f188a36cSJames Morse 
775f188a36cSJames Morse 	/*
776f188a36cSJames Morse 	 * ....and then the last (maybe) partial 32bit word. When wd is a
777f188a36cSJames Morse 	 * multiple of 32, msb should be 31 to write a full 32bit word.
778f188a36cSJames Morse 	 */
779f188a36cSJames Morse 	msb = (wd - 1) % 32;
780f188a36cSJames Morse 	bm = GENMASK(msb, 0);
781f188a36cSJames Morse 	__mpam_write_reg(msc, reg, bm);
782f188a36cSJames Morse }
783f188a36cSJames Morse 
784f188a36cSJames Morse static void mpam_reset_ris_partid(struct mpam_msc_ris *ris, u16 partid)
785f188a36cSJames Morse {
786f188a36cSJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
787f188a36cSJames Morse 	struct mpam_props *rprops = &ris->props;
788f188a36cSJames Morse 
789f188a36cSJames Morse 	WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu)));
790f188a36cSJames Morse 
791f188a36cSJames Morse 	mutex_lock(&msc->part_sel_lock);
792f188a36cSJames Morse 	__mpam_part_sel(ris->ris_idx, partid, msc);
793f188a36cSJames Morse 
794f188a36cSJames Morse 	if (mpam_has_feature(mpam_feat_cpor_part, rprops))
795f188a36cSJames Morse 		mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd);
796f188a36cSJames Morse 
797f188a36cSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_part, rprops))
798f188a36cSJames Morse 		mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
799f188a36cSJames Morse 
800f188a36cSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_min, rprops))
801f188a36cSJames Morse 		mpam_write_partsel_reg(msc, MBW_MIN, 0);
802f188a36cSJames Morse 
803f188a36cSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_max, rprops))
804f188a36cSJames Morse 		mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX);
805f188a36cSJames Morse 
806f188a36cSJames Morse 	mutex_unlock(&msc->part_sel_lock);
807f188a36cSJames Morse }
808f188a36cSJames Morse 
809475228d1SJames Morse /*
810475228d1SJames Morse  * Called via smp_call_on_cpu() to prevent migration, while still being
811*3bd04fe7SJames Morse  * pre-emptible. Caller must hold mpam_srcu.
812475228d1SJames Morse  */
813475228d1SJames Morse static int mpam_reset_ris(void *arg)
814f188a36cSJames Morse {
815f188a36cSJames Morse 	u16 partid, partid_max;
816475228d1SJames Morse 	struct mpam_msc_ris *ris = arg;
817f188a36cSJames Morse 
818f188a36cSJames Morse 	if (ris->in_reset_state)
819475228d1SJames Morse 		return 0;
820f188a36cSJames Morse 
821f188a36cSJames Morse 	spin_lock(&partid_max_lock);
822f188a36cSJames Morse 	partid_max = mpam_partid_max;
823f188a36cSJames Morse 	spin_unlock(&partid_max_lock);
824f188a36cSJames Morse 	for (partid = 0; partid <= partid_max; partid++)
825f188a36cSJames Morse 		mpam_reset_ris_partid(ris, partid);
826475228d1SJames Morse 
827475228d1SJames Morse 	return 0;
828475228d1SJames Morse }
829475228d1SJames Morse 
830475228d1SJames Morse /*
831475228d1SJames Morse  * Get the preferred CPU for this MSC. If it is accessible from this CPU,
832475228d1SJames Morse  * this CPU is preferred. This can be preempted/migrated, it will only result
833475228d1SJames Morse  * in more work.
834475228d1SJames Morse  */
835475228d1SJames Morse static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc)
836475228d1SJames Morse {
837475228d1SJames Morse 	int cpu = raw_smp_processor_id();
838475228d1SJames Morse 
839475228d1SJames Morse 	if (cpumask_test_cpu(cpu, &msc->accessibility))
840475228d1SJames Morse 		return cpu;
841475228d1SJames Morse 
842475228d1SJames Morse 	return cpumask_first_and(&msc->accessibility, cpu_online_mask);
843475228d1SJames Morse }
844475228d1SJames Morse 
845475228d1SJames Morse static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *arg)
846475228d1SJames Morse {
847475228d1SJames Morse 	lockdep_assert_irqs_enabled();
848475228d1SJames Morse 	lockdep_assert_cpus_held();
849475228d1SJames Morse 	WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu)));
850475228d1SJames Morse 
851475228d1SJames Morse 	return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true);
852f188a36cSJames Morse }
853f188a36cSJames Morse 
854f188a36cSJames Morse static void mpam_reset_msc(struct mpam_msc *msc, bool online)
855f188a36cSJames Morse {
856f188a36cSJames Morse 	struct mpam_msc_ris *ris;
857f188a36cSJames Morse 
858f188a36cSJames Morse 	list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&mpam_srcu)) {
859475228d1SJames Morse 		mpam_touch_msc(msc, &mpam_reset_ris, ris);
860f188a36cSJames Morse 
861f188a36cSJames Morse 		/*
862f188a36cSJames Morse 		 * Set in_reset_state when coming online. The reset state
863f188a36cSJames Morse 		 * for non-zero partid may be lost while the CPUs are offline.
864f188a36cSJames Morse 		 */
865f188a36cSJames Morse 		ris->in_reset_state = online;
866f188a36cSJames Morse 	}
867f188a36cSJames Morse }
868f188a36cSJames Morse 
8698f8d0ac1SJames Morse static int mpam_cpu_online(unsigned int cpu)
8708f8d0ac1SJames Morse {
871f188a36cSJames Morse 	struct mpam_msc *msc;
872f188a36cSJames Morse 
873f188a36cSJames Morse 	guard(srcu)(&mpam_srcu);
874f188a36cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
875f188a36cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
876f188a36cSJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
877f188a36cSJames Morse 			continue;
878f188a36cSJames Morse 
879f188a36cSJames Morse 		if (atomic_fetch_inc(&msc->online_refs) == 0)
880f188a36cSJames Morse 			mpam_reset_msc(msc, true);
881f188a36cSJames Morse 	}
882f188a36cSJames Morse 
8838f8d0ac1SJames Morse 	return 0;
8848f8d0ac1SJames Morse }
8858f8d0ac1SJames Morse 
8868f8d0ac1SJames Morse /* Before mpam is enabled, try to probe new MSC */
8878f8d0ac1SJames Morse static int mpam_discovery_cpu_online(unsigned int cpu)
8888f8d0ac1SJames Morse {
8898f8d0ac1SJames Morse 	int err = 0;
8908f8d0ac1SJames Morse 	struct mpam_msc *msc;
8918f8d0ac1SJames Morse 	bool new_device_probed = false;
8928f8d0ac1SJames Morse 
8938f8d0ac1SJames Morse 	guard(srcu)(&mpam_srcu);
8948f8d0ac1SJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
8958f8d0ac1SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
8968f8d0ac1SJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
8978f8d0ac1SJames Morse 			continue;
8988f8d0ac1SJames Morse 
8998f8d0ac1SJames Morse 		mutex_lock(&msc->probe_lock);
9008f8d0ac1SJames Morse 		if (!msc->probed)
9018f8d0ac1SJames Morse 			err = mpam_msc_hw_probe(msc);
9028f8d0ac1SJames Morse 		mutex_unlock(&msc->probe_lock);
9038f8d0ac1SJames Morse 
9048f8d0ac1SJames Morse 		if (err)
9058f8d0ac1SJames Morse 			break;
9068f8d0ac1SJames Morse 		new_device_probed = true;
9078f8d0ac1SJames Morse 	}
9088f8d0ac1SJames Morse 
9098f8d0ac1SJames Morse 	if (new_device_probed && !err)
9108f8d0ac1SJames Morse 		schedule_work(&mpam_enable_work);
9118f8d0ac1SJames Morse 	if (err) {
9128f8d0ac1SJames Morse 		mpam_disable_reason = "error during probing";
9138f8d0ac1SJames Morse 		schedule_work(&mpam_broken_work);
9148f8d0ac1SJames Morse 	}
9158f8d0ac1SJames Morse 
9168f8d0ac1SJames Morse 	return err;
9178f8d0ac1SJames Morse }
9188f8d0ac1SJames Morse 
9198f8d0ac1SJames Morse static int mpam_cpu_offline(unsigned int cpu)
9208f8d0ac1SJames Morse {
921f188a36cSJames Morse 	struct mpam_msc *msc;
922f188a36cSJames Morse 
923f188a36cSJames Morse 	guard(srcu)(&mpam_srcu);
924f188a36cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
925f188a36cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
926f188a36cSJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
927f188a36cSJames Morse 			continue;
928f188a36cSJames Morse 
929f188a36cSJames Morse 		if (atomic_dec_and_test(&msc->online_refs))
930f188a36cSJames Morse 			mpam_reset_msc(msc, false);
931f188a36cSJames Morse 	}
932f188a36cSJames Morse 
9338f8d0ac1SJames Morse 	return 0;
9348f8d0ac1SJames Morse }
9358f8d0ac1SJames Morse 
9368f8d0ac1SJames Morse static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online),
9378f8d0ac1SJames Morse 					  int (*offline)(unsigned int offline),
9388f8d0ac1SJames Morse 					  char *name)
9398f8d0ac1SJames Morse {
9408f8d0ac1SJames Morse 	mutex_lock(&mpam_cpuhp_state_lock);
9418f8d0ac1SJames Morse 	if (mpam_cpuhp_state) {
9428f8d0ac1SJames Morse 		cpuhp_remove_state(mpam_cpuhp_state);
9438f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
9448f8d0ac1SJames Morse 	}
9458f8d0ac1SJames Morse 
9468f8d0ac1SJames Morse 	mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online,
9478f8d0ac1SJames Morse 					     offline);
9488f8d0ac1SJames Morse 	if (mpam_cpuhp_state <= 0) {
9498f8d0ac1SJames Morse 		pr_err("Failed to register cpuhp callbacks");
9508f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
9518f8d0ac1SJames Morse 	}
9528f8d0ac1SJames Morse 	mutex_unlock(&mpam_cpuhp_state_lock);
9538f8d0ac1SJames Morse }
9548f8d0ac1SJames Morse 
95501fb4b82SJames Morse /*
956f04046f2SJames Morse  * An MSC can control traffic from a set of CPUs, but may only be accessible
957f04046f2SJames Morse  * from a (hopefully wider) set of CPUs. The common reason for this is power
958f04046f2SJames Morse  * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the
959f04046f2SJames Morse  * corresponding cache may also be powered off. By making accesses from
960f04046f2SJames Morse  * one of those CPUs, we ensure we don't access a cache that's powered off.
961f04046f2SJames Morse  */
962f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc)
963f04046f2SJames Morse {
964f04046f2SJames Morse 	u32 affinity_id;
965f04046f2SJames Morse 	int err;
966f04046f2SJames Morse 
967f04046f2SJames Morse 	err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity",
968f04046f2SJames Morse 				       &affinity_id);
969f04046f2SJames Morse 	if (err)
970f04046f2SJames Morse 		cpumask_copy(&msc->accessibility, cpu_possible_mask);
971f04046f2SJames Morse 	else
972f04046f2SJames Morse 		acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility);
973f04046f2SJames Morse }
974f04046f2SJames Morse 
97501fb4b82SJames Morse /*
97601fb4b82SJames Morse  * There are two ways of reaching a struct mpam_msc_ris. Via the
97701fb4b82SJames Morse  * class->component->vmsc->ris, or via the msc.
97801fb4b82SJames Morse  * When destroying the msc, the other side needs unlinking and cleaning up too.
97901fb4b82SJames Morse  */
980f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc)
981f04046f2SJames Morse {
982f04046f2SJames Morse 	struct platform_device *pdev = msc->pdev;
98301fb4b82SJames Morse 	struct mpam_msc_ris *ris, *tmp;
984f04046f2SJames Morse 
985f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
986f04046f2SJames Morse 
98701fb4b82SJames Morse 	list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list)
98801fb4b82SJames Morse 		mpam_ris_destroy(ris);
98901fb4b82SJames Morse 
990f04046f2SJames Morse 	list_del_rcu(&msc->all_msc_list);
991f04046f2SJames Morse 	platform_set_drvdata(pdev, NULL);
99201fb4b82SJames Morse 
99301fb4b82SJames Morse 	add_to_garbage(msc);
994f04046f2SJames Morse }
995f04046f2SJames Morse 
996f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev)
997f04046f2SJames Morse {
998f04046f2SJames Morse 	struct mpam_msc *msc = platform_get_drvdata(pdev);
999f04046f2SJames Morse 
1000f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
1001f04046f2SJames Morse 	mpam_msc_destroy(msc);
1002f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
1003f04046f2SJames Morse 
100401fb4b82SJames Morse 	mpam_free_garbage();
1005f04046f2SJames Morse }
1006f04046f2SJames Morse 
1007f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
1008f04046f2SJames Morse {
1009f04046f2SJames Morse 	int err;
1010f04046f2SJames Morse 	u32 tmp;
1011f04046f2SJames Morse 	struct mpam_msc *msc;
1012f04046f2SJames Morse 	struct resource *msc_res;
1013f04046f2SJames Morse 	struct device *dev = &pdev->dev;
1014f04046f2SJames Morse 
1015f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
1016f04046f2SJames Morse 
1017f04046f2SJames Morse 	msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL);
1018f04046f2SJames Morse 	if (!msc)
1019f04046f2SJames Morse 		return ERR_PTR(-ENOMEM);
102001fb4b82SJames Morse 	init_garbage(&msc->garbage);
102101fb4b82SJames Morse 	msc->garbage.pdev = pdev;
1022f04046f2SJames Morse 
1023f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->probe_lock);
1024f04046f2SJames Morse 	if (err)
1025f04046f2SJames Morse 		return ERR_PTR(err);
1026f04046f2SJames Morse 
1027f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->part_sel_lock);
1028f04046f2SJames Morse 	if (err)
1029f04046f2SJames Morse 		return ERR_PTR(err);
1030f04046f2SJames Morse 
1031d02beb06SJames Morse 	mpam_mon_sel_lock_init(msc);
1032f04046f2SJames Morse 	msc->id = pdev->id;
1033f04046f2SJames Morse 	msc->pdev = pdev;
1034f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->all_msc_list);
1035f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->ris);
1036f04046f2SJames Morse 
1037f04046f2SJames Morse 	update_msc_accessibility(msc);
1038f04046f2SJames Morse 	if (cpumask_empty(&msc->accessibility)) {
1039f04046f2SJames Morse 		dev_err_once(dev, "MSC is not accessible from any CPU!");
1040f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
1041f04046f2SJames Morse 	}
1042f04046f2SJames Morse 
1043f04046f2SJames Morse 	if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp))
1044f04046f2SJames Morse 		msc->iface = MPAM_IFACE_MMIO;
1045f04046f2SJames Morse 	else
1046f04046f2SJames Morse 		msc->iface = MPAM_IFACE_PCC;
1047f04046f2SJames Morse 
1048f04046f2SJames Morse 	if (msc->iface == MPAM_IFACE_MMIO) {
1049f04046f2SJames Morse 		void __iomem *io;
1050f04046f2SJames Morse 
1051f04046f2SJames Morse 		io = devm_platform_get_and_ioremap_resource(pdev, 0,
1052f04046f2SJames Morse 							    &msc_res);
1053f04046f2SJames Morse 		if (IS_ERR(io)) {
1054f04046f2SJames Morse 			dev_err_once(dev, "Failed to map MSC base address\n");
1055f04046f2SJames Morse 			return ERR_CAST(io);
1056f04046f2SJames Morse 		}
1057f04046f2SJames Morse 		msc->mapped_hwpage_sz = msc_res->end - msc_res->start;
1058f04046f2SJames Morse 		msc->mapped_hwpage = io;
1059f04046f2SJames Morse 	} else {
1060f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
1061f04046f2SJames Morse 	}
1062f04046f2SJames Morse 
1063f04046f2SJames Morse 	list_add_rcu(&msc->all_msc_list, &mpam_all_msc);
1064f04046f2SJames Morse 	platform_set_drvdata(pdev, msc);
1065f04046f2SJames Morse 
1066f04046f2SJames Morse 	return msc;
1067f04046f2SJames Morse }
1068f04046f2SJames Morse 
1069f04046f2SJames Morse static int fw_num_msc;
1070f04046f2SJames Morse 
1071f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev)
1072f04046f2SJames Morse {
1073f04046f2SJames Morse 	int err;
1074f04046f2SJames Morse 	struct mpam_msc *msc = NULL;
1075f04046f2SJames Morse 	void *plat_data = pdev->dev.platform_data;
1076f04046f2SJames Morse 
1077f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
1078f04046f2SJames Morse 	msc = do_mpam_msc_drv_probe(pdev);
1079f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
1080f04046f2SJames Morse 
1081f04046f2SJames Morse 	if (IS_ERR(msc))
1082f04046f2SJames Morse 		return PTR_ERR(msc);
1083f04046f2SJames Morse 
1084f04046f2SJames Morse 	/* Create RIS entries described by firmware */
1085f04046f2SJames Morse 	err = acpi_mpam_parse_resources(msc, plat_data);
1086f04046f2SJames Morse 	if (err) {
1087f04046f2SJames Morse 		mpam_msc_drv_remove(pdev);
1088f04046f2SJames Morse 		return err;
1089f04046f2SJames Morse 	}
1090f04046f2SJames Morse 
1091f04046f2SJames Morse 	if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc)
10928f8d0ac1SJames Morse 		mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL,
10938f8d0ac1SJames Morse 					      "mpam:drv_probe");
1094f04046f2SJames Morse 
1095f04046f2SJames Morse 	return 0;
1096f04046f2SJames Morse }
1097f04046f2SJames Morse 
1098f04046f2SJames Morse static struct platform_driver mpam_msc_driver = {
1099f04046f2SJames Morse 	.driver = {
1100f04046f2SJames Morse 		.name = "mpam_msc",
1101f04046f2SJames Morse 	},
1102f04046f2SJames Morse 	.probe = mpam_msc_drv_probe,
1103f04046f2SJames Morse 	.remove = mpam_msc_drv_remove,
1104f04046f2SJames Morse };
1105f04046f2SJames Morse 
1106c10ca83aSJames Morse /* Any of these features mean the BWA_WD field is valid. */
1107c10ca83aSJames Morse static bool mpam_has_bwa_wd_feature(struct mpam_props *props)
1108c10ca83aSJames Morse {
1109c10ca83aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_min, props))
1110c10ca83aSJames Morse 		return true;
1111c10ca83aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_max, props))
1112c10ca83aSJames Morse 		return true;
1113c10ca83aSJames Morse 	return false;
1114c10ca83aSJames Morse }
1115c10ca83aSJames Morse 
1116c10ca83aSJames Morse #define MISMATCHED_HELPER(parent, child, helper, field, alias)		\
1117c10ca83aSJames Morse 	helper(parent) &&						\
1118c10ca83aSJames Morse 	((helper(child) && (parent)->field != (child)->field) ||	\
1119c10ca83aSJames Morse 	 (!helper(child) && !(alias)))
1120c10ca83aSJames Morse 
1121c10ca83aSJames Morse #define MISMATCHED_FEAT(parent, child, feat, field, alias)		     \
1122c10ca83aSJames Morse 	mpam_has_feature((feat), (parent)) &&				     \
1123c10ca83aSJames Morse 	((mpam_has_feature((feat), (child)) && (parent)->field != (child)->field) || \
1124c10ca83aSJames Morse 	 (!mpam_has_feature((feat), (child)) && !(alias)))
1125c10ca83aSJames Morse 
1126c10ca83aSJames Morse #define CAN_MERGE_FEAT(parent, child, feat, alias)			\
1127c10ca83aSJames Morse 	(alias) && !mpam_has_feature((feat), (parent)) &&		\
1128c10ca83aSJames Morse 	mpam_has_feature((feat), (child))
1129c10ca83aSJames Morse 
1130c10ca83aSJames Morse /*
1131c10ca83aSJames Morse  * Combine two props fields.
1132c10ca83aSJames Morse  * If this is for controls that alias the same resource, it is safe to just
1133c10ca83aSJames Morse  * copy the values over. If two aliasing controls implement the same scheme
1134c10ca83aSJames Morse  * a safe value must be picked.
1135c10ca83aSJames Morse  * For non-aliasing controls, these control different resources, and the
1136c10ca83aSJames Morse  * resulting safe value must be compatible with both. When merging values in
1137c10ca83aSJames Morse  * the tree, all the aliasing resources must be handled first.
1138c10ca83aSJames Morse  * On mismatch, parent is modified.
1139c10ca83aSJames Morse  */
1140c10ca83aSJames Morse static void __props_mismatch(struct mpam_props *parent,
1141c10ca83aSJames Morse 			     struct mpam_props *child, bool alias)
1142c10ca83aSJames Morse {
1143c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) {
1144c10ca83aSJames Morse 		parent->cpbm_wd = child->cpbm_wd;
1145c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part,
1146c10ca83aSJames Morse 				   cpbm_wd, alias)) {
1147c10ca83aSJames Morse 		pr_debug("cleared cpor_part\n");
1148c10ca83aSJames Morse 		mpam_clear_feature(mpam_feat_cpor_part, parent);
1149c10ca83aSJames Morse 		parent->cpbm_wd = 0;
1150c10ca83aSJames Morse 	}
1151c10ca83aSJames Morse 
1152c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) {
1153c10ca83aSJames Morse 		parent->mbw_pbm_bits = child->mbw_pbm_bits;
1154c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part,
1155c10ca83aSJames Morse 				   mbw_pbm_bits, alias)) {
1156c10ca83aSJames Morse 		pr_debug("cleared mbw_part\n");
1157c10ca83aSJames Morse 		mpam_clear_feature(mpam_feat_mbw_part, parent);
1158c10ca83aSJames Morse 		parent->mbw_pbm_bits = 0;
1159c10ca83aSJames Morse 	}
1160c10ca83aSJames Morse 
1161c10ca83aSJames Morse 	/* bwa_wd is a count of bits, fewer bits means less precision */
1162c10ca83aSJames Morse 	if (alias && !mpam_has_bwa_wd_feature(parent) &&
1163c10ca83aSJames Morse 	    mpam_has_bwa_wd_feature(child)) {
1164c10ca83aSJames Morse 		parent->bwa_wd = child->bwa_wd;
1165c10ca83aSJames Morse 	} else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature,
1166c10ca83aSJames Morse 				     bwa_wd, alias)) {
1167c10ca83aSJames Morse 		pr_debug("took the min bwa_wd\n");
1168c10ca83aSJames Morse 		parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd);
1169c10ca83aSJames Morse 	}
1170c10ca83aSJames Morse 
1171c10ca83aSJames Morse 	/* For num properties, take the minimum */
1172c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) {
1173c10ca83aSJames Morse 		parent->num_csu_mon = child->num_csu_mon;
1174c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu,
1175c10ca83aSJames Morse 				   num_csu_mon, alias)) {
1176c10ca83aSJames Morse 		pr_debug("took the min num_csu_mon\n");
1177c10ca83aSJames Morse 		parent->num_csu_mon = min(parent->num_csu_mon,
1178c10ca83aSJames Morse 					  child->num_csu_mon);
1179c10ca83aSJames Morse 	}
1180c10ca83aSJames Morse 
1181c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) {
1182c10ca83aSJames Morse 		parent->num_mbwu_mon = child->num_mbwu_mon;
1183c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu,
1184c10ca83aSJames Morse 				   num_mbwu_mon, alias)) {
1185c10ca83aSJames Morse 		pr_debug("took the min num_mbwu_mon\n");
1186c10ca83aSJames Morse 		parent->num_mbwu_mon = min(parent->num_mbwu_mon,
1187c10ca83aSJames Morse 					   child->num_mbwu_mon);
1188c10ca83aSJames Morse 	}
1189c10ca83aSJames Morse 
1190c10ca83aSJames Morse 	if (alias) {
1191c10ca83aSJames Morse 		/* Merge features for aliased resources */
1192c10ca83aSJames Morse 		bitmap_or(parent->features, parent->features, child->features, MPAM_FEATURE_LAST);
1193c10ca83aSJames Morse 	} else {
1194c10ca83aSJames Morse 		/* Clear missing features for non aliasing */
1195c10ca83aSJames Morse 		bitmap_and(parent->features, parent->features, child->features, MPAM_FEATURE_LAST);
1196c10ca83aSJames Morse 	}
1197c10ca83aSJames Morse }
1198c10ca83aSJames Morse 
1199c10ca83aSJames Morse /*
1200c10ca83aSJames Morse  * If a vmsc doesn't match class feature/configuration, do the right thing(tm).
1201c10ca83aSJames Morse  * For 'num' properties we can just take the minimum.
1202c10ca83aSJames Morse  * For properties where the mismatched unused bits would make a difference, we
1203c10ca83aSJames Morse  * nobble the class feature, as we can't configure all the resources.
1204c10ca83aSJames Morse  * e.g. The L3 cache is composed of two resources with 13 and 17 portion
1205c10ca83aSJames Morse  * bitmaps respectively.
1206c10ca83aSJames Morse  */
1207c10ca83aSJames Morse static void
1208c10ca83aSJames Morse __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc)
1209c10ca83aSJames Morse {
1210c10ca83aSJames Morse 	struct mpam_props *cprops = &class->props;
1211c10ca83aSJames Morse 	struct mpam_props *vprops = &vmsc->props;
1212c10ca83aSJames Morse 	struct device *dev = &vmsc->msc->pdev->dev;
1213c10ca83aSJames Morse 
1214c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock); /* we modify class */
1215c10ca83aSJames Morse 
1216c10ca83aSJames Morse 	dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n",
1217c10ca83aSJames Morse 		(long)cprops->features, (long)vprops->features);
1218c10ca83aSJames Morse 
1219c10ca83aSJames Morse 	/* Take the safe value for any common features */
1220c10ca83aSJames Morse 	__props_mismatch(cprops, vprops, false);
1221c10ca83aSJames Morse }
1222c10ca83aSJames Morse 
1223c10ca83aSJames Morse static void
1224c10ca83aSJames Morse __vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris)
1225c10ca83aSJames Morse {
1226c10ca83aSJames Morse 	struct mpam_props *rprops = &ris->props;
1227c10ca83aSJames Morse 	struct mpam_props *vprops = &vmsc->props;
1228c10ca83aSJames Morse 	struct device *dev = &vmsc->msc->pdev->dev;
1229c10ca83aSJames Morse 
1230c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */
1231c10ca83aSJames Morse 
1232c10ca83aSJames Morse 	dev_dbg(dev, "Merging features for vmsc:0x%lx |= ris:0x%lx\n",
1233c10ca83aSJames Morse 		(long)vprops->features, (long)rprops->features);
1234c10ca83aSJames Morse 
1235c10ca83aSJames Morse 	/*
1236c10ca83aSJames Morse 	 * Merge mismatched features - Copy any features that aren't common,
1237c10ca83aSJames Morse 	 * but take the safe value for any common features.
1238c10ca83aSJames Morse 	 */
1239c10ca83aSJames Morse 	__props_mismatch(vprops, rprops, true);
1240c10ca83aSJames Morse }
1241c10ca83aSJames Morse 
1242c10ca83aSJames Morse /*
1243c10ca83aSJames Morse  * Copy the first component's first vMSC's properties and features to the
1244c10ca83aSJames Morse  * class. __class_props_mismatch() will remove conflicts.
1245c10ca83aSJames Morse  * It is not possible to have a class with no components, or a component with
1246c10ca83aSJames Morse  * no resources. The vMSC properties have already been built.
1247c10ca83aSJames Morse  */
1248c10ca83aSJames Morse static void mpam_enable_init_class_features(struct mpam_class *class)
1249c10ca83aSJames Morse {
1250c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
1251c10ca83aSJames Morse 	struct mpam_component *comp;
1252c10ca83aSJames Morse 
1253c10ca83aSJames Morse 	comp = list_first_entry(&class->components,
1254c10ca83aSJames Morse 				struct mpam_component, class_list);
1255c10ca83aSJames Morse 	vmsc = list_first_entry(&comp->vmsc,
1256c10ca83aSJames Morse 				struct mpam_vmsc, comp_list);
1257c10ca83aSJames Morse 
1258c10ca83aSJames Morse 	class->props = vmsc->props;
1259c10ca83aSJames Morse }
1260c10ca83aSJames Morse 
1261c10ca83aSJames Morse static void mpam_enable_merge_vmsc_features(struct mpam_component *comp)
1262c10ca83aSJames Morse {
1263c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
1264c10ca83aSJames Morse 	struct mpam_msc_ris *ris;
1265c10ca83aSJames Morse 	struct mpam_class *class = comp->class;
1266c10ca83aSJames Morse 
1267c10ca83aSJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
1268c10ca83aSJames Morse 		list_for_each_entry(ris, &vmsc->ris, vmsc_list) {
1269c10ca83aSJames Morse 			__vmsc_props_mismatch(vmsc, ris);
1270c10ca83aSJames Morse 			class->nrdy_usec = max(class->nrdy_usec,
1271c10ca83aSJames Morse 					       vmsc->msc->nrdy_usec);
1272c10ca83aSJames Morse 		}
1273c10ca83aSJames Morse 	}
1274c10ca83aSJames Morse }
1275c10ca83aSJames Morse 
1276c10ca83aSJames Morse static void mpam_enable_merge_class_features(struct mpam_component *comp)
1277c10ca83aSJames Morse {
1278c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
1279c10ca83aSJames Morse 	struct mpam_class *class = comp->class;
1280c10ca83aSJames Morse 
1281c10ca83aSJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list)
1282c10ca83aSJames Morse 		__class_props_mismatch(class, vmsc);
1283c10ca83aSJames Morse }
1284c10ca83aSJames Morse 
1285c10ca83aSJames Morse /*
1286c10ca83aSJames Morse  * Merge all the common resource features into class.
1287c10ca83aSJames Morse  * vmsc features are bitwise-or'd together by mpam_enable_merge_vmsc_features()
1288c10ca83aSJames Morse  * as the first step so that mpam_enable_init_class_features() can initialise
1289c10ca83aSJames Morse  * the class with a representative set of features.
1290c10ca83aSJames Morse  * Next the mpam_enable_merge_class_features() bitwise-and's all the vmsc
1291c10ca83aSJames Morse  * features to form the class features.
1292c10ca83aSJames Morse  * Other features are the min/max as appropriate.
1293c10ca83aSJames Morse  *
1294c10ca83aSJames Morse  * To avoid walking the whole tree twice, the class->nrdy_usec property is
1295c10ca83aSJames Morse  * updated when working with the vmsc as it is a max(), and doesn't need
1296c10ca83aSJames Morse  * initialising first.
1297c10ca83aSJames Morse  */
1298c10ca83aSJames Morse static void mpam_enable_merge_features(struct list_head *all_classes_list)
1299c10ca83aSJames Morse {
1300c10ca83aSJames Morse 	struct mpam_class *class;
1301c10ca83aSJames Morse 	struct mpam_component *comp;
1302c10ca83aSJames Morse 
1303c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock);
1304c10ca83aSJames Morse 
1305c10ca83aSJames Morse 	list_for_each_entry(class, all_classes_list, classes_list) {
1306c10ca83aSJames Morse 		list_for_each_entry(comp, &class->components, class_list)
1307c10ca83aSJames Morse 			mpam_enable_merge_vmsc_features(comp);
1308c10ca83aSJames Morse 
1309c10ca83aSJames Morse 		mpam_enable_init_class_features(class);
1310c10ca83aSJames Morse 
1311c10ca83aSJames Morse 		list_for_each_entry(comp, &class->components, class_list)
1312c10ca83aSJames Morse 			mpam_enable_merge_class_features(comp);
1313c10ca83aSJames Morse 	}
1314c10ca83aSJames Morse }
1315c10ca83aSJames Morse 
13168f8d0ac1SJames Morse static void mpam_enable_once(void)
13178f8d0ac1SJames Morse {
1318bd221f9fSJames Morse 	/*
1319bd221f9fSJames Morse 	 * Once the cpuhp callbacks have been changed, mpam_partid_max can no
1320bd221f9fSJames Morse 	 * longer change.
1321bd221f9fSJames Morse 	 */
1322bd221f9fSJames Morse 	spin_lock(&partid_max_lock);
1323bd221f9fSJames Morse 	partid_max_published = true;
1324bd221f9fSJames Morse 	spin_unlock(&partid_max_lock);
1325bd221f9fSJames Morse 
1326c10ca83aSJames Morse 	mutex_lock(&mpam_list_lock);
1327c10ca83aSJames Morse 	mpam_enable_merge_features(&mpam_classes);
1328c10ca83aSJames Morse 	mutex_unlock(&mpam_list_lock);
1329c10ca83aSJames Morse 
13308f8d0ac1SJames Morse 	mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline,
13318f8d0ac1SJames Morse 				      "mpam:online");
13328f8d0ac1SJames Morse 
1333bd221f9fSJames Morse 	/* Use printk() to avoid the pr_fmt adding the function name. */
1334bd221f9fSJames Morse 	printk(KERN_INFO "MPAM enabled with %u PARTIDs and %u PMGs\n",
1335bd221f9fSJames Morse 	       mpam_partid_max + 1, mpam_pmg_max + 1);
13368f8d0ac1SJames Morse }
13378f8d0ac1SJames Morse 
1338*3bd04fe7SJames Morse static void mpam_reset_component_locked(struct mpam_component *comp)
1339*3bd04fe7SJames Morse {
1340*3bd04fe7SJames Morse 	struct mpam_vmsc *vmsc;
1341*3bd04fe7SJames Morse 
1342*3bd04fe7SJames Morse 	lockdep_assert_cpus_held();
1343*3bd04fe7SJames Morse 
1344*3bd04fe7SJames Morse 	guard(srcu)(&mpam_srcu);
1345*3bd04fe7SJames Morse 	list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list,
1346*3bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
1347*3bd04fe7SJames Morse 		struct mpam_msc *msc = vmsc->msc;
1348*3bd04fe7SJames Morse 		struct mpam_msc_ris *ris;
1349*3bd04fe7SJames Morse 
1350*3bd04fe7SJames Morse 		list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
1351*3bd04fe7SJames Morse 					 srcu_read_lock_held(&mpam_srcu)) {
1352*3bd04fe7SJames Morse 			if (!ris->in_reset_state)
1353*3bd04fe7SJames Morse 				mpam_touch_msc(msc, mpam_reset_ris, ris);
1354*3bd04fe7SJames Morse 			ris->in_reset_state = true;
1355*3bd04fe7SJames Morse 		}
1356*3bd04fe7SJames Morse 	}
1357*3bd04fe7SJames Morse }
1358*3bd04fe7SJames Morse 
1359*3bd04fe7SJames Morse static void mpam_reset_class_locked(struct mpam_class *class)
1360*3bd04fe7SJames Morse {
1361*3bd04fe7SJames Morse 	struct mpam_component *comp;
1362*3bd04fe7SJames Morse 
1363*3bd04fe7SJames Morse 	lockdep_assert_cpus_held();
1364*3bd04fe7SJames Morse 
1365*3bd04fe7SJames Morse 	guard(srcu)(&mpam_srcu);
1366*3bd04fe7SJames Morse 	list_for_each_entry_srcu(comp, &class->components, class_list,
1367*3bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu))
1368*3bd04fe7SJames Morse 		mpam_reset_component_locked(comp);
1369*3bd04fe7SJames Morse }
1370*3bd04fe7SJames Morse 
1371*3bd04fe7SJames Morse static void mpam_reset_class(struct mpam_class *class)
1372*3bd04fe7SJames Morse {
1373*3bd04fe7SJames Morse 	cpus_read_lock();
1374*3bd04fe7SJames Morse 	mpam_reset_class_locked(class);
1375*3bd04fe7SJames Morse 	cpus_read_unlock();
1376*3bd04fe7SJames Morse }
1377*3bd04fe7SJames Morse 
1378*3bd04fe7SJames Morse /*
1379*3bd04fe7SJames Morse  * Called in response to an error IRQ.
1380*3bd04fe7SJames Morse  * All of MPAMs errors indicate a software bug, restore any modified
1381*3bd04fe7SJames Morse  * controls to their reset values.
1382*3bd04fe7SJames Morse  */
13838f8d0ac1SJames Morse void mpam_disable(struct work_struct *ignored)
13848f8d0ac1SJames Morse {
1385*3bd04fe7SJames Morse 	int idx;
1386*3bd04fe7SJames Morse 	struct mpam_class *class;
13878f8d0ac1SJames Morse 	struct mpam_msc *msc, *tmp;
13888f8d0ac1SJames Morse 
13898f8d0ac1SJames Morse 	mutex_lock(&mpam_cpuhp_state_lock);
13908f8d0ac1SJames Morse 	if (mpam_cpuhp_state) {
13918f8d0ac1SJames Morse 		cpuhp_remove_state(mpam_cpuhp_state);
13928f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
13938f8d0ac1SJames Morse 	}
13948f8d0ac1SJames Morse 	mutex_unlock(&mpam_cpuhp_state_lock);
13958f8d0ac1SJames Morse 
1396*3bd04fe7SJames Morse 	idx = srcu_read_lock(&mpam_srcu);
1397*3bd04fe7SJames Morse 	list_for_each_entry_srcu(class, &mpam_classes, classes_list,
1398*3bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu))
1399*3bd04fe7SJames Morse 		mpam_reset_class(class);
1400*3bd04fe7SJames Morse 	srcu_read_unlock(&mpam_srcu, idx);
1401*3bd04fe7SJames Morse 
14028f8d0ac1SJames Morse 	mutex_lock(&mpam_list_lock);
14038f8d0ac1SJames Morse 	list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list)
14048f8d0ac1SJames Morse 		mpam_msc_destroy(msc);
14058f8d0ac1SJames Morse 	mutex_unlock(&mpam_list_lock);
14068f8d0ac1SJames Morse 	mpam_free_garbage();
14078f8d0ac1SJames Morse 
14088f8d0ac1SJames Morse 	pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason);
14098f8d0ac1SJames Morse }
14108f8d0ac1SJames Morse 
14118f8d0ac1SJames Morse /*
14128f8d0ac1SJames Morse  * Enable mpam once all devices have been probed.
14138f8d0ac1SJames Morse  * Scheduled by mpam_discovery_cpu_online() once all devices have been created.
14148f8d0ac1SJames Morse  * Also scheduled when new devices are probed when new CPUs come online.
14158f8d0ac1SJames Morse  */
14168f8d0ac1SJames Morse void mpam_enable(struct work_struct *work)
14178f8d0ac1SJames Morse {
14188f8d0ac1SJames Morse 	static atomic_t once;
14198f8d0ac1SJames Morse 	struct mpam_msc *msc;
14208f8d0ac1SJames Morse 	bool all_devices_probed = true;
14218f8d0ac1SJames Morse 
14228f8d0ac1SJames Morse 	/* Have we probed all the hw devices? */
14238f8d0ac1SJames Morse 	guard(srcu)(&mpam_srcu);
14248f8d0ac1SJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
14258f8d0ac1SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
14268f8d0ac1SJames Morse 		mutex_lock(&msc->probe_lock);
14278f8d0ac1SJames Morse 		if (!msc->probed)
14288f8d0ac1SJames Morse 			all_devices_probed = false;
14298f8d0ac1SJames Morse 		mutex_unlock(&msc->probe_lock);
14308f8d0ac1SJames Morse 
14318f8d0ac1SJames Morse 		if (!all_devices_probed)
14328f8d0ac1SJames Morse 			break;
14338f8d0ac1SJames Morse 	}
14348f8d0ac1SJames Morse 
14358f8d0ac1SJames Morse 	if (all_devices_probed && !atomic_fetch_inc(&once))
14368f8d0ac1SJames Morse 		mpam_enable_once();
14378f8d0ac1SJames Morse }
14388f8d0ac1SJames Morse 
1439f04046f2SJames Morse static int __init mpam_msc_driver_init(void)
1440f04046f2SJames Morse {
1441f04046f2SJames Morse 	if (!system_supports_mpam())
1442f04046f2SJames Morse 		return -EOPNOTSUPP;
1443f04046f2SJames Morse 
1444f04046f2SJames Morse 	init_srcu_struct(&mpam_srcu);
1445f04046f2SJames Morse 
1446f04046f2SJames Morse 	fw_num_msc = acpi_mpam_count_msc();
1447f04046f2SJames Morse 	if (fw_num_msc <= 0) {
1448f04046f2SJames Morse 		pr_err("No MSC devices found in firmware\n");
1449f04046f2SJames Morse 		return -EINVAL;
1450f04046f2SJames Morse 	}
1451f04046f2SJames Morse 
1452f04046f2SJames Morse 	return platform_driver_register(&mpam_msc_driver);
1453f04046f2SJames Morse }
1454bd221f9fSJames Morse 
1455bd221f9fSJames Morse /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */
1456f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init);
1457