xref: /linux/drivers/resctrl/mpam_devices.c (revision c2803bd580db226008aabf2fb2f0c9a7d3b5d0de)
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>
1749aa621cSJames Morse #include <linux/interrupt.h>
1849aa621cSJames Morse #include <linux/irq.h>
1949aa621cSJames Morse #include <linux/irqdesc.h>
20f04046f2SJames Morse #include <linux/list.h>
21f04046f2SJames Morse #include <linux/lockdep.h>
22f04046f2SJames Morse #include <linux/mutex.h>
23f04046f2SJames Morse #include <linux/platform_device.h>
24f04046f2SJames Morse #include <linux/printk.h>
25f04046f2SJames Morse #include <linux/srcu.h>
26d02beb06SJames Morse #include <linux/spinlock.h>
27f04046f2SJames Morse #include <linux/types.h>
288f8d0ac1SJames Morse #include <linux/workqueue.h>
29f04046f2SJames Morse 
30f04046f2SJames Morse #include "mpam_internal.h"
31f04046f2SJames Morse 
323796f75aSJames Morse DEFINE_STATIC_KEY_FALSE(mpam_enabled); /* This moves to arch code */
333796f75aSJames Morse 
34f04046f2SJames Morse /*
35f04046f2SJames Morse  * mpam_list_lock protects the SRCU lists when writing. Once the
36f04046f2SJames Morse  * mpam_enabled key is enabled these lists are read-only,
37f04046f2SJames Morse  * unless the error interrupt disables the driver.
38f04046f2SJames Morse  */
39f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock);
40f04046f2SJames Morse static LIST_HEAD(mpam_all_msc);
41f04046f2SJames Morse 
42f04046f2SJames Morse struct srcu_struct mpam_srcu;
43f04046f2SJames Morse 
44f04046f2SJames Morse /*
45f04046f2SJames Morse  * Number of MSCs that have been probed. Once all MSCs have been probed MPAM
46f04046f2SJames Morse  * can be enabled.
47f04046f2SJames Morse  */
48f04046f2SJames Morse static atomic_t mpam_num_msc;
49f04046f2SJames Morse 
508f8d0ac1SJames Morse static int mpam_cpuhp_state;
518f8d0ac1SJames Morse static DEFINE_MUTEX(mpam_cpuhp_state_lock);
528f8d0ac1SJames Morse 
538f8d0ac1SJames Morse /*
54bd221f9fSJames Morse  * The smallest common values for any CPU or MSC in the system.
55bd221f9fSJames Morse  * Generating traffic outside this range will result in screaming interrupts.
56bd221f9fSJames Morse  */
57bd221f9fSJames Morse u16 mpam_partid_max;
58bd221f9fSJames Morse u8 mpam_pmg_max;
59bd221f9fSJames Morse static bool partid_max_init, partid_max_published;
60bd221f9fSJames Morse static DEFINE_SPINLOCK(partid_max_lock);
61bd221f9fSJames Morse 
62bd221f9fSJames Morse /*
638f8d0ac1SJames Morse  * mpam is enabled once all devices have been probed from CPU online callbacks,
648f8d0ac1SJames Morse  * scheduled via this work_struct. If access to an MSC depends on a CPU that
658f8d0ac1SJames Morse  * was not brought online at boot, this can happen surprisingly late.
668f8d0ac1SJames Morse  */
678f8d0ac1SJames Morse static DECLARE_WORK(mpam_enable_work, &mpam_enable);
688f8d0ac1SJames Morse 
698f8d0ac1SJames Morse /*
708f8d0ac1SJames Morse  * All mpam error interrupts indicate a software bug. On receipt, disable the
718f8d0ac1SJames Morse  * driver.
728f8d0ac1SJames Morse  */
738f8d0ac1SJames Morse static DECLARE_WORK(mpam_broken_work, &mpam_disable);
748f8d0ac1SJames Morse 
758f8d0ac1SJames Morse /* When mpam is disabled, the printed reason to aid debugging */
768f8d0ac1SJames Morse static char *mpam_disable_reason;
778f8d0ac1SJames Morse 
78f04046f2SJames Morse /*
7901fb4b82SJames Morse  * An MSC is a physical container for controls and monitors, each identified by
8001fb4b82SJames Morse  * their RIS index. These share a base-address, interrupts and some MMIO
8101fb4b82SJames Morse  * registers. A vMSC is a virtual container for RIS in an MSC that control or
8201fb4b82SJames Morse  * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but
8301fb4b82SJames Morse  * not all RIS in an MSC share a vMSC.
8401fb4b82SJames Morse  *
8501fb4b82SJames Morse  * Components are a group of vMSC that control or monitor the same thing but
8601fb4b82SJames Morse  * are from different MSC, so have different base-address, interrupts etc.
8701fb4b82SJames Morse  * Classes are the set components of the same type.
8801fb4b82SJames Morse  *
8901fb4b82SJames Morse  * The features of a vMSC is the union of the RIS it contains.
9001fb4b82SJames Morse  * The features of a Class and Component are the common subset of the vMSC
9101fb4b82SJames Morse  * they contain.
9201fb4b82SJames Morse  *
9301fb4b82SJames Morse  * e.g. The system cache may have bandwidth controls on multiple interfaces,
9401fb4b82SJames Morse  * for regulating traffic from devices independently of traffic from CPUs.
9501fb4b82SJames Morse  * If these are two RIS in one MSC, they will be treated as controlling
9601fb4b82SJames Morse  * different things, and will not share a vMSC/component/class.
9701fb4b82SJames Morse  *
9801fb4b82SJames Morse  * e.g. The L2 may have one MSC and two RIS, one for cache-controls another
9901fb4b82SJames Morse  * for bandwidth. These two RIS are members of the same vMSC.
10001fb4b82SJames Morse  *
10101fb4b82SJames Morse  * e.g. The set of RIS that make up the L2 are grouped as a component. These
10201fb4b82SJames Morse  * are sometimes termed slices. They should be configured the same, as if there
10301fb4b82SJames Morse  * were only one.
10401fb4b82SJames Morse  *
10501fb4b82SJames Morse  * e.g. The SoC probably has more than one L2, each attached to a distinct set
10601fb4b82SJames Morse  * of CPUs. All the L2 components are grouped as a class.
10701fb4b82SJames Morse  *
10801fb4b82SJames Morse  * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list,
10901fb4b82SJames Morse  * then linked via struct mpam_ris to a vmsc, component and class.
11001fb4b82SJames Morse  * The same MSC may exist under different class->component->vmsc paths, but the
11101fb4b82SJames Morse  * RIS index will be unique.
11201fb4b82SJames Morse  */
11301fb4b82SJames Morse LIST_HEAD(mpam_classes);
11401fb4b82SJames Morse 
11501fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */
11601fb4b82SJames Morse static LLIST_HEAD(mpam_garbage);
11701fb4b82SJames Morse 
11801fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage)
11901fb4b82SJames Morse {
12001fb4b82SJames Morse 	init_llist_node(&garbage->llist);
12101fb4b82SJames Morse }
12201fb4b82SJames Morse 
12301fb4b82SJames Morse #define add_to_garbage(x)				\
12401fb4b82SJames Morse do {							\
12501fb4b82SJames Morse 	__typeof__(x) _x = (x);				\
12601fb4b82SJames Morse 	_x->garbage.to_free = _x;			\
12701fb4b82SJames Morse 	llist_add(&_x->garbage.llist, &mpam_garbage);	\
12801fb4b82SJames Morse } while (0)
12901fb4b82SJames Morse 
13001fb4b82SJames Morse static void mpam_free_garbage(void)
13101fb4b82SJames Morse {
13201fb4b82SJames Morse 	struct mpam_garbage *iter, *tmp;
13301fb4b82SJames Morse 	struct llist_node *to_free = llist_del_all(&mpam_garbage);
13401fb4b82SJames Morse 
13501fb4b82SJames Morse 	if (!to_free)
13601fb4b82SJames Morse 		return;
13701fb4b82SJames Morse 
13801fb4b82SJames Morse 	synchronize_srcu(&mpam_srcu);
13901fb4b82SJames Morse 
14001fb4b82SJames Morse 	llist_for_each_entry_safe(iter, tmp, to_free, llist) {
14101fb4b82SJames Morse 		if (iter->pdev)
14201fb4b82SJames Morse 			devm_kfree(&iter->pdev->dev, iter->to_free);
14301fb4b82SJames Morse 		else
14401fb4b82SJames Morse 			kfree(iter->to_free);
14501fb4b82SJames Morse 	}
14601fb4b82SJames Morse }
14701fb4b82SJames Morse 
14809b89d2aSJames Morse /*
14909b89d2aSJames Morse  * Once mpam is enabled, new requestors cannot further reduce the available
15009b89d2aSJames Morse  * partid. Assert that the size is fixed, and new requestors will be turned
15109b89d2aSJames Morse  * away.
15209b89d2aSJames Morse  */
15309b89d2aSJames Morse static void mpam_assert_partid_sizes_fixed(void)
15409b89d2aSJames Morse {
15509b89d2aSJames Morse 	WARN_ON_ONCE(!partid_max_published);
15609b89d2aSJames Morse }
15709b89d2aSJames Morse 
1588f8d0ac1SJames Morse static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg)
1598f8d0ac1SJames Morse {
1608f8d0ac1SJames Morse 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
1618f8d0ac1SJames Morse 
1628f8d0ac1SJames Morse 	return readl_relaxed(msc->mapped_hwpage + reg);
1638f8d0ac1SJames Morse }
1648f8d0ac1SJames Morse 
1658f8d0ac1SJames Morse static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg)
1668f8d0ac1SJames Morse {
1678f8d0ac1SJames Morse 	lockdep_assert_held_once(&msc->part_sel_lock);
1688f8d0ac1SJames Morse 	return __mpam_read_reg(msc, reg);
1698f8d0ac1SJames Morse }
1708f8d0ac1SJames Morse 
1718f8d0ac1SJames Morse #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg)
1728f8d0ac1SJames Morse 
173bd221f9fSJames Morse static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val)
174bd221f9fSJames Morse {
175bd221f9fSJames Morse 	WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz);
176bd221f9fSJames Morse 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
177bd221f9fSJames Morse 
178bd221f9fSJames Morse 	writel_relaxed(val, msc->mapped_hwpage + reg);
179bd221f9fSJames Morse }
180bd221f9fSJames Morse 
181bd221f9fSJames Morse static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
182bd221f9fSJames Morse {
183bd221f9fSJames Morse 	lockdep_assert_held_once(&msc->part_sel_lock);
184bd221f9fSJames Morse 	__mpam_write_reg(msc, reg, val);
185bd221f9fSJames Morse }
186bd221f9fSJames Morse 
187bd221f9fSJames Morse #define mpam_write_partsel_reg(msc, reg, val)  _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val)
188bd221f9fSJames Morse 
1898c90dc68SJames Morse static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg)
1908c90dc68SJames Morse {
1918c90dc68SJames Morse 	mpam_mon_sel_lock_held(msc);
1928c90dc68SJames Morse 	return __mpam_read_reg(msc, reg);
1938c90dc68SJames Morse }
1948c90dc68SJames Morse 
1958c90dc68SJames Morse #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg)
1968c90dc68SJames Morse 
1978c90dc68SJames Morse static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
1988c90dc68SJames Morse {
1998c90dc68SJames Morse 	mpam_mon_sel_lock_held(msc);
2008c90dc68SJames Morse 	__mpam_write_reg(msc, reg, val);
2018c90dc68SJames Morse }
2028c90dc68SJames Morse 
2038c90dc68SJames Morse #define mpam_write_monsel_reg(msc, reg, val)   _mpam_write_monsel_reg(msc, MSMON_##reg, val)
2048c90dc68SJames Morse 
205bd221f9fSJames Morse static u64 mpam_msc_read_idr(struct mpam_msc *msc)
206bd221f9fSJames Morse {
207bd221f9fSJames Morse 	u64 idr_high = 0, idr_low;
208bd221f9fSJames Morse 
209bd221f9fSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
210bd221f9fSJames Morse 
211bd221f9fSJames Morse 	idr_low = mpam_read_partsel_reg(msc, IDR);
212bd221f9fSJames Morse 	if (FIELD_GET(MPAMF_IDR_EXT, idr_low))
213bd221f9fSJames Morse 		idr_high = mpam_read_partsel_reg(msc, IDR + 4);
214bd221f9fSJames Morse 
215bd221f9fSJames Morse 	return (idr_high << 32) | idr_low;
216bd221f9fSJames Morse }
217bd221f9fSJames Morse 
21849aa621cSJames Morse static void mpam_msc_clear_esr(struct mpam_msc *msc)
21949aa621cSJames Morse {
22049aa621cSJames Morse 	u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR);
22149aa621cSJames Morse 
22249aa621cSJames Morse 	if (!esr_low)
22349aa621cSJames Morse 		return;
22449aa621cSJames Morse 
22549aa621cSJames Morse 	/*
22649aa621cSJames Morse 	 * Clearing the high/low bits of MPAMF_ESR can not be atomic.
22749aa621cSJames Morse 	 * Clear the top half first, so that the pending error bits in the
22849aa621cSJames Morse 	 * lower half prevent hardware from updating either half of the
22949aa621cSJames Morse 	 * register.
23049aa621cSJames Morse 	 */
23149aa621cSJames Morse 	if (msc->has_extd_esr)
23249aa621cSJames Morse 		__mpam_write_reg(msc, MPAMF_ESR + 4, 0);
23349aa621cSJames Morse 	__mpam_write_reg(msc, MPAMF_ESR, 0);
23449aa621cSJames Morse }
23549aa621cSJames Morse 
23649aa621cSJames Morse static u64 mpam_msc_read_esr(struct mpam_msc *msc)
23749aa621cSJames Morse {
23849aa621cSJames Morse 	u64 esr_high = 0, esr_low;
23949aa621cSJames Morse 
24049aa621cSJames Morse 	esr_low = __mpam_read_reg(msc, MPAMF_ESR);
24149aa621cSJames Morse 	if (msc->has_extd_esr)
24249aa621cSJames Morse 		esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4);
24349aa621cSJames Morse 
24449aa621cSJames Morse 	return (esr_high << 32) | esr_low;
24549aa621cSJames Morse }
24649aa621cSJames Morse 
247bd221f9fSJames Morse static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc)
248bd221f9fSJames Morse {
249bd221f9fSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
250bd221f9fSJames Morse 
251bd221f9fSJames Morse 	mpam_write_partsel_reg(msc, PART_SEL, partsel);
252bd221f9fSJames Morse }
253bd221f9fSJames Morse 
254bd221f9fSJames Morse static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc)
255bd221f9fSJames Morse {
256bd221f9fSJames Morse 	u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) |
257bd221f9fSJames Morse 		      FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid);
258bd221f9fSJames Morse 
259bd221f9fSJames Morse 	__mpam_part_sel_raw(partsel, msc);
260bd221f9fSJames Morse }
261bd221f9fSJames Morse 
262880df85dSJames Morse static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc *msc)
263880df85dSJames Morse {
264880df85dSJames Morse 	u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) |
265880df85dSJames Morse 		      FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) |
266880df85dSJames Morse 		      MPAMCFG_PART_SEL_INTERNAL;
267880df85dSJames Morse 
268880df85dSJames Morse 	__mpam_part_sel_raw(partsel, msc);
269880df85dSJames Morse }
270880df85dSJames Morse 
271bd221f9fSJames Morse int mpam_register_requestor(u16 partid_max, u8 pmg_max)
272bd221f9fSJames Morse {
273bd221f9fSJames Morse 	guard(spinlock)(&partid_max_lock);
274bd221f9fSJames Morse 	if (!partid_max_init) {
275bd221f9fSJames Morse 		mpam_partid_max = partid_max;
276bd221f9fSJames Morse 		mpam_pmg_max = pmg_max;
277bd221f9fSJames Morse 		partid_max_init = true;
278bd221f9fSJames Morse 	} else if (!partid_max_published) {
279bd221f9fSJames Morse 		mpam_partid_max = min(mpam_partid_max, partid_max);
280bd221f9fSJames Morse 		mpam_pmg_max = min(mpam_pmg_max, pmg_max);
281bd221f9fSJames Morse 	} else {
282bd221f9fSJames Morse 		/* New requestors can't lower the values */
283bd221f9fSJames Morse 		if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max)
284bd221f9fSJames Morse 			return -EBUSY;
285bd221f9fSJames Morse 	}
286bd221f9fSJames Morse 
287bd221f9fSJames Morse 	return 0;
288bd221f9fSJames Morse }
289bd221f9fSJames Morse EXPORT_SYMBOL(mpam_register_requestor);
290bd221f9fSJames Morse 
29101fb4b82SJames Morse static struct mpam_class *
29201fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type)
29301fb4b82SJames Morse {
29401fb4b82SJames Morse 	struct mpam_class *class;
29501fb4b82SJames Morse 
29601fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
29701fb4b82SJames Morse 
29801fb4b82SJames Morse 	class = kzalloc(sizeof(*class), GFP_KERNEL);
29901fb4b82SJames Morse 	if (!class)
30001fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
30101fb4b82SJames Morse 	init_garbage(&class->garbage);
30201fb4b82SJames Morse 
30301fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->components);
30401fb4b82SJames Morse 	/* Affinity is updated when ris are added */
30501fb4b82SJames Morse 	class->level = level_idx;
30601fb4b82SJames Morse 	class->type = type;
30701fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&class->classes_list);
308c891bae6SJames Morse 	ida_init(&class->ida_csu_mon);
309c891bae6SJames Morse 	ida_init(&class->ida_mbwu_mon);
31001fb4b82SJames Morse 
31101fb4b82SJames Morse 	list_add_rcu(&class->classes_list, &mpam_classes);
31201fb4b82SJames Morse 
31301fb4b82SJames Morse 	return class;
31401fb4b82SJames Morse }
31501fb4b82SJames Morse 
31601fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class)
31701fb4b82SJames Morse {
31801fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
31901fb4b82SJames Morse 
32001fb4b82SJames Morse 	list_del_rcu(&class->classes_list);
32101fb4b82SJames Morse 	add_to_garbage(class);
32201fb4b82SJames Morse }
32301fb4b82SJames Morse 
32401fb4b82SJames Morse static struct mpam_class *
32501fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type)
32601fb4b82SJames Morse {
32701fb4b82SJames Morse 	struct mpam_class *class;
32801fb4b82SJames Morse 
32901fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
33001fb4b82SJames Morse 
33101fb4b82SJames Morse 	list_for_each_entry(class, &mpam_classes, classes_list) {
33201fb4b82SJames Morse 		if (class->type == type && class->level == level_idx)
33301fb4b82SJames Morse 			return class;
33401fb4b82SJames Morse 	}
33501fb4b82SJames Morse 
33601fb4b82SJames Morse 	return mpam_class_alloc(level_idx, type);
33701fb4b82SJames Morse }
33801fb4b82SJames Morse 
33901fb4b82SJames Morse static struct mpam_component *
34001fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id)
34101fb4b82SJames Morse {
34201fb4b82SJames Morse 	struct mpam_component *comp;
34301fb4b82SJames Morse 
34401fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
34501fb4b82SJames Morse 
34601fb4b82SJames Morse 	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
34701fb4b82SJames Morse 	if (!comp)
34801fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
34901fb4b82SJames Morse 	init_garbage(&comp->garbage);
35001fb4b82SJames Morse 
35101fb4b82SJames Morse 	comp->comp_id = id;
35201fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->vmsc);
35301fb4b82SJames Morse 	/* Affinity is updated when RIS are added */
35401fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&comp->class_list);
35501fb4b82SJames Morse 	comp->class = class;
35601fb4b82SJames Morse 
35701fb4b82SJames Morse 	list_add_rcu(&comp->class_list, &class->components);
35801fb4b82SJames Morse 
35901fb4b82SJames Morse 	return comp;
36001fb4b82SJames Morse }
36101fb4b82SJames Morse 
36209b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp);
36309b89d2aSJames Morse 
36401fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp)
36501fb4b82SJames Morse {
36601fb4b82SJames Morse 	struct mpam_class *class = comp->class;
36701fb4b82SJames Morse 
36801fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
36901fb4b82SJames Morse 
37009b89d2aSJames Morse 	__destroy_component_cfg(comp);
37109b89d2aSJames Morse 
37201fb4b82SJames Morse 	list_del_rcu(&comp->class_list);
37301fb4b82SJames Morse 	add_to_garbage(comp);
37401fb4b82SJames Morse 
37501fb4b82SJames Morse 	if (list_empty(&class->components))
37601fb4b82SJames Morse 		mpam_class_destroy(class);
37701fb4b82SJames Morse }
37801fb4b82SJames Morse 
37901fb4b82SJames Morse static struct mpam_component *
38001fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id)
38101fb4b82SJames Morse {
38201fb4b82SJames Morse 	struct mpam_component *comp;
38301fb4b82SJames Morse 
38401fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
38501fb4b82SJames Morse 
38601fb4b82SJames Morse 	list_for_each_entry(comp, &class->components, class_list) {
38701fb4b82SJames Morse 		if (comp->comp_id == id)
38801fb4b82SJames Morse 			return comp;
38901fb4b82SJames Morse 	}
39001fb4b82SJames Morse 
39101fb4b82SJames Morse 	return mpam_component_alloc(class, id);
39201fb4b82SJames Morse }
39301fb4b82SJames Morse 
39401fb4b82SJames Morse static struct mpam_vmsc *
39501fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc)
39601fb4b82SJames Morse {
39701fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
39801fb4b82SJames Morse 
39901fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
40001fb4b82SJames Morse 
40101fb4b82SJames Morse 	vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL);
40201fb4b82SJames Morse 	if (!vmsc)
40301fb4b82SJames Morse 		return ERR_PTR(-ENOMEM);
40401fb4b82SJames Morse 	init_garbage(&vmsc->garbage);
40501fb4b82SJames Morse 
40601fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->ris);
40701fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&vmsc->comp_list);
40801fb4b82SJames Morse 	vmsc->comp = comp;
40901fb4b82SJames Morse 	vmsc->msc = msc;
41001fb4b82SJames Morse 
41101fb4b82SJames Morse 	list_add_rcu(&vmsc->comp_list, &comp->vmsc);
41201fb4b82SJames Morse 
41301fb4b82SJames Morse 	return vmsc;
41401fb4b82SJames Morse }
41501fb4b82SJames Morse 
41601fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc)
41701fb4b82SJames Morse {
41801fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
41901fb4b82SJames Morse 
42001fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
42101fb4b82SJames Morse 
42201fb4b82SJames Morse 	list_del_rcu(&vmsc->comp_list);
42301fb4b82SJames Morse 	add_to_garbage(vmsc);
42401fb4b82SJames Morse 
42501fb4b82SJames Morse 	if (list_empty(&comp->vmsc))
42601fb4b82SJames Morse 		mpam_component_destroy(comp);
42701fb4b82SJames Morse }
42801fb4b82SJames Morse 
42901fb4b82SJames Morse static struct mpam_vmsc *
43001fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc)
43101fb4b82SJames Morse {
43201fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
43301fb4b82SJames Morse 
43401fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
43501fb4b82SJames Morse 
43601fb4b82SJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
43701fb4b82SJames Morse 		if (vmsc->msc->id == msc->id)
43801fb4b82SJames Morse 			return vmsc;
43901fb4b82SJames Morse 	}
44001fb4b82SJames Morse 
44101fb4b82SJames Morse 	return mpam_vmsc_alloc(comp, msc);
44201fb4b82SJames Morse }
44301fb4b82SJames Morse 
44401fb4b82SJames Morse /*
44501fb4b82SJames Morse  * The cacheinfo structures are only populated when CPUs are online.
44601fb4b82SJames Morse  * This helper walks the acpi tables to include offline CPUs too.
44701fb4b82SJames Morse  */
44801fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level,
44901fb4b82SJames Morse 				   cpumask_t *affinity)
45001fb4b82SJames Morse {
45101fb4b82SJames Morse 	return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity);
45201fb4b82SJames Morse }
45301fb4b82SJames Morse 
45401fb4b82SJames Morse /*
45501fb4b82SJames Morse  * cpumask_of_node() only knows about online CPUs. This can't tell us whether
45601fb4b82SJames Morse  * a class is represented on all possible CPUs.
45701fb4b82SJames Morse  */
45801fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity)
45901fb4b82SJames Morse {
46001fb4b82SJames Morse 	int cpu;
46101fb4b82SJames Morse 
46201fb4b82SJames Morse 	for_each_possible_cpu(cpu) {
46301fb4b82SJames Morse 		if (node_id == cpu_to_node(cpu))
46401fb4b82SJames Morse 			cpumask_set_cpu(cpu, affinity);
46501fb4b82SJames Morse 	}
46601fb4b82SJames Morse }
46701fb4b82SJames Morse 
46801fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity,
46901fb4b82SJames Morse 				 enum mpam_class_types type,
47001fb4b82SJames Morse 				 struct mpam_class *class,
47101fb4b82SJames Morse 				 struct mpam_component *comp)
47201fb4b82SJames Morse {
47301fb4b82SJames Morse 	int err;
47401fb4b82SJames Morse 
47501fb4b82SJames Morse 	switch (type) {
47601fb4b82SJames Morse 	case MPAM_CLASS_CACHE:
47701fb4b82SJames Morse 		err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level,
47801fb4b82SJames Morse 						     affinity);
47901fb4b82SJames Morse 		if (err) {
48001fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev,
48101fb4b82SJames Morse 				      "Failed to determine CPU affinity\n");
48201fb4b82SJames Morse 			return err;
48301fb4b82SJames Morse 		}
48401fb4b82SJames Morse 
48501fb4b82SJames Morse 		if (cpumask_empty(affinity))
48601fb4b82SJames Morse 			dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n");
48701fb4b82SJames Morse 
48801fb4b82SJames Morse 		break;
48901fb4b82SJames Morse 	case MPAM_CLASS_MEMORY:
49001fb4b82SJames Morse 		get_cpumask_from_node_id(comp->comp_id, affinity);
49101fb4b82SJames Morse 		/* affinity may be empty for CPU-less memory nodes */
49201fb4b82SJames Morse 		break;
49301fb4b82SJames Morse 	case MPAM_CLASS_UNKNOWN:
49401fb4b82SJames Morse 		return 0;
49501fb4b82SJames Morse 	}
49601fb4b82SJames Morse 
49701fb4b82SJames Morse 	cpumask_and(affinity, affinity, &msc->accessibility);
49801fb4b82SJames Morse 
49901fb4b82SJames Morse 	return 0;
50001fb4b82SJames Morse }
50101fb4b82SJames Morse 
50201fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx,
50301fb4b82SJames Morse 				  enum mpam_class_types type, u8 class_id,
50401fb4b82SJames Morse 				  int component_id)
50501fb4b82SJames Morse {
50601fb4b82SJames Morse 	int err;
50701fb4b82SJames Morse 	struct mpam_vmsc *vmsc;
50801fb4b82SJames Morse 	struct mpam_msc_ris *ris;
50901fb4b82SJames Morse 	struct mpam_class *class;
51001fb4b82SJames Morse 	struct mpam_component *comp;
51101fb4b82SJames Morse 	struct platform_device *pdev = msc->pdev;
51201fb4b82SJames Morse 
51301fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
51401fb4b82SJames Morse 
51501fb4b82SJames Morse 	if (ris_idx > MPAM_MSC_MAX_NUM_RIS)
51601fb4b82SJames Morse 		return -EINVAL;
51701fb4b82SJames Morse 
51801fb4b82SJames Morse 	if (test_and_set_bit(ris_idx, &msc->ris_idxs))
51901fb4b82SJames Morse 		return -EBUSY;
52001fb4b82SJames Morse 
52101fb4b82SJames Morse 	ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL);
52201fb4b82SJames Morse 	if (!ris)
52301fb4b82SJames Morse 		return -ENOMEM;
52401fb4b82SJames Morse 	init_garbage(&ris->garbage);
52501fb4b82SJames Morse 	ris->garbage.pdev = pdev;
52601fb4b82SJames Morse 
52701fb4b82SJames Morse 	class = mpam_class_find(class_id, type);
52801fb4b82SJames Morse 	if (IS_ERR(class))
52901fb4b82SJames Morse 		return PTR_ERR(class);
53001fb4b82SJames Morse 
53101fb4b82SJames Morse 	comp = mpam_component_find(class, component_id);
53201fb4b82SJames Morse 	if (IS_ERR(comp)) {
53301fb4b82SJames Morse 		if (list_empty(&class->components))
53401fb4b82SJames Morse 			mpam_class_destroy(class);
53501fb4b82SJames Morse 		return PTR_ERR(comp);
53601fb4b82SJames Morse 	}
53701fb4b82SJames Morse 
53801fb4b82SJames Morse 	vmsc = mpam_vmsc_find(comp, msc);
53901fb4b82SJames Morse 	if (IS_ERR(vmsc)) {
54001fb4b82SJames Morse 		if (list_empty(&comp->vmsc))
54101fb4b82SJames Morse 			mpam_component_destroy(comp);
54201fb4b82SJames Morse 		return PTR_ERR(vmsc);
54301fb4b82SJames Morse 	}
54401fb4b82SJames Morse 
54501fb4b82SJames Morse 	err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp);
54601fb4b82SJames Morse 	if (err) {
54701fb4b82SJames Morse 		if (list_empty(&vmsc->ris))
54801fb4b82SJames Morse 			mpam_vmsc_destroy(vmsc);
54901fb4b82SJames Morse 		return err;
55001fb4b82SJames Morse 	}
55101fb4b82SJames Morse 
55201fb4b82SJames Morse 	ris->ris_idx = ris_idx;
55301fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->msc_list);
55401fb4b82SJames Morse 	INIT_LIST_HEAD_RCU(&ris->vmsc_list);
55501fb4b82SJames Morse 	ris->vmsc = vmsc;
55601fb4b82SJames Morse 
55701fb4b82SJames Morse 	cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity);
55801fb4b82SJames Morse 	cpumask_or(&class->affinity, &class->affinity, &ris->affinity);
55901fb4b82SJames Morse 	list_add_rcu(&ris->vmsc_list, &vmsc->ris);
56001fb4b82SJames Morse 	list_add_rcu(&ris->msc_list, &msc->ris);
56101fb4b82SJames Morse 
56201fb4b82SJames Morse 	return 0;
56301fb4b82SJames Morse }
56401fb4b82SJames Morse 
56501fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris)
56601fb4b82SJames Morse {
56701fb4b82SJames Morse 	struct mpam_vmsc *vmsc = ris->vmsc;
56801fb4b82SJames Morse 	struct mpam_msc *msc = vmsc->msc;
56901fb4b82SJames Morse 	struct mpam_component *comp = vmsc->comp;
57001fb4b82SJames Morse 	struct mpam_class *class = comp->class;
57101fb4b82SJames Morse 
57201fb4b82SJames Morse 	lockdep_assert_held(&mpam_list_lock);
57301fb4b82SJames Morse 
57401fb4b82SJames Morse 	/*
57501fb4b82SJames Morse 	 * It is assumed affinities don't overlap. If they do the class becomes
57601fb4b82SJames Morse 	 * unusable immediately.
57701fb4b82SJames Morse 	 */
57801fb4b82SJames Morse 	cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity);
57901fb4b82SJames Morse 	cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity);
58001fb4b82SJames Morse 	clear_bit(ris->ris_idx, &msc->ris_idxs);
58101fb4b82SJames Morse 	list_del_rcu(&ris->msc_list);
58201fb4b82SJames Morse 	list_del_rcu(&ris->vmsc_list);
58301fb4b82SJames Morse 	add_to_garbage(ris);
58401fb4b82SJames Morse 
58501fb4b82SJames Morse 	if (list_empty(&vmsc->ris))
58601fb4b82SJames Morse 		mpam_vmsc_destroy(vmsc);
58701fb4b82SJames Morse }
58801fb4b82SJames Morse 
58901fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx,
59001fb4b82SJames Morse 		    enum mpam_class_types type, u8 class_id, int component_id)
59101fb4b82SJames Morse {
59201fb4b82SJames Morse 	int err;
59301fb4b82SJames Morse 
59401fb4b82SJames Morse 	mutex_lock(&mpam_list_lock);
59501fb4b82SJames Morse 	err = mpam_ris_create_locked(msc, ris_idx, type, class_id,
59601fb4b82SJames Morse 				     component_id);
59701fb4b82SJames Morse 	mutex_unlock(&mpam_list_lock);
59801fb4b82SJames Morse 	if (err)
59901fb4b82SJames Morse 		mpam_free_garbage();
60001fb4b82SJames Morse 
60101fb4b82SJames Morse 	return err;
60201fb4b82SJames Morse }
60301fb4b82SJames Morse 
604bd221f9fSJames Morse static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc,
605bd221f9fSJames Morse 						   u8 ris_idx)
606bd221f9fSJames Morse {
607bd221f9fSJames Morse 	int err;
608bd221f9fSJames Morse 	struct mpam_msc_ris *ris;
609bd221f9fSJames Morse 
610bd221f9fSJames Morse 	lockdep_assert_held(&mpam_list_lock);
611bd221f9fSJames Morse 
612bd221f9fSJames Morse 	if (!test_bit(ris_idx, &msc->ris_idxs)) {
613bd221f9fSJames Morse 		err = mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN,
614bd221f9fSJames Morse 					     0, 0);
615bd221f9fSJames Morse 		if (err)
616bd221f9fSJames Morse 			return ERR_PTR(err);
617bd221f9fSJames Morse 	}
618bd221f9fSJames Morse 
619bd221f9fSJames Morse 	list_for_each_entry(ris, &msc->ris, msc_list) {
620bd221f9fSJames Morse 		if (ris->ris_idx == ris_idx)
621bd221f9fSJames Morse 			return ris;
622bd221f9fSJames Morse 	}
623bd221f9fSJames Morse 
624bd221f9fSJames Morse 	return ERR_PTR(-ENOENT);
625bd221f9fSJames Morse }
626bd221f9fSJames Morse 
6278c90dc68SJames Morse /*
6288c90dc68SJames Morse  * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour
6298c90dc68SJames Morse  * of NRDY, software can use this bit for any purpose" - so hardware might not
6308c90dc68SJames Morse  * implement this - but it isn't RES0.
6318c90dc68SJames Morse  *
6328c90dc68SJames Morse  * Try and see what values stick in this bit. If we can write either value,
6338c90dc68SJames Morse  * its probably not implemented by hardware.
6348c90dc68SJames Morse  */
6358c90dc68SJames Morse static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg)
6368c90dc68SJames Morse {
6378c90dc68SJames Morse 	u32 now;
6388c90dc68SJames Morse 	u64 mon_sel;
6398c90dc68SJames Morse 	bool can_set, can_clear;
6408c90dc68SJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
6418c90dc68SJames Morse 
6428c90dc68SJames Morse 	if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
6438c90dc68SJames Morse 		return false;
6448c90dc68SJames Morse 
6458c90dc68SJames Morse 	mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) |
6468c90dc68SJames Morse 		  FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
6478c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, mon_sel);
6488c90dc68SJames Morse 
6498c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY);
6508c90dc68SJames Morse 	now = _mpam_read_monsel_reg(msc, mon_reg);
6518c90dc68SJames Morse 	can_set = now & MSMON___NRDY;
6528c90dc68SJames Morse 
6538c90dc68SJames Morse 	_mpam_write_monsel_reg(msc, mon_reg, 0);
6548c90dc68SJames Morse 	now = _mpam_read_monsel_reg(msc, mon_reg);
6558c90dc68SJames Morse 	can_clear = !(now & MSMON___NRDY);
6568c90dc68SJames Morse 	mpam_mon_sel_unlock(msc);
6578c90dc68SJames Morse 
6588c90dc68SJames Morse 	return (!can_set || !can_clear);
6598c90dc68SJames Morse }
6608c90dc68SJames Morse 
6618c90dc68SJames Morse #define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg)			\
6628c90dc68SJames Morse 	_mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg)
6638c90dc68SJames Morse 
6648c90dc68SJames Morse static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
6658c90dc68SJames Morse {
6668c90dc68SJames Morse 	int err;
6678c90dc68SJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
6688c90dc68SJames Morse 	struct device *dev = &msc->pdev->dev;
6698c90dc68SJames Morse 	struct mpam_props *props = &ris->props;
670880df85dSJames Morse 	struct mpam_class *class = ris->vmsc->comp->class;
6718c90dc68SJames Morse 
6728c90dc68SJames Morse 	lockdep_assert_held(&msc->probe_lock);
6738c90dc68SJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
6748c90dc68SJames Morse 
675880df85dSJames Morse 	/* Cache Capacity Partitioning */
676880df85dSJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) {
677880df85dSJames Morse 		u32 ccap_features = mpam_read_partsel_reg(msc, CCAP_IDR);
678880df85dSJames Morse 
679880df85dSJames Morse 		props->cmax_wd = FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features);
680880df85dSJames Morse 		if (props->cmax_wd &&
681880df85dSJames Morse 		    FIELD_GET(MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM, ccap_features))
682880df85dSJames Morse 			mpam_set_feature(mpam_feat_cmax_softlim, props);
683880df85dSJames Morse 
684880df85dSJames Morse 		if (props->cmax_wd &&
685880df85dSJames Morse 		    !FIELD_GET(MPAMF_CCAP_IDR_NO_CMAX, ccap_features))
686880df85dSJames Morse 			mpam_set_feature(mpam_feat_cmax_cmax, props);
687880df85dSJames Morse 
688880df85dSJames Morse 		if (props->cmax_wd &&
689880df85dSJames Morse 		    FIELD_GET(MPAMF_CCAP_IDR_HAS_CMIN, ccap_features))
690880df85dSJames Morse 			mpam_set_feature(mpam_feat_cmax_cmin, props);
691880df85dSJames Morse 
692880df85dSJames Morse 		props->cassoc_wd = FIELD_GET(MPAMF_CCAP_IDR_CASSOC_WD, ccap_features);
693880df85dSJames Morse 		if (props->cassoc_wd &&
694880df85dSJames Morse 		    FIELD_GET(MPAMF_CCAP_IDR_HAS_CASSOC, ccap_features))
695880df85dSJames Morse 			mpam_set_feature(mpam_feat_cmax_cassoc, props);
696880df85dSJames Morse 	}
697880df85dSJames Morse 
6988c90dc68SJames Morse 	/* Cache Portion partitioning */
6998c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) {
7008c90dc68SJames Morse 		u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR);
7018c90dc68SJames Morse 
7028c90dc68SJames Morse 		props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features);
7038c90dc68SJames Morse 		if (props->cpbm_wd)
7048c90dc68SJames Morse 			mpam_set_feature(mpam_feat_cpor_part, props);
7058c90dc68SJames Morse 	}
7068c90dc68SJames Morse 
7078c90dc68SJames Morse 	/* Memory bandwidth partitioning */
7088c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) {
7098c90dc68SJames Morse 		u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR);
7108c90dc68SJames Morse 
7118c90dc68SJames Morse 		/* portion bitmap resolution */
7128c90dc68SJames Morse 		props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features);
7138c90dc68SJames Morse 		if (props->mbw_pbm_bits &&
7148c90dc68SJames Morse 		    FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features))
7158c90dc68SJames Morse 			mpam_set_feature(mpam_feat_mbw_part, props);
7168c90dc68SJames Morse 
7178c90dc68SJames Morse 		props->bwa_wd = FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features);
7188c90dc68SJames Morse 		if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features))
7198c90dc68SJames Morse 			mpam_set_feature(mpam_feat_mbw_max, props);
720880df85dSJames Morse 
721880df85dSJames Morse 		if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features))
722880df85dSJames Morse 			mpam_set_feature(mpam_feat_mbw_min, props);
723880df85dSJames Morse 
724880df85dSJames Morse 		if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_PROP, mbw_features))
725880df85dSJames Morse 			mpam_set_feature(mpam_feat_mbw_prop, props);
726880df85dSJames Morse 	}
727880df85dSJames Morse 
728880df85dSJames Morse 	/* Priority partitioning */
729880df85dSJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) {
730880df85dSJames Morse 		u32 pri_features = mpam_read_partsel_reg(msc, PRI_IDR);
731880df85dSJames Morse 
732880df85dSJames Morse 		props->intpri_wd = FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features);
733880df85dSJames Morse 		if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features)) {
734880df85dSJames Morse 			mpam_set_feature(mpam_feat_intpri_part, props);
735880df85dSJames Morse 			if (FIELD_GET(MPAMF_PRI_IDR_INTPRI_0_IS_LOW, pri_features))
736880df85dSJames Morse 				mpam_set_feature(mpam_feat_intpri_part_0_low, props);
737880df85dSJames Morse 		}
738880df85dSJames Morse 
739880df85dSJames Morse 		props->dspri_wd = FIELD_GET(MPAMF_PRI_IDR_DSPRI_WD, pri_features);
740880df85dSJames Morse 		if (props->dspri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_DSPRI, pri_features)) {
741880df85dSJames Morse 			mpam_set_feature(mpam_feat_dspri_part, props);
742880df85dSJames Morse 			if (FIELD_GET(MPAMF_PRI_IDR_DSPRI_0_IS_LOW, pri_features))
743880df85dSJames Morse 				mpam_set_feature(mpam_feat_dspri_part_0_low, props);
744880df85dSJames Morse 		}
7458c90dc68SJames Morse 	}
7468c90dc68SJames Morse 
7478c90dc68SJames Morse 	/* Performance Monitoring */
7488c90dc68SJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) {
7498c90dc68SJames Morse 		u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR);
7508c90dc68SJames Morse 
7518c90dc68SJames Morse 		/*
7528c90dc68SJames Morse 		 * If the firmware max-nrdy-us property is missing, the
7538c90dc68SJames Morse 		 * CSU counters can't be used. Should we wait forever?
7548c90dc68SJames Morse 		 */
7558c90dc68SJames Morse 		err = device_property_read_u32(&msc->pdev->dev,
7568c90dc68SJames Morse 					       "arm,not-ready-us",
7578c90dc68SJames Morse 					       &msc->nrdy_usec);
7588c90dc68SJames Morse 
7598c90dc68SJames Morse 		if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) {
7608c90dc68SJames Morse 			u32 csumonidr;
7618c90dc68SJames Morse 
7628c90dc68SJames Morse 			csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR);
7638c90dc68SJames Morse 			props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr);
7648c90dc68SJames Morse 			if (props->num_csu_mon) {
7658c90dc68SJames Morse 				bool hw_managed;
7668c90dc68SJames Morse 
7678c90dc68SJames Morse 				mpam_set_feature(mpam_feat_msmon_csu, props);
7688c90dc68SJames Morse 
769880df85dSJames Morse 				if (FIELD_GET(MPAMF_CSUMON_IDR_HAS_XCL, csumonidr))
770880df85dSJames Morse 					mpam_set_feature(mpam_feat_msmon_csu_xcl, props);
771880df85dSJames Morse 
7728c90dc68SJames Morse 				/* Is NRDY hardware managed? */
7738c90dc68SJames Morse 				hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU);
7748c90dc68SJames Morse 				if (hw_managed)
7758c90dc68SJames Morse 					mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props);
7768c90dc68SJames Morse 			}
7778c90dc68SJames Morse 
7788c90dc68SJames Morse 			/*
7798c90dc68SJames Morse 			 * Accept the missing firmware property if NRDY appears
7808c90dc68SJames Morse 			 * un-implemented.
7818c90dc68SJames Morse 			 */
7828c90dc68SJames Morse 			if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props))
7838c90dc68SJames Morse 				dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware.");
7848c90dc68SJames Morse 		}
7858c90dc68SJames Morse 		if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) {
786fdc29a14SRohit Mathew 			bool has_long, hw_managed;
7878c90dc68SJames Morse 			u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR);
7888c90dc68SJames Morse 
7898c90dc68SJames Morse 			props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr);
790fdc29a14SRohit Mathew 			if (props->num_mbwu_mon) {
7918c90dc68SJames Morse 				mpam_set_feature(mpam_feat_msmon_mbwu, props);
7928c90dc68SJames Morse 
793880df85dSJames Morse 				if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumon_idr))
794880df85dSJames Morse 					mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props);
795880df85dSJames Morse 
796fdc29a14SRohit Mathew 				has_long = FIELD_GET(MPAMF_MBWUMON_IDR_HAS_LONG, mbwumon_idr);
797fdc29a14SRohit Mathew 				if (has_long) {
798fdc29a14SRohit Mathew 					if (FIELD_GET(MPAMF_MBWUMON_IDR_LWD, mbwumon_idr))
799fdc29a14SRohit Mathew 						mpam_set_feature(mpam_feat_msmon_mbwu_63counter, props);
800fdc29a14SRohit Mathew 					else
801fdc29a14SRohit Mathew 						mpam_set_feature(mpam_feat_msmon_mbwu_44counter, props);
802fdc29a14SRohit Mathew 				} else {
803fdc29a14SRohit Mathew 					mpam_set_feature(mpam_feat_msmon_mbwu_31counter, props);
804fdc29a14SRohit Mathew 				}
805fdc29a14SRohit Mathew 
8068c90dc68SJames Morse 				/* Is NRDY hardware managed? */
8078c90dc68SJames Morse 				hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU);
8088c90dc68SJames Morse 				if (hw_managed)
8098c90dc68SJames Morse 					mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props);
8108c90dc68SJames Morse 
8118c90dc68SJames Morse 				/*
8128c90dc68SJames Morse 				 * Don't warn about any missing firmware property for
8138c90dc68SJames Morse 				 * MBWU NRDY - it doesn't make any sense!
8148c90dc68SJames Morse 				 */
8158c90dc68SJames Morse 			}
8168c90dc68SJames Morse 		}
817fdc29a14SRohit Mathew 	}
818880df85dSJames Morse 
819880df85dSJames Morse 	/*
820880df85dSJames Morse 	 * RIS with PARTID narrowing don't have enough storage for one
821880df85dSJames Morse 	 * configuration per PARTID. If these are in a class we could use,
822880df85dSJames Morse 	 * reduce the supported partid_max to match the number of intpartid.
823880df85dSJames Morse 	 * If the class is unknown, just ignore it.
824880df85dSJames Morse 	 */
825880df85dSJames Morse 	if (FIELD_GET(MPAMF_IDR_HAS_PARTID_NRW, ris->idr) &&
826880df85dSJames Morse 	    class->type != MPAM_CLASS_UNKNOWN) {
827880df85dSJames Morse 		u32 nrwidr = mpam_read_partsel_reg(msc, PARTID_NRW_IDR);
828880df85dSJames Morse 		u16 partid_max = FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr);
829880df85dSJames Morse 
830880df85dSJames Morse 		mpam_set_feature(mpam_feat_partid_nrw, props);
831880df85dSJames Morse 		msc->partid_max = min(msc->partid_max, partid_max);
832880df85dSJames Morse 	}
8338c90dc68SJames Morse }
8348c90dc68SJames Morse 
8358f8d0ac1SJames Morse static int mpam_msc_hw_probe(struct mpam_msc *msc)
8368f8d0ac1SJames Morse {
8378f8d0ac1SJames Morse 	u64 idr;
838bd221f9fSJames Morse 	u16 partid_max;
839bd221f9fSJames Morse 	u8 ris_idx, pmg_max;
840bd221f9fSJames Morse 	struct mpam_msc_ris *ris;
8418f8d0ac1SJames Morse 	struct device *dev = &msc->pdev->dev;
8428f8d0ac1SJames Morse 
8438f8d0ac1SJames Morse 	lockdep_assert_held(&msc->probe_lock);
8448f8d0ac1SJames Morse 
8458f8d0ac1SJames Morse 	idr = __mpam_read_reg(msc, MPAMF_AIDR);
8468f8d0ac1SJames Morse 	if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) {
8478f8d0ac1SJames Morse 		dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n");
8488f8d0ac1SJames Morse 		return -EIO;
8498f8d0ac1SJames Morse 	}
8508f8d0ac1SJames Morse 
851bd221f9fSJames Morse 	/* Grab an IDR value to find out how many RIS there are */
852bd221f9fSJames Morse 	mutex_lock(&msc->part_sel_lock);
853bd221f9fSJames Morse 	idr = mpam_msc_read_idr(msc);
854bd221f9fSJames Morse 	mutex_unlock(&msc->part_sel_lock);
855bd221f9fSJames Morse 
856bd221f9fSJames Morse 	msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr);
857bd221f9fSJames Morse 
858bd221f9fSJames Morse 	/* Use these values so partid/pmg always starts with a valid value */
859bd221f9fSJames Morse 	msc->partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
860bd221f9fSJames Morse 	msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
861bd221f9fSJames Morse 
862bd221f9fSJames Morse 	for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) {
863bd221f9fSJames Morse 		mutex_lock(&msc->part_sel_lock);
864bd221f9fSJames Morse 		__mpam_part_sel(ris_idx, 0, msc);
865bd221f9fSJames Morse 		idr = mpam_msc_read_idr(msc);
866bd221f9fSJames Morse 		mutex_unlock(&msc->part_sel_lock);
867bd221f9fSJames Morse 
868bd221f9fSJames Morse 		partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
869bd221f9fSJames Morse 		pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
870bd221f9fSJames Morse 		msc->partid_max = min(msc->partid_max, partid_max);
871bd221f9fSJames Morse 		msc->pmg_max = min(msc->pmg_max, pmg_max);
87249aa621cSJames Morse 		msc->has_extd_esr = FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr);
873bd221f9fSJames Morse 
874bd221f9fSJames Morse 		mutex_lock(&mpam_list_lock);
875bd221f9fSJames Morse 		ris = mpam_get_or_create_ris(msc, ris_idx);
876bd221f9fSJames Morse 		mutex_unlock(&mpam_list_lock);
877bd221f9fSJames Morse 		if (IS_ERR(ris))
878bd221f9fSJames Morse 			return PTR_ERR(ris);
8798c90dc68SJames Morse 		ris->idr = idr;
8808c90dc68SJames Morse 
8818c90dc68SJames Morse 		mutex_lock(&msc->part_sel_lock);
8828c90dc68SJames Morse 		__mpam_part_sel(ris_idx, 0, msc);
8838c90dc68SJames Morse 		mpam_ris_hw_probe(ris);
8848c90dc68SJames Morse 		mutex_unlock(&msc->part_sel_lock);
885bd221f9fSJames Morse 	}
886bd221f9fSJames Morse 
88749aa621cSJames Morse 	/* Clear any stale errors */
88849aa621cSJames Morse 	mpam_msc_clear_esr(msc);
88949aa621cSJames Morse 
890bd221f9fSJames Morse 	spin_lock(&partid_max_lock);
891bd221f9fSJames Morse 	mpam_partid_max = min(mpam_partid_max, msc->partid_max);
892bd221f9fSJames Morse 	mpam_pmg_max = min(mpam_pmg_max, msc->pmg_max);
893bd221f9fSJames Morse 	spin_unlock(&partid_max_lock);
894bd221f9fSJames Morse 
8958f8d0ac1SJames Morse 	msc->probed = true;
8968f8d0ac1SJames Morse 
8978f8d0ac1SJames Morse 	return 0;
8988f8d0ac1SJames Morse }
8998f8d0ac1SJames Morse 
900823e7c37SJames Morse struct mon_read {
901823e7c37SJames Morse 	struct mpam_msc_ris		*ris;
902823e7c37SJames Morse 	struct mon_cfg			*ctx;
903823e7c37SJames Morse 	enum mpam_device_features	type;
904823e7c37SJames Morse 	u64				*val;
905823e7c37SJames Morse 	int				err;
906823e7c37SJames Morse };
907823e7c37SJames Morse 
9089e5afb7cSRohit Mathew static bool mpam_ris_has_mbwu_long_counter(struct mpam_msc_ris *ris)
9099e5afb7cSRohit Mathew {
9109e5afb7cSRohit Mathew 	return (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, &ris->props) ||
9119e5afb7cSRohit Mathew 		mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props));
9129e5afb7cSRohit Mathew }
9139e5afb7cSRohit Mathew 
9149e5afb7cSRohit Mathew static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc)
9159e5afb7cSRohit Mathew {
9169e5afb7cSRohit Mathew 	int retry = 3;
9179e5afb7cSRohit Mathew 	u32 mbwu_l_low;
9189e5afb7cSRohit Mathew 	u64 mbwu_l_high1, mbwu_l_high2;
9199e5afb7cSRohit Mathew 
9209e5afb7cSRohit Mathew 	mpam_mon_sel_lock_held(msc);
9219e5afb7cSRohit Mathew 
9229e5afb7cSRohit Mathew 	WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
9239e5afb7cSRohit Mathew 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
9249e5afb7cSRohit Mathew 
9259e5afb7cSRohit Mathew 	mbwu_l_high2 = __mpam_read_reg(msc, MSMON_MBWU_L + 4);
9269e5afb7cSRohit Mathew 	do {
9279e5afb7cSRohit Mathew 		mbwu_l_high1 = mbwu_l_high2;
9289e5afb7cSRohit Mathew 		mbwu_l_low = __mpam_read_reg(msc, MSMON_MBWU_L);
9299e5afb7cSRohit Mathew 		mbwu_l_high2 = __mpam_read_reg(msc, MSMON_MBWU_L + 4);
9309e5afb7cSRohit Mathew 
9319e5afb7cSRohit Mathew 		retry--;
9329e5afb7cSRohit Mathew 	} while (mbwu_l_high1 != mbwu_l_high2 && retry > 0);
9339e5afb7cSRohit Mathew 
9349e5afb7cSRohit Mathew 	if (mbwu_l_high1 == mbwu_l_high2)
9359e5afb7cSRohit Mathew 		return (mbwu_l_high1 << 32) | mbwu_l_low;
9369e5afb7cSRohit Mathew 
9379e5afb7cSRohit Mathew 	pr_warn("Failed to read a stable value\n");
9389e5afb7cSRohit Mathew 	return MSMON___L_NRDY;
9399e5afb7cSRohit Mathew }
9409e5afb7cSRohit Mathew 
9419e5afb7cSRohit Mathew static void mpam_msc_zero_mbwu_l(struct mpam_msc *msc)
9429e5afb7cSRohit Mathew {
9439e5afb7cSRohit Mathew 	mpam_mon_sel_lock_held(msc);
9449e5afb7cSRohit Mathew 
9459e5afb7cSRohit Mathew 	WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
9469e5afb7cSRohit Mathew 	WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
9479e5afb7cSRohit Mathew 
9489e5afb7cSRohit Mathew 	__mpam_write_reg(msc, MSMON_MBWU_L, 0);
9499e5afb7cSRohit Mathew 	__mpam_write_reg(msc, MSMON_MBWU_L + 4, 0);
9509e5afb7cSRohit Mathew }
9519e5afb7cSRohit Mathew 
952823e7c37SJames Morse static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
953823e7c37SJames Morse 				   u32 *flt_val)
954823e7c37SJames Morse {
955823e7c37SJames Morse 	struct mon_cfg *ctx = m->ctx;
956823e7c37SJames Morse 
957823e7c37SJames Morse 	/*
958823e7c37SJames Morse 	 * For CSU counters its implementation-defined what happens when not
959823e7c37SJames Morse 	 * filtering by partid.
960823e7c37SJames Morse 	 */
961823e7c37SJames Morse 	*ctl_val = MSMON_CFG_x_CTL_MATCH_PARTID;
962823e7c37SJames Morse 
963823e7c37SJames Morse 	*flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid);
964823e7c37SJames Morse 
965823e7c37SJames Morse 	if (m->ctx->match_pmg) {
966823e7c37SJames Morse 		*ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
967823e7c37SJames Morse 		*flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg);
968823e7c37SJames Morse 	}
969823e7c37SJames Morse 
970823e7c37SJames Morse 	switch (m->type) {
971823e7c37SJames Morse 	case mpam_feat_msmon_csu:
972823e7c37SJames Morse 		*ctl_val |= MSMON_CFG_CSU_CTL_TYPE_CSU;
973823e7c37SJames Morse 
974823e7c37SJames Morse 		if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props))
975823e7c37SJames Morse 			*flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL, ctx->csu_exclude_clean);
976823e7c37SJames Morse 
977823e7c37SJames Morse 		break;
9789e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_31counter:
9799e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_44counter:
9809e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_63counter:
981823e7c37SJames Morse 		*ctl_val |= MSMON_CFG_MBWU_CTL_TYPE_MBWU;
982823e7c37SJames Morse 
983823e7c37SJames Morse 		if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props))
984823e7c37SJames Morse 			*flt_val |= FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts);
985823e7c37SJames Morse 
986823e7c37SJames Morse 		break;
987823e7c37SJames Morse 	default:
988823e7c37SJames Morse 		pr_warn("Unexpected monitor type %d\n", m->type);
989823e7c37SJames Morse 	}
990823e7c37SJames Morse }
991823e7c37SJames Morse 
992823e7c37SJames Morse static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
993823e7c37SJames Morse 				    u32 *flt_val)
994823e7c37SJames Morse {
995823e7c37SJames Morse 	struct mpam_msc *msc = m->ris->vmsc->msc;
996823e7c37SJames Morse 
997823e7c37SJames Morse 	switch (m->type) {
998823e7c37SJames Morse 	case mpam_feat_msmon_csu:
999823e7c37SJames Morse 		*ctl_val = mpam_read_monsel_reg(msc, CFG_CSU_CTL);
1000823e7c37SJames Morse 		*flt_val = mpam_read_monsel_reg(msc, CFG_CSU_FLT);
1001823e7c37SJames Morse 		break;
10029e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_31counter:
10039e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_44counter:
10049e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_63counter:
1005823e7c37SJames Morse 		*ctl_val = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
1006823e7c37SJames Morse 		*flt_val = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
1007823e7c37SJames Morse 		break;
1008823e7c37SJames Morse 	default:
1009823e7c37SJames Morse 		pr_warn("Unexpected monitor type %d\n", m->type);
1010823e7c37SJames Morse 	}
1011823e7c37SJames Morse }
1012823e7c37SJames Morse 
1013823e7c37SJames Morse /* Remove values set by the hardware to prevent apparent mismatches. */
1014823e7c37SJames Morse static inline void clean_msmon_ctl_val(u32 *cur_ctl)
1015823e7c37SJames Morse {
1016823e7c37SJames Morse 	*cur_ctl &= ~MSMON_CFG_x_CTL_OFLOW_STATUS;
10179e5afb7cSRohit Mathew 
10189e5afb7cSRohit Mathew 	if (FIELD_GET(MSMON_CFG_x_CTL_TYPE, *cur_ctl) == MSMON_CFG_MBWU_CTL_TYPE_MBWU)
10199e5afb7cSRohit Mathew 		*cur_ctl &= ~MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L;
1020823e7c37SJames Morse }
1021823e7c37SJames Morse 
1022823e7c37SJames Morse static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
1023823e7c37SJames Morse 				     u32 flt_val)
1024823e7c37SJames Morse {
1025823e7c37SJames Morse 	struct mpam_msc *msc = m->ris->vmsc->msc;
1026823e7c37SJames Morse 
1027823e7c37SJames Morse 	/*
1028823e7c37SJames Morse 	 * Write the ctl_val with the enable bit cleared, reset the counter,
1029823e7c37SJames Morse 	 * then enable counter.
1030823e7c37SJames Morse 	 */
1031823e7c37SJames Morse 	switch (m->type) {
1032823e7c37SJames Morse 	case mpam_feat_msmon_csu:
1033823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
1034823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
1035823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CSU, 0);
1036823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
1037823e7c37SJames Morse 		break;
10389e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_31counter:
10399e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_44counter:
10409e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_63counter:
1041823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
1042823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
1043823e7c37SJames Morse 		mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
1044823e7c37SJames Morse 		/* Counting monitors require NRDY to be reset by software */
10459e5afb7cSRohit Mathew 		if (m->type == mpam_feat_msmon_mbwu_31counter)
1046823e7c37SJames Morse 			mpam_write_monsel_reg(msc, MBWU, 0);
10479e5afb7cSRohit Mathew 		else
10489e5afb7cSRohit Mathew 			mpam_msc_zero_mbwu_l(m->ris->vmsc->msc);
1049823e7c37SJames Morse 		break;
1050823e7c37SJames Morse 	default:
1051823e7c37SJames Morse 		pr_warn("Unexpected monitor type %d\n", m->type);
1052823e7c37SJames Morse 	}
1053823e7c37SJames Morse }
1054823e7c37SJames Morse 
1055b3536379SBen Horgan static u64 mpam_msmon_overflow_val(enum mpam_device_features type)
1056b3536379SBen Horgan {
10579e5afb7cSRohit Mathew 	/* TODO: implement scaling counters */
10589e5afb7cSRohit Mathew 	switch (type) {
10599e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_63counter:
10609e5afb7cSRohit Mathew 		return BIT_ULL(hweight_long(MSMON___LWD_VALUE));
10619e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_44counter:
10629e5afb7cSRohit Mathew 		return BIT_ULL(hweight_long(MSMON___L_VALUE));
10639e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_31counter:
1064b3536379SBen Horgan 		return BIT_ULL(hweight_long(MSMON___VALUE));
10659e5afb7cSRohit Mathew 	default:
10669e5afb7cSRohit Mathew 		return 0;
10679e5afb7cSRohit Mathew 	}
1068b3536379SBen Horgan }
1069b3536379SBen Horgan 
1070823e7c37SJames Morse static void __ris_msmon_read(void *arg)
1071823e7c37SJames Morse {
1072823e7c37SJames Morse 	u64 now;
1073823e7c37SJames Morse 	bool nrdy = false;
1074823e7c37SJames Morse 	bool config_mismatch;
1075*c2803bd5SBen Horgan 	bool overflow = false;
1076823e7c37SJames Morse 	struct mon_read *m = arg;
1077823e7c37SJames Morse 	struct mon_cfg *ctx = m->ctx;
1078201d96caSJames Morse 	bool reset_on_next_read = false;
1079823e7c37SJames Morse 	struct mpam_msc_ris *ris = m->ris;
108041e8a149SJames Morse 	struct msmon_mbwu_state *mbwu_state;
1081823e7c37SJames Morse 	struct mpam_props *rprops = &ris->props;
1082823e7c37SJames Morse 	struct mpam_msc *msc = m->ris->vmsc->msc;
1083823e7c37SJames Morse 	u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt;
1084823e7c37SJames Morse 
1085823e7c37SJames Morse 	if (!mpam_mon_sel_lock(msc)) {
1086823e7c37SJames Morse 		m->err = -EIO;
1087823e7c37SJames Morse 		return;
1088823e7c37SJames Morse 	}
1089823e7c37SJames Morse 	mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) |
1090823e7c37SJames Morse 		  FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
1091823e7c37SJames Morse 	mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
1092823e7c37SJames Morse 
1093201d96caSJames Morse 	switch (m->type) {
1094201d96caSJames Morse 	case mpam_feat_msmon_mbwu_31counter:
1095201d96caSJames Morse 	case mpam_feat_msmon_mbwu_44counter:
1096201d96caSJames Morse 	case mpam_feat_msmon_mbwu_63counter:
1097201d96caSJames Morse 		mbwu_state = &ris->mbwu_state[ctx->mon];
1098201d96caSJames Morse 		if (mbwu_state) {
1099201d96caSJames Morse 			reset_on_next_read = mbwu_state->reset_on_next_read;
1100201d96caSJames Morse 			mbwu_state->reset_on_next_read = false;
1101201d96caSJames Morse 		}
1102201d96caSJames Morse 		break;
1103201d96caSJames Morse 	default:
1104201d96caSJames Morse 		break;
1105201d96caSJames Morse 	}
1106201d96caSJames Morse 
1107823e7c37SJames Morse 	/*
1108823e7c37SJames Morse 	 * Read the existing configuration to avoid re-writing the same values.
1109823e7c37SJames Morse 	 * This saves waiting for 'nrdy' on subsequent reads.
1110823e7c37SJames Morse 	 */
1111823e7c37SJames Morse 	read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt);
11129e5afb7cSRohit Mathew 
11139e5afb7cSRohit Mathew 	if (mpam_feat_msmon_mbwu_31counter == m->type)
1114b3536379SBen Horgan 		overflow = cur_ctl & MSMON_CFG_x_CTL_OFLOW_STATUS;
11159e5afb7cSRohit Mathew 	else if (mpam_feat_msmon_mbwu_44counter == m->type ||
11169e5afb7cSRohit Mathew 		 mpam_feat_msmon_mbwu_63counter == m->type)
11179e5afb7cSRohit Mathew 		overflow = cur_ctl & MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L;
1118b3536379SBen Horgan 
1119823e7c37SJames Morse 	clean_msmon_ctl_val(&cur_ctl);
1120823e7c37SJames Morse 	gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val);
1121823e7c37SJames Morse 	config_mismatch = cur_flt != flt_val ||
1122823e7c37SJames Morse 			  cur_ctl != (ctl_val | MSMON_CFG_x_CTL_EN);
1123823e7c37SJames Morse 
1124201d96caSJames Morse 	if (config_mismatch || reset_on_next_read) {
1125823e7c37SJames Morse 		write_msmon_ctl_flt_vals(m, ctl_val, flt_val);
1126b3536379SBen Horgan 		overflow = false;
1127b3536379SBen Horgan 	} else if (overflow) {
1128b3536379SBen Horgan 		mpam_write_monsel_reg(msc, CFG_MBWU_CTL,
11299e5afb7cSRohit Mathew 				      cur_ctl &
11309e5afb7cSRohit Mathew 				      ~(MSMON_CFG_x_CTL_OFLOW_STATUS |
11319e5afb7cSRohit Mathew 					MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L));
1132b3536379SBen Horgan 	}
1133823e7c37SJames Morse 
1134823e7c37SJames Morse 	switch (m->type) {
1135823e7c37SJames Morse 	case mpam_feat_msmon_csu:
1136823e7c37SJames Morse 		now = mpam_read_monsel_reg(msc, CSU);
1137823e7c37SJames Morse 		if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops))
1138823e7c37SJames Morse 			nrdy = now & MSMON___NRDY;
113941e8a149SJames Morse 		now = FIELD_GET(MSMON___VALUE, now);
1140823e7c37SJames Morse 		break;
11419e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_31counter:
11429e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_44counter:
11439e5afb7cSRohit Mathew 	case mpam_feat_msmon_mbwu_63counter:
11449e5afb7cSRohit Mathew 		if (m->type != mpam_feat_msmon_mbwu_31counter) {
11459e5afb7cSRohit Mathew 			now = mpam_msc_read_mbwu_l(msc);
11469e5afb7cSRohit Mathew 			if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops))
11479e5afb7cSRohit Mathew 				nrdy = now & MSMON___L_NRDY;
11489e5afb7cSRohit Mathew 
11499e5afb7cSRohit Mathew 			if (m->type == mpam_feat_msmon_mbwu_63counter)
11509e5afb7cSRohit Mathew 				now = FIELD_GET(MSMON___LWD_VALUE, now);
11519e5afb7cSRohit Mathew 			else
11529e5afb7cSRohit Mathew 				now = FIELD_GET(MSMON___L_VALUE, now);
11539e5afb7cSRohit Mathew 		} else {
1154823e7c37SJames Morse 			now = mpam_read_monsel_reg(msc, MBWU);
1155823e7c37SJames Morse 			if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops))
1156823e7c37SJames Morse 				nrdy = now & MSMON___NRDY;
115741e8a149SJames Morse 			now = FIELD_GET(MSMON___VALUE, now);
11589e5afb7cSRohit Mathew 		}
115941e8a149SJames Morse 
116041e8a149SJames Morse 		if (nrdy)
116141e8a149SJames Morse 			break;
116241e8a149SJames Morse 
116341e8a149SJames Morse 		mbwu_state = &ris->mbwu_state[ctx->mon];
116441e8a149SJames Morse 
1165b3536379SBen Horgan 		if (overflow)
1166b3536379SBen Horgan 			mbwu_state->correction += mpam_msmon_overflow_val(m->type);
1167b3536379SBen Horgan 
1168b3536379SBen Horgan 		/*
1169b3536379SBen Horgan 		 * Include bandwidth consumed before the last hardware reset and
1170b3536379SBen Horgan 		 * a counter size increment for each overflow.
1171b3536379SBen Horgan 		 */
117241e8a149SJames Morse 		now += mbwu_state->correction;
1173823e7c37SJames Morse 		break;
1174823e7c37SJames Morse 	default:
1175823e7c37SJames Morse 		m->err = -EINVAL;
1176823e7c37SJames Morse 	}
1177823e7c37SJames Morse 	mpam_mon_sel_unlock(msc);
1178823e7c37SJames Morse 
1179*c2803bd5SBen Horgan 	if (nrdy)
1180823e7c37SJames Morse 		m->err = -EBUSY;
1181*c2803bd5SBen Horgan 
1182*c2803bd5SBen Horgan 	if (m->err)
1183823e7c37SJames Morse 		return;
1184823e7c37SJames Morse 
1185823e7c37SJames Morse 	*m->val += now;
1186823e7c37SJames Morse }
1187823e7c37SJames Morse 
1188823e7c37SJames Morse static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
1189823e7c37SJames Morse {
1190823e7c37SJames Morse 	int err, any_err = 0;
1191823e7c37SJames Morse 	struct mpam_vmsc *vmsc;
1192823e7c37SJames Morse 
1193823e7c37SJames Morse 	guard(srcu)(&mpam_srcu);
1194823e7c37SJames Morse 	list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list,
1195823e7c37SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
1196823e7c37SJames Morse 		struct mpam_msc *msc = vmsc->msc;
1197823e7c37SJames Morse 		struct mpam_msc_ris *ris;
1198823e7c37SJames Morse 
1199823e7c37SJames Morse 		list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
1200823e7c37SJames Morse 					 srcu_read_lock_held(&mpam_srcu)) {
1201823e7c37SJames Morse 			arg->ris = ris;
1202823e7c37SJames Morse 
1203823e7c37SJames Morse 			err = smp_call_function_any(&msc->accessibility,
1204823e7c37SJames Morse 						    __ris_msmon_read, arg,
1205823e7c37SJames Morse 						    true);
1206823e7c37SJames Morse 			if (!err && arg->err)
1207823e7c37SJames Morse 				err = arg->err;
1208823e7c37SJames Morse 
1209823e7c37SJames Morse 			/*
1210823e7c37SJames Morse 			 * Save one error to be returned to the caller, but
1211823e7c37SJames Morse 			 * keep reading counters so that get reprogrammed. On
1212823e7c37SJames Morse 			 * platforms with NRDY this lets us wait once.
1213823e7c37SJames Morse 			 */
1214823e7c37SJames Morse 			if (err)
1215823e7c37SJames Morse 				any_err = err;
1216823e7c37SJames Morse 		}
1217823e7c37SJames Morse 	}
1218823e7c37SJames Morse 
1219823e7c37SJames Morse 	return any_err;
1220823e7c37SJames Morse }
1221823e7c37SJames Morse 
12229e5afb7cSRohit Mathew static enum mpam_device_features mpam_msmon_choose_counter(struct mpam_class *class)
12239e5afb7cSRohit Mathew {
12249e5afb7cSRohit Mathew 	struct mpam_props *cprops = &class->props;
12259e5afb7cSRohit Mathew 
12269e5afb7cSRohit Mathew 	if (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, cprops))
12279e5afb7cSRohit Mathew 		return mpam_feat_msmon_mbwu_63counter;
12289e5afb7cSRohit Mathew 	if (mpam_has_feature(mpam_feat_msmon_mbwu_44counter, cprops))
12299e5afb7cSRohit Mathew 		return mpam_feat_msmon_mbwu_44counter;
12309e5afb7cSRohit Mathew 
12319e5afb7cSRohit Mathew 	return mpam_feat_msmon_mbwu_31counter;
12329e5afb7cSRohit Mathew }
12339e5afb7cSRohit Mathew 
1234823e7c37SJames Morse int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
1235823e7c37SJames Morse 		    enum mpam_device_features type, u64 *val)
1236823e7c37SJames Morse {
1237823e7c37SJames Morse 	int err;
1238823e7c37SJames Morse 	struct mon_read arg;
1239823e7c37SJames Morse 	u64 wait_jiffies = 0;
12409e5afb7cSRohit Mathew 	struct mpam_class *class = comp->class;
12419e5afb7cSRohit Mathew 	struct mpam_props *cprops = &class->props;
1242823e7c37SJames Morse 
1243823e7c37SJames Morse 	might_sleep();
1244823e7c37SJames Morse 
1245823e7c37SJames Morse 	if (!mpam_is_enabled())
1246823e7c37SJames Morse 		return -EIO;
1247823e7c37SJames Morse 
1248823e7c37SJames Morse 	if (!mpam_has_feature(type, cprops))
1249823e7c37SJames Morse 		return -EOPNOTSUPP;
1250823e7c37SJames Morse 
12519e5afb7cSRohit Mathew 	if (type == mpam_feat_msmon_mbwu)
12529e5afb7cSRohit Mathew 		type = mpam_msmon_choose_counter(class);
12539e5afb7cSRohit Mathew 
1254823e7c37SJames Morse 	arg = (struct mon_read) {
1255823e7c37SJames Morse 		.ctx = ctx,
1256823e7c37SJames Morse 		.type = type,
1257823e7c37SJames Morse 		.val = val,
1258823e7c37SJames Morse 	};
1259823e7c37SJames Morse 	*val = 0;
1260823e7c37SJames Morse 
1261823e7c37SJames Morse 	err = _msmon_read(comp, &arg);
12629e5afb7cSRohit Mathew 	if (err == -EBUSY && class->nrdy_usec)
12639e5afb7cSRohit Mathew 		wait_jiffies = usecs_to_jiffies(class->nrdy_usec);
1264823e7c37SJames Morse 
1265823e7c37SJames Morse 	while (wait_jiffies)
1266823e7c37SJames Morse 		wait_jiffies = schedule_timeout_uninterruptible(wait_jiffies);
1267823e7c37SJames Morse 
1268823e7c37SJames Morse 	if (err == -EBUSY) {
1269823e7c37SJames Morse 		arg = (struct mon_read) {
1270823e7c37SJames Morse 			.ctx = ctx,
1271823e7c37SJames Morse 			.type = type,
1272823e7c37SJames Morse 			.val = val,
1273823e7c37SJames Morse 		};
1274823e7c37SJames Morse 		*val = 0;
1275823e7c37SJames Morse 
1276823e7c37SJames Morse 		err = _msmon_read(comp, &arg);
1277823e7c37SJames Morse 	}
1278823e7c37SJames Morse 
1279823e7c37SJames Morse 	return err;
1280823e7c37SJames Morse }
1281823e7c37SJames Morse 
1282201d96caSJames Morse void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ctx)
1283201d96caSJames Morse {
1284201d96caSJames Morse 	struct mpam_msc *msc;
1285201d96caSJames Morse 	struct mpam_vmsc *vmsc;
1286201d96caSJames Morse 	struct mpam_msc_ris *ris;
1287201d96caSJames Morse 
1288201d96caSJames Morse 	if (!mpam_is_enabled())
1289201d96caSJames Morse 		return;
1290201d96caSJames Morse 
1291201d96caSJames Morse 	guard(srcu)(&mpam_srcu);
1292201d96caSJames Morse 	list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list,
1293201d96caSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
1294201d96caSJames Morse 		if (!mpam_has_feature(mpam_feat_msmon_mbwu, &vmsc->props))
1295201d96caSJames Morse 			continue;
1296201d96caSJames Morse 
1297201d96caSJames Morse 		msc = vmsc->msc;
1298201d96caSJames Morse 		list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
1299201d96caSJames Morse 					 srcu_read_lock_held(&mpam_srcu)) {
1300201d96caSJames Morse 			if (!mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props))
1301201d96caSJames Morse 				continue;
1302201d96caSJames Morse 
1303201d96caSJames Morse 			if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
1304201d96caSJames Morse 				continue;
1305201d96caSJames Morse 
1306201d96caSJames Morse 			ris->mbwu_state[ctx->mon].correction = 0;
1307201d96caSJames Morse 			ris->mbwu_state[ctx->mon].reset_on_next_read = true;
1308201d96caSJames Morse 			mpam_mon_sel_unlock(msc);
1309201d96caSJames Morse 		}
1310201d96caSJames Morse 	}
1311201d96caSJames Morse }
1312201d96caSJames Morse 
1313f188a36cSJames Morse static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
1314f188a36cSJames Morse {
1315f188a36cSJames Morse 	u32 num_words, msb;
1316f188a36cSJames Morse 	u32 bm = ~0;
1317f188a36cSJames Morse 	int i;
1318f188a36cSJames Morse 
1319f188a36cSJames Morse 	lockdep_assert_held(&msc->part_sel_lock);
1320f188a36cSJames Morse 
1321f188a36cSJames Morse 	if (wd == 0)
1322f188a36cSJames Morse 		return;
1323f188a36cSJames Morse 
1324f188a36cSJames Morse 	/*
1325f188a36cSJames Morse 	 * Write all ~0 to all but the last 32bit-word, which may
1326f188a36cSJames Morse 	 * have fewer bits...
1327f188a36cSJames Morse 	 */
1328f188a36cSJames Morse 	num_words = DIV_ROUND_UP(wd, 32);
1329f188a36cSJames Morse 	for (i = 0; i < num_words - 1; i++, reg += sizeof(bm))
1330f188a36cSJames Morse 		__mpam_write_reg(msc, reg, bm);
1331f188a36cSJames Morse 
1332f188a36cSJames Morse 	/*
1333f188a36cSJames Morse 	 * ....and then the last (maybe) partial 32bit word. When wd is a
1334f188a36cSJames Morse 	 * multiple of 32, msb should be 31 to write a full 32bit word.
1335f188a36cSJames Morse 	 */
1336f188a36cSJames Morse 	msb = (wd - 1) % 32;
1337f188a36cSJames Morse 	bm = GENMASK(msb, 0);
1338f188a36cSJames Morse 	__mpam_write_reg(msc, reg, bm);
1339f188a36cSJames Morse }
1340f188a36cSJames Morse 
134109b89d2aSJames Morse /* Called via IPI. Call while holding an SRCU reference */
134209b89d2aSJames Morse static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
134309b89d2aSJames Morse 				      struct mpam_config *cfg)
1344f188a36cSJames Morse {
1345880df85dSJames Morse 	u32 pri_val = 0;
1346880df85dSJames Morse 	u16 cmax = MPAMCFG_CMAX_CMAX;
1347f188a36cSJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
1348f188a36cSJames Morse 	struct mpam_props *rprops = &ris->props;
1349880df85dSJames Morse 	u16 dspri = GENMASK(rprops->dspri_wd, 0);
1350880df85dSJames Morse 	u16 intpri = GENMASK(rprops->intpri_wd, 0);
1351f188a36cSJames Morse 
1352f188a36cSJames Morse 	mutex_lock(&msc->part_sel_lock);
1353f188a36cSJames Morse 	__mpam_part_sel(ris->ris_idx, partid, msc);
1354f188a36cSJames Morse 
1355880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_partid_nrw, rprops)) {
1356880df85dSJames Morse 		/* Update the intpartid mapping */
1357880df85dSJames Morse 		mpam_write_partsel_reg(msc, INTPARTID,
1358880df85dSJames Morse 				       MPAMCFG_INTPARTID_INTERNAL | partid);
1359880df85dSJames Morse 
1360880df85dSJames Morse 		/*
1361880df85dSJames Morse 		 * Then switch to the 'internal' partid to update the
1362880df85dSJames Morse 		 * configuration.
1363880df85dSJames Morse 		 */
1364880df85dSJames Morse 		__mpam_intpart_sel(ris->ris_idx, partid, msc);
1365880df85dSJames Morse 	}
1366880df85dSJames Morse 
136709b89d2aSJames Morse 	if (mpam_has_feature(mpam_feat_cpor_part, rprops) &&
136809b89d2aSJames Morse 	    mpam_has_feature(mpam_feat_cpor_part, cfg)) {
136909b89d2aSJames Morse 		if (cfg->reset_cpbm)
1370f188a36cSJames Morse 			mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd);
137109b89d2aSJames Morse 		else
137209b89d2aSJames Morse 			mpam_write_partsel_reg(msc, CPBM, cfg->cpbm);
137309b89d2aSJames Morse 	}
1374f188a36cSJames Morse 
137509b89d2aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_part, rprops) &&
137609b89d2aSJames Morse 	    mpam_has_feature(mpam_feat_mbw_part, cfg)) {
137709b89d2aSJames Morse 		if (cfg->reset_mbw_pbm)
1378f188a36cSJames Morse 			mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
137909b89d2aSJames Morse 		else
138009b89d2aSJames Morse 			mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
138109b89d2aSJames Morse 	}
1382f188a36cSJames Morse 
138309b89d2aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_min, rprops) &&
138409b89d2aSJames Morse 	    mpam_has_feature(mpam_feat_mbw_min, cfg))
1385f188a36cSJames Morse 		mpam_write_partsel_reg(msc, MBW_MIN, 0);
1386f188a36cSJames Morse 
138709b89d2aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_max, rprops) &&
138809b89d2aSJames Morse 	    mpam_has_feature(mpam_feat_mbw_max, cfg)) {
138909b89d2aSJames Morse 		if (cfg->reset_mbw_max)
1390f188a36cSJames Morse 			mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX);
139109b89d2aSJames Morse 		else
139209b89d2aSJames Morse 			mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
139309b89d2aSJames Morse 	}
1394f188a36cSJames Morse 
1395880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_prop, rprops) &&
1396880df85dSJames Morse 	    mpam_has_feature(mpam_feat_mbw_prop, cfg))
1397880df85dSJames Morse 		mpam_write_partsel_reg(msc, MBW_PROP, 0);
1398880df85dSJames Morse 
1399880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_cmax_cmax, rprops))
1400880df85dSJames Morse 		mpam_write_partsel_reg(msc, CMAX, cmax);
1401880df85dSJames Morse 
1402880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_cmax_cmin, rprops))
1403880df85dSJames Morse 		mpam_write_partsel_reg(msc, CMIN, 0);
1404880df85dSJames Morse 
1405880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_cmax_cassoc, rprops))
1406880df85dSJames Morse 		mpam_write_partsel_reg(msc, CASSOC, MPAMCFG_CASSOC_CASSOC);
1407880df85dSJames Morse 
1408880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_intpri_part, rprops) ||
1409880df85dSJames Morse 	    mpam_has_feature(mpam_feat_dspri_part, rprops)) {
1410880df85dSJames Morse 		/* aces high? */
1411880df85dSJames Morse 		if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
1412880df85dSJames Morse 			intpri = 0;
1413880df85dSJames Morse 		if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops))
1414880df85dSJames Morse 			dspri = 0;
1415880df85dSJames Morse 
1416880df85dSJames Morse 		if (mpam_has_feature(mpam_feat_intpri_part, rprops))
1417880df85dSJames Morse 			pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri);
1418880df85dSJames Morse 		if (mpam_has_feature(mpam_feat_dspri_part, rprops))
1419880df85dSJames Morse 			pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri);
1420880df85dSJames Morse 
1421880df85dSJames Morse 		mpam_write_partsel_reg(msc, PRI, pri_val);
1422880df85dSJames Morse 	}
1423880df85dSJames Morse 
1424f188a36cSJames Morse 	mutex_unlock(&msc->part_sel_lock);
1425f188a36cSJames Morse }
1426f188a36cSJames Morse 
142741e8a149SJames Morse /* Call with msc cfg_lock held */
142841e8a149SJames Morse static int mpam_restore_mbwu_state(void *_ris)
142941e8a149SJames Morse {
143041e8a149SJames Morse 	int i;
143141e8a149SJames Morse 	struct mon_read mwbu_arg;
143241e8a149SJames Morse 	struct mpam_msc_ris *ris = _ris;
14339e5afb7cSRohit Mathew 	struct mpam_class *class = ris->vmsc->comp->class;
143441e8a149SJames Morse 
143541e8a149SJames Morse 	for (i = 0; i < ris->props.num_mbwu_mon; i++) {
143641e8a149SJames Morse 		if (ris->mbwu_state[i].enabled) {
143741e8a149SJames Morse 			mwbu_arg.ris = ris;
143841e8a149SJames Morse 			mwbu_arg.ctx = &ris->mbwu_state[i].cfg;
14399e5afb7cSRohit Mathew 			mwbu_arg.type = mpam_msmon_choose_counter(class);
144041e8a149SJames Morse 
144141e8a149SJames Morse 			__ris_msmon_read(&mwbu_arg);
144241e8a149SJames Morse 		}
144341e8a149SJames Morse 	}
144441e8a149SJames Morse 
144541e8a149SJames Morse 	return 0;
144641e8a149SJames Morse }
144741e8a149SJames Morse 
144841e8a149SJames Morse /* Call with MSC cfg_lock held */
144941e8a149SJames Morse static int mpam_save_mbwu_state(void *arg)
145041e8a149SJames Morse {
145141e8a149SJames Morse 	int i;
145241e8a149SJames Morse 	u64 val;
145341e8a149SJames Morse 	struct mon_cfg *cfg;
145441e8a149SJames Morse 	u32 cur_flt, cur_ctl, mon_sel;
145541e8a149SJames Morse 	struct mpam_msc_ris *ris = arg;
145641e8a149SJames Morse 	struct msmon_mbwu_state *mbwu_state;
145741e8a149SJames Morse 	struct mpam_msc *msc = ris->vmsc->msc;
145841e8a149SJames Morse 
145941e8a149SJames Morse 	for (i = 0; i < ris->props.num_mbwu_mon; i++) {
146041e8a149SJames Morse 		mbwu_state = &ris->mbwu_state[i];
146141e8a149SJames Morse 		cfg = &mbwu_state->cfg;
146241e8a149SJames Morse 
146341e8a149SJames Morse 		if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
146441e8a149SJames Morse 			return -EIO;
146541e8a149SJames Morse 
146641e8a149SJames Morse 		mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) |
146741e8a149SJames Morse 			  FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
146841e8a149SJames Morse 		mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
146941e8a149SJames Morse 
147041e8a149SJames Morse 		cur_flt = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
147141e8a149SJames Morse 		cur_ctl = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
147241e8a149SJames Morse 		mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0);
147341e8a149SJames Morse 
14749e5afb7cSRohit Mathew 		if (mpam_ris_has_mbwu_long_counter(ris)) {
14759e5afb7cSRohit Mathew 			val = mpam_msc_read_mbwu_l(msc);
14769e5afb7cSRohit Mathew 			mpam_msc_zero_mbwu_l(msc);
14779e5afb7cSRohit Mathew 		} else {
147841e8a149SJames Morse 			val = mpam_read_monsel_reg(msc, MBWU);
147941e8a149SJames Morse 			mpam_write_monsel_reg(msc, MBWU, 0);
14809e5afb7cSRohit Mathew 		}
148141e8a149SJames Morse 
148241e8a149SJames Morse 		cfg->mon = i;
148341e8a149SJames Morse 		cfg->pmg = FIELD_GET(MSMON_CFG_x_FLT_PMG, cur_flt);
148441e8a149SJames Morse 		cfg->match_pmg = FIELD_GET(MSMON_CFG_x_CTL_MATCH_PMG, cur_ctl);
148541e8a149SJames Morse 		cfg->partid = FIELD_GET(MSMON_CFG_x_FLT_PARTID, cur_flt);
148641e8a149SJames Morse 		mbwu_state->correction += val;
148741e8a149SJames Morse 		mbwu_state->enabled = FIELD_GET(MSMON_CFG_x_CTL_EN, cur_ctl);
148841e8a149SJames Morse 		mpam_mon_sel_unlock(msc);
148941e8a149SJames Morse 	}
149041e8a149SJames Morse 
149141e8a149SJames Morse 	return 0;
149241e8a149SJames Morse }
149341e8a149SJames Morse 
149409b89d2aSJames Morse static void mpam_init_reset_cfg(struct mpam_config *reset_cfg)
149509b89d2aSJames Morse {
149609b89d2aSJames Morse 	*reset_cfg = (struct mpam_config) {
149709b89d2aSJames Morse 		.reset_cpbm = true,
149809b89d2aSJames Morse 		.reset_mbw_pbm = true,
149909b89d2aSJames Morse 		.reset_mbw_max = true,
150009b89d2aSJames Morse 	};
150109b89d2aSJames Morse 	bitmap_fill(reset_cfg->features, MPAM_FEATURE_LAST);
150209b89d2aSJames Morse }
150309b89d2aSJames Morse 
1504475228d1SJames Morse /*
1505475228d1SJames Morse  * Called via smp_call_on_cpu() to prevent migration, while still being
15063bd04fe7SJames Morse  * pre-emptible. Caller must hold mpam_srcu.
1507475228d1SJames Morse  */
1508475228d1SJames Morse static int mpam_reset_ris(void *arg)
1509f188a36cSJames Morse {
1510f188a36cSJames Morse 	u16 partid, partid_max;
151109b89d2aSJames Morse 	struct mpam_config reset_cfg;
1512475228d1SJames Morse 	struct mpam_msc_ris *ris = arg;
1513f188a36cSJames Morse 
1514f188a36cSJames Morse 	if (ris->in_reset_state)
1515475228d1SJames Morse 		return 0;
1516f188a36cSJames Morse 
151709b89d2aSJames Morse 	mpam_init_reset_cfg(&reset_cfg);
151809b89d2aSJames Morse 
1519f188a36cSJames Morse 	spin_lock(&partid_max_lock);
1520f188a36cSJames Morse 	partid_max = mpam_partid_max;
1521f188a36cSJames Morse 	spin_unlock(&partid_max_lock);
1522f188a36cSJames Morse 	for (partid = 0; partid <= partid_max; partid++)
152309b89d2aSJames Morse 		mpam_reprogram_ris_partid(ris, partid, &reset_cfg);
1524475228d1SJames Morse 
1525475228d1SJames Morse 	return 0;
1526475228d1SJames Morse }
1527475228d1SJames Morse 
1528475228d1SJames Morse /*
1529475228d1SJames Morse  * Get the preferred CPU for this MSC. If it is accessible from this CPU,
1530475228d1SJames Morse  * this CPU is preferred. This can be preempted/migrated, it will only result
1531475228d1SJames Morse  * in more work.
1532475228d1SJames Morse  */
1533475228d1SJames Morse static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc)
1534475228d1SJames Morse {
1535475228d1SJames Morse 	int cpu = raw_smp_processor_id();
1536475228d1SJames Morse 
1537475228d1SJames Morse 	if (cpumask_test_cpu(cpu, &msc->accessibility))
1538475228d1SJames Morse 		return cpu;
1539475228d1SJames Morse 
1540475228d1SJames Morse 	return cpumask_first_and(&msc->accessibility, cpu_online_mask);
1541475228d1SJames Morse }
1542475228d1SJames Morse 
1543475228d1SJames Morse static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *arg)
1544475228d1SJames Morse {
1545475228d1SJames Morse 	lockdep_assert_irqs_enabled();
1546475228d1SJames Morse 	lockdep_assert_cpus_held();
1547475228d1SJames Morse 	WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu)));
1548475228d1SJames Morse 
1549475228d1SJames Morse 	return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true);
1550f188a36cSJames Morse }
1551f188a36cSJames Morse 
155209b89d2aSJames Morse struct mpam_write_config_arg {
1553f188a36cSJames Morse 	struct mpam_msc_ris *ris;
155409b89d2aSJames Morse 	struct mpam_component *comp;
155509b89d2aSJames Morse 	u16 partid;
155609b89d2aSJames Morse };
1557f188a36cSJames Morse 
155809b89d2aSJames Morse static int __write_config(void *arg)
155909b89d2aSJames Morse {
156009b89d2aSJames Morse 	struct mpam_write_config_arg *c = arg;
156109b89d2aSJames Morse 
156209b89d2aSJames Morse 	mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]);
156309b89d2aSJames Morse 
156409b89d2aSJames Morse 	return 0;
156509b89d2aSJames Morse }
156609b89d2aSJames Morse 
156709b89d2aSJames Morse static void mpam_reprogram_msc(struct mpam_msc *msc)
156809b89d2aSJames Morse {
156909b89d2aSJames Morse 	u16 partid;
157009b89d2aSJames Morse 	bool reset;
157109b89d2aSJames Morse 	struct mpam_config *cfg;
157209b89d2aSJames Morse 	struct mpam_msc_ris *ris;
157309b89d2aSJames Morse 	struct mpam_write_config_arg arg;
1574f188a36cSJames Morse 
1575f188a36cSJames Morse 	/*
157609b89d2aSJames Morse 	 * No lock for mpam_partid_max as partid_max_published has been
157709b89d2aSJames Morse 	 * set by mpam_enabled(), so the values can no longer change.
1578f188a36cSJames Morse 	 */
157909b89d2aSJames Morse 	mpam_assert_partid_sizes_fixed();
158009b89d2aSJames Morse 
158109b89d2aSJames Morse 	mutex_lock(&msc->cfg_lock);
158209b89d2aSJames Morse 	list_for_each_entry_srcu(ris, &msc->ris, msc_list,
158309b89d2aSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
158409b89d2aSJames Morse 		if (!mpam_is_enabled() && !ris->in_reset_state) {
158509b89d2aSJames Morse 			mpam_touch_msc(msc, &mpam_reset_ris, ris);
158609b89d2aSJames Morse 			ris->in_reset_state = true;
158709b89d2aSJames Morse 			continue;
1588f188a36cSJames Morse 		}
158909b89d2aSJames Morse 
159009b89d2aSJames Morse 		arg.comp = ris->vmsc->comp;
159109b89d2aSJames Morse 		arg.ris = ris;
159209b89d2aSJames Morse 		reset = true;
159309b89d2aSJames Morse 		for (partid = 0; partid <= mpam_partid_max; partid++) {
159409b89d2aSJames Morse 			cfg = &ris->vmsc->comp->cfg[partid];
159509b89d2aSJames Morse 			if (!bitmap_empty(cfg->features, MPAM_FEATURE_LAST))
159609b89d2aSJames Morse 				reset = false;
159709b89d2aSJames Morse 
159809b89d2aSJames Morse 			arg.partid = partid;
159909b89d2aSJames Morse 			mpam_touch_msc(msc, __write_config, &arg);
160009b89d2aSJames Morse 		}
160109b89d2aSJames Morse 		ris->in_reset_state = reset;
160241e8a149SJames Morse 
160341e8a149SJames Morse 		if (mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props))
160441e8a149SJames Morse 			mpam_touch_msc(msc, &mpam_restore_mbwu_state, ris);
160509b89d2aSJames Morse 	}
160609b89d2aSJames Morse 	mutex_unlock(&msc->cfg_lock);
1607f188a36cSJames Morse }
1608f188a36cSJames Morse 
160949aa621cSJames Morse static void _enable_percpu_irq(void *_irq)
161049aa621cSJames Morse {
161149aa621cSJames Morse 	int *irq = _irq;
161249aa621cSJames Morse 
161349aa621cSJames Morse 	enable_percpu_irq(*irq, IRQ_TYPE_NONE);
161449aa621cSJames Morse }
161549aa621cSJames Morse 
16168f8d0ac1SJames Morse static int mpam_cpu_online(unsigned int cpu)
16178f8d0ac1SJames Morse {
1618f188a36cSJames Morse 	struct mpam_msc *msc;
1619f188a36cSJames Morse 
1620f188a36cSJames Morse 	guard(srcu)(&mpam_srcu);
1621f188a36cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
1622f188a36cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
1623f188a36cSJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
1624f188a36cSJames Morse 			continue;
1625f188a36cSJames Morse 
162649aa621cSJames Morse 		if (msc->reenable_error_ppi)
162749aa621cSJames Morse 			_enable_percpu_irq(&msc->reenable_error_ppi);
162849aa621cSJames Morse 
1629f188a36cSJames Morse 		if (atomic_fetch_inc(&msc->online_refs) == 0)
163009b89d2aSJames Morse 			mpam_reprogram_msc(msc);
1631f188a36cSJames Morse 	}
1632f188a36cSJames Morse 
16338f8d0ac1SJames Morse 	return 0;
16348f8d0ac1SJames Morse }
16358f8d0ac1SJames Morse 
16368f8d0ac1SJames Morse /* Before mpam is enabled, try to probe new MSC */
16378f8d0ac1SJames Morse static int mpam_discovery_cpu_online(unsigned int cpu)
16388f8d0ac1SJames Morse {
16398f8d0ac1SJames Morse 	int err = 0;
16408f8d0ac1SJames Morse 	struct mpam_msc *msc;
16418f8d0ac1SJames Morse 	bool new_device_probed = false;
16428f8d0ac1SJames Morse 
16433796f75aSJames Morse 	if (mpam_is_enabled())
16443796f75aSJames Morse 		return 0;
16453796f75aSJames Morse 
16468f8d0ac1SJames Morse 	guard(srcu)(&mpam_srcu);
16478f8d0ac1SJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
16488f8d0ac1SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
16498f8d0ac1SJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
16508f8d0ac1SJames Morse 			continue;
16518f8d0ac1SJames Morse 
16528f8d0ac1SJames Morse 		mutex_lock(&msc->probe_lock);
16538f8d0ac1SJames Morse 		if (!msc->probed)
16548f8d0ac1SJames Morse 			err = mpam_msc_hw_probe(msc);
16558f8d0ac1SJames Morse 		mutex_unlock(&msc->probe_lock);
16568f8d0ac1SJames Morse 
16578f8d0ac1SJames Morse 		if (err)
16588f8d0ac1SJames Morse 			break;
16598f8d0ac1SJames Morse 		new_device_probed = true;
16608f8d0ac1SJames Morse 	}
16618f8d0ac1SJames Morse 
16628f8d0ac1SJames Morse 	if (new_device_probed && !err)
16638f8d0ac1SJames Morse 		schedule_work(&mpam_enable_work);
16648f8d0ac1SJames Morse 	if (err) {
16658f8d0ac1SJames Morse 		mpam_disable_reason = "error during probing";
16668f8d0ac1SJames Morse 		schedule_work(&mpam_broken_work);
16678f8d0ac1SJames Morse 	}
16688f8d0ac1SJames Morse 
16698f8d0ac1SJames Morse 	return err;
16708f8d0ac1SJames Morse }
16718f8d0ac1SJames Morse 
16728f8d0ac1SJames Morse static int mpam_cpu_offline(unsigned int cpu)
16738f8d0ac1SJames Morse {
1674f188a36cSJames Morse 	struct mpam_msc *msc;
1675f188a36cSJames Morse 
1676f188a36cSJames Morse 	guard(srcu)(&mpam_srcu);
1677f188a36cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
1678f188a36cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
1679f188a36cSJames Morse 		if (!cpumask_test_cpu(cpu, &msc->accessibility))
1680f188a36cSJames Morse 			continue;
1681f188a36cSJames Morse 
168249aa621cSJames Morse 		if (msc->reenable_error_ppi)
168349aa621cSJames Morse 			disable_percpu_irq(msc->reenable_error_ppi);
168449aa621cSJames Morse 
168509b89d2aSJames Morse 		if (atomic_dec_and_test(&msc->online_refs)) {
168609b89d2aSJames Morse 			struct mpam_msc_ris *ris;
168709b89d2aSJames Morse 
168809b89d2aSJames Morse 			mutex_lock(&msc->cfg_lock);
168909b89d2aSJames Morse 			list_for_each_entry_srcu(ris, &msc->ris, msc_list,
169009b89d2aSJames Morse 						 srcu_read_lock_held(&mpam_srcu)) {
169109b89d2aSJames Morse 				mpam_touch_msc(msc, &mpam_reset_ris, ris);
169209b89d2aSJames Morse 
169309b89d2aSJames Morse 				/*
169409b89d2aSJames Morse 				 * The reset state for non-zero partid may be
169509b89d2aSJames Morse 				 * lost while the CPUs are offline.
169609b89d2aSJames Morse 				 */
169709b89d2aSJames Morse 				ris->in_reset_state = false;
169841e8a149SJames Morse 
169941e8a149SJames Morse 				if (mpam_is_enabled())
170041e8a149SJames Morse 					mpam_touch_msc(msc, &mpam_save_mbwu_state, ris);
170109b89d2aSJames Morse 			}
170209b89d2aSJames Morse 			mutex_unlock(&msc->cfg_lock);
170309b89d2aSJames Morse 		}
1704f188a36cSJames Morse 	}
1705f188a36cSJames Morse 
17068f8d0ac1SJames Morse 	return 0;
17078f8d0ac1SJames Morse }
17088f8d0ac1SJames Morse 
17098f8d0ac1SJames Morse static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online),
17108f8d0ac1SJames Morse 					  int (*offline)(unsigned int offline),
17118f8d0ac1SJames Morse 					  char *name)
17128f8d0ac1SJames Morse {
17138f8d0ac1SJames Morse 	mutex_lock(&mpam_cpuhp_state_lock);
17148f8d0ac1SJames Morse 	if (mpam_cpuhp_state) {
17158f8d0ac1SJames Morse 		cpuhp_remove_state(mpam_cpuhp_state);
17168f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
17178f8d0ac1SJames Morse 	}
17188f8d0ac1SJames Morse 
17198f8d0ac1SJames Morse 	mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online,
17208f8d0ac1SJames Morse 					     offline);
17218f8d0ac1SJames Morse 	if (mpam_cpuhp_state <= 0) {
17228f8d0ac1SJames Morse 		pr_err("Failed to register cpuhp callbacks");
17238f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
17248f8d0ac1SJames Morse 	}
17258f8d0ac1SJames Morse 	mutex_unlock(&mpam_cpuhp_state_lock);
17268f8d0ac1SJames Morse }
17278f8d0ac1SJames Morse 
172849aa621cSJames Morse static int __setup_ppi(struct mpam_msc *msc)
172949aa621cSJames Morse {
173049aa621cSJames Morse 	int cpu;
173149aa621cSJames Morse 
173249aa621cSJames Morse 	msc->error_dev_id = alloc_percpu(struct mpam_msc *);
173349aa621cSJames Morse 	if (!msc->error_dev_id)
173449aa621cSJames Morse 		return -ENOMEM;
173549aa621cSJames Morse 
173649aa621cSJames Morse 	for_each_cpu(cpu, &msc->accessibility)
173749aa621cSJames Morse 		*per_cpu_ptr(msc->error_dev_id, cpu) = msc;
173849aa621cSJames Morse 
173949aa621cSJames Morse 	return 0;
174049aa621cSJames Morse }
174149aa621cSJames Morse 
174249aa621cSJames Morse static int mpam_msc_setup_error_irq(struct mpam_msc *msc)
174349aa621cSJames Morse {
174449aa621cSJames Morse 	int irq;
174549aa621cSJames Morse 
174649aa621cSJames Morse 	irq = platform_get_irq_byname_optional(msc->pdev, "error");
174749aa621cSJames Morse 	if (irq <= 0)
174849aa621cSJames Morse 		return 0;
174949aa621cSJames Morse 
175049aa621cSJames Morse 	/* Allocate and initialise the percpu device pointer for PPI */
175149aa621cSJames Morse 	if (irq_is_percpu(irq))
175249aa621cSJames Morse 		return __setup_ppi(msc);
175349aa621cSJames Morse 
175449aa621cSJames Morse 	/* sanity check: shared interrupts can be routed anywhere? */
175549aa621cSJames Morse 	if (!cpumask_equal(&msc->accessibility, cpu_possible_mask)) {
175649aa621cSJames Morse 		pr_err_once("msc:%u is a private resource with a shared error interrupt",
175749aa621cSJames Morse 			    msc->id);
175849aa621cSJames Morse 		return -EINVAL;
175949aa621cSJames Morse 	}
176049aa621cSJames Morse 
176149aa621cSJames Morse 	return 0;
176249aa621cSJames Morse }
176349aa621cSJames Morse 
176401fb4b82SJames Morse /*
1765f04046f2SJames Morse  * An MSC can control traffic from a set of CPUs, but may only be accessible
1766f04046f2SJames Morse  * from a (hopefully wider) set of CPUs. The common reason for this is power
1767f04046f2SJames Morse  * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the
1768f04046f2SJames Morse  * corresponding cache may also be powered off. By making accesses from
1769f04046f2SJames Morse  * one of those CPUs, we ensure we don't access a cache that's powered off.
1770f04046f2SJames Morse  */
1771f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc)
1772f04046f2SJames Morse {
1773f04046f2SJames Morse 	u32 affinity_id;
1774f04046f2SJames Morse 	int err;
1775f04046f2SJames Morse 
1776f04046f2SJames Morse 	err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity",
1777f04046f2SJames Morse 				       &affinity_id);
1778f04046f2SJames Morse 	if (err)
1779f04046f2SJames Morse 		cpumask_copy(&msc->accessibility, cpu_possible_mask);
1780f04046f2SJames Morse 	else
1781f04046f2SJames Morse 		acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility);
1782f04046f2SJames Morse }
1783f04046f2SJames Morse 
178401fb4b82SJames Morse /*
178501fb4b82SJames Morse  * There are two ways of reaching a struct mpam_msc_ris. Via the
178601fb4b82SJames Morse  * class->component->vmsc->ris, or via the msc.
178701fb4b82SJames Morse  * When destroying the msc, the other side needs unlinking and cleaning up too.
178801fb4b82SJames Morse  */
1789f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc)
1790f04046f2SJames Morse {
1791f04046f2SJames Morse 	struct platform_device *pdev = msc->pdev;
179201fb4b82SJames Morse 	struct mpam_msc_ris *ris, *tmp;
1793f04046f2SJames Morse 
1794f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
1795f04046f2SJames Morse 
179601fb4b82SJames Morse 	list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list)
179701fb4b82SJames Morse 		mpam_ris_destroy(ris);
179801fb4b82SJames Morse 
1799f04046f2SJames Morse 	list_del_rcu(&msc->all_msc_list);
1800f04046f2SJames Morse 	platform_set_drvdata(pdev, NULL);
180101fb4b82SJames Morse 
180201fb4b82SJames Morse 	add_to_garbage(msc);
1803f04046f2SJames Morse }
1804f04046f2SJames Morse 
1805f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev)
1806f04046f2SJames Morse {
1807f04046f2SJames Morse 	struct mpam_msc *msc = platform_get_drvdata(pdev);
1808f04046f2SJames Morse 
1809f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
1810f04046f2SJames Morse 	mpam_msc_destroy(msc);
1811f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
1812f04046f2SJames Morse 
181301fb4b82SJames Morse 	mpam_free_garbage();
1814f04046f2SJames Morse }
1815f04046f2SJames Morse 
1816f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
1817f04046f2SJames Morse {
1818f04046f2SJames Morse 	int err;
1819f04046f2SJames Morse 	u32 tmp;
1820f04046f2SJames Morse 	struct mpam_msc *msc;
1821f04046f2SJames Morse 	struct resource *msc_res;
1822f04046f2SJames Morse 	struct device *dev = &pdev->dev;
1823f04046f2SJames Morse 
1824f04046f2SJames Morse 	lockdep_assert_held(&mpam_list_lock);
1825f04046f2SJames Morse 
1826f04046f2SJames Morse 	msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL);
1827f04046f2SJames Morse 	if (!msc)
1828f04046f2SJames Morse 		return ERR_PTR(-ENOMEM);
182901fb4b82SJames Morse 	init_garbage(&msc->garbage);
183001fb4b82SJames Morse 	msc->garbage.pdev = pdev;
1831f04046f2SJames Morse 
1832f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->probe_lock);
1833f04046f2SJames Morse 	if (err)
1834f04046f2SJames Morse 		return ERR_PTR(err);
1835f04046f2SJames Morse 
1836f04046f2SJames Morse 	err = devm_mutex_init(dev, &msc->part_sel_lock);
1837f04046f2SJames Morse 	if (err)
1838f04046f2SJames Morse 		return ERR_PTR(err);
1839f04046f2SJames Morse 
184049aa621cSJames Morse 	err = devm_mutex_init(dev, &msc->error_irq_lock);
184149aa621cSJames Morse 	if (err)
184249aa621cSJames Morse 		return ERR_PTR(err);
184309b89d2aSJames Morse 
184409b89d2aSJames Morse 	err = devm_mutex_init(dev, &msc->cfg_lock);
184509b89d2aSJames Morse 	if (err)
184609b89d2aSJames Morse 		return ERR_PTR(err);
184709b89d2aSJames Morse 
1848d02beb06SJames Morse 	mpam_mon_sel_lock_init(msc);
1849f04046f2SJames Morse 	msc->id = pdev->id;
1850f04046f2SJames Morse 	msc->pdev = pdev;
1851f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->all_msc_list);
1852f04046f2SJames Morse 	INIT_LIST_HEAD_RCU(&msc->ris);
1853f04046f2SJames Morse 
1854f04046f2SJames Morse 	update_msc_accessibility(msc);
1855f04046f2SJames Morse 	if (cpumask_empty(&msc->accessibility)) {
1856f04046f2SJames Morse 		dev_err_once(dev, "MSC is not accessible from any CPU!");
1857f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
1858f04046f2SJames Morse 	}
1859f04046f2SJames Morse 
186049aa621cSJames Morse 	err = mpam_msc_setup_error_irq(msc);
186149aa621cSJames Morse 	if (err)
186249aa621cSJames Morse 		return ERR_PTR(err);
186349aa621cSJames Morse 
1864f04046f2SJames Morse 	if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp))
1865f04046f2SJames Morse 		msc->iface = MPAM_IFACE_MMIO;
1866f04046f2SJames Morse 	else
1867f04046f2SJames Morse 		msc->iface = MPAM_IFACE_PCC;
1868f04046f2SJames Morse 
1869f04046f2SJames Morse 	if (msc->iface == MPAM_IFACE_MMIO) {
1870f04046f2SJames Morse 		void __iomem *io;
1871f04046f2SJames Morse 
1872f04046f2SJames Morse 		io = devm_platform_get_and_ioremap_resource(pdev, 0,
1873f04046f2SJames Morse 							    &msc_res);
1874f04046f2SJames Morse 		if (IS_ERR(io)) {
1875f04046f2SJames Morse 			dev_err_once(dev, "Failed to map MSC base address\n");
1876f04046f2SJames Morse 			return ERR_CAST(io);
1877f04046f2SJames Morse 		}
1878f04046f2SJames Morse 		msc->mapped_hwpage_sz = msc_res->end - msc_res->start;
1879f04046f2SJames Morse 		msc->mapped_hwpage = io;
1880f04046f2SJames Morse 	} else {
1881f04046f2SJames Morse 		return ERR_PTR(-EINVAL);
1882f04046f2SJames Morse 	}
1883f04046f2SJames Morse 
1884f04046f2SJames Morse 	list_add_rcu(&msc->all_msc_list, &mpam_all_msc);
1885f04046f2SJames Morse 	platform_set_drvdata(pdev, msc);
1886f04046f2SJames Morse 
1887f04046f2SJames Morse 	return msc;
1888f04046f2SJames Morse }
1889f04046f2SJames Morse 
1890f04046f2SJames Morse static int fw_num_msc;
1891f04046f2SJames Morse 
1892f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev)
1893f04046f2SJames Morse {
1894f04046f2SJames Morse 	int err;
1895f04046f2SJames Morse 	struct mpam_msc *msc = NULL;
1896f04046f2SJames Morse 	void *plat_data = pdev->dev.platform_data;
1897f04046f2SJames Morse 
1898f04046f2SJames Morse 	mutex_lock(&mpam_list_lock);
1899f04046f2SJames Morse 	msc = do_mpam_msc_drv_probe(pdev);
1900f04046f2SJames Morse 	mutex_unlock(&mpam_list_lock);
1901f04046f2SJames Morse 
1902f04046f2SJames Morse 	if (IS_ERR(msc))
1903f04046f2SJames Morse 		return PTR_ERR(msc);
1904f04046f2SJames Morse 
1905f04046f2SJames Morse 	/* Create RIS entries described by firmware */
1906f04046f2SJames Morse 	err = acpi_mpam_parse_resources(msc, plat_data);
1907f04046f2SJames Morse 	if (err) {
1908f04046f2SJames Morse 		mpam_msc_drv_remove(pdev);
1909f04046f2SJames Morse 		return err;
1910f04046f2SJames Morse 	}
1911f04046f2SJames Morse 
1912f04046f2SJames Morse 	if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc)
19138f8d0ac1SJames Morse 		mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL,
19148f8d0ac1SJames Morse 					      "mpam:drv_probe");
1915f04046f2SJames Morse 
1916f04046f2SJames Morse 	return 0;
1917f04046f2SJames Morse }
1918f04046f2SJames Morse 
1919f04046f2SJames Morse static struct platform_driver mpam_msc_driver = {
1920f04046f2SJames Morse 	.driver = {
1921f04046f2SJames Morse 		.name = "mpam_msc",
1922f04046f2SJames Morse 	},
1923f04046f2SJames Morse 	.probe = mpam_msc_drv_probe,
1924f04046f2SJames Morse 	.remove = mpam_msc_drv_remove,
1925f04046f2SJames Morse };
1926f04046f2SJames Morse 
1927c10ca83aSJames Morse /* Any of these features mean the BWA_WD field is valid. */
1928c10ca83aSJames Morse static bool mpam_has_bwa_wd_feature(struct mpam_props *props)
1929c10ca83aSJames Morse {
1930c10ca83aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_min, props))
1931c10ca83aSJames Morse 		return true;
1932c10ca83aSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_max, props))
1933c10ca83aSJames Morse 		return true;
1934880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_mbw_prop, props))
1935880df85dSJames Morse 		return true;
1936880df85dSJames Morse 	return false;
1937880df85dSJames Morse }
1938880df85dSJames Morse 
1939880df85dSJames Morse /* Any of these features mean the CMAX_WD field is valid. */
1940880df85dSJames Morse static bool mpam_has_cmax_wd_feature(struct mpam_props *props)
1941880df85dSJames Morse {
1942880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_cmax_cmax, props))
1943880df85dSJames Morse 		return true;
1944880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_cmax_cmin, props))
1945880df85dSJames Morse 		return true;
1946c10ca83aSJames Morse 	return false;
1947c10ca83aSJames Morse }
1948c10ca83aSJames Morse 
1949c10ca83aSJames Morse #define MISMATCHED_HELPER(parent, child, helper, field, alias)		\
1950c10ca83aSJames Morse 	helper(parent) &&						\
1951c10ca83aSJames Morse 	((helper(child) && (parent)->field != (child)->field) ||	\
1952c10ca83aSJames Morse 	 (!helper(child) && !(alias)))
1953c10ca83aSJames Morse 
1954c10ca83aSJames Morse #define MISMATCHED_FEAT(parent, child, feat, field, alias)		     \
1955c10ca83aSJames Morse 	mpam_has_feature((feat), (parent)) &&				     \
1956c10ca83aSJames Morse 	((mpam_has_feature((feat), (child)) && (parent)->field != (child)->field) || \
1957c10ca83aSJames Morse 	 (!mpam_has_feature((feat), (child)) && !(alias)))
1958c10ca83aSJames Morse 
1959c10ca83aSJames Morse #define CAN_MERGE_FEAT(parent, child, feat, alias)			\
1960c10ca83aSJames Morse 	(alias) && !mpam_has_feature((feat), (parent)) &&		\
1961c10ca83aSJames Morse 	mpam_has_feature((feat), (child))
1962c10ca83aSJames Morse 
1963c10ca83aSJames Morse /*
1964c10ca83aSJames Morse  * Combine two props fields.
1965c10ca83aSJames Morse  * If this is for controls that alias the same resource, it is safe to just
1966c10ca83aSJames Morse  * copy the values over. If two aliasing controls implement the same scheme
1967c10ca83aSJames Morse  * a safe value must be picked.
1968c10ca83aSJames Morse  * For non-aliasing controls, these control different resources, and the
1969c10ca83aSJames Morse  * resulting safe value must be compatible with both. When merging values in
1970c10ca83aSJames Morse  * the tree, all the aliasing resources must be handled first.
1971c10ca83aSJames Morse  * On mismatch, parent is modified.
1972c10ca83aSJames Morse  */
1973c10ca83aSJames Morse static void __props_mismatch(struct mpam_props *parent,
1974c10ca83aSJames Morse 			     struct mpam_props *child, bool alias)
1975c10ca83aSJames Morse {
1976c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) {
1977c10ca83aSJames Morse 		parent->cpbm_wd = child->cpbm_wd;
1978c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part,
1979c10ca83aSJames Morse 				   cpbm_wd, alias)) {
1980c10ca83aSJames Morse 		pr_debug("cleared cpor_part\n");
1981c10ca83aSJames Morse 		mpam_clear_feature(mpam_feat_cpor_part, parent);
1982c10ca83aSJames Morse 		parent->cpbm_wd = 0;
1983c10ca83aSJames Morse 	}
1984c10ca83aSJames Morse 
1985c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) {
1986c10ca83aSJames Morse 		parent->mbw_pbm_bits = child->mbw_pbm_bits;
1987c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part,
1988c10ca83aSJames Morse 				   mbw_pbm_bits, alias)) {
1989c10ca83aSJames Morse 		pr_debug("cleared mbw_part\n");
1990c10ca83aSJames Morse 		mpam_clear_feature(mpam_feat_mbw_part, parent);
1991c10ca83aSJames Morse 		parent->mbw_pbm_bits = 0;
1992c10ca83aSJames Morse 	}
1993c10ca83aSJames Morse 
1994c10ca83aSJames Morse 	/* bwa_wd is a count of bits, fewer bits means less precision */
1995c10ca83aSJames Morse 	if (alias && !mpam_has_bwa_wd_feature(parent) &&
1996c10ca83aSJames Morse 	    mpam_has_bwa_wd_feature(child)) {
1997c10ca83aSJames Morse 		parent->bwa_wd = child->bwa_wd;
1998c10ca83aSJames Morse 	} else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature,
1999c10ca83aSJames Morse 				     bwa_wd, alias)) {
2000c10ca83aSJames Morse 		pr_debug("took the min bwa_wd\n");
2001c10ca83aSJames Morse 		parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd);
2002c10ca83aSJames Morse 	}
2003c10ca83aSJames Morse 
2004880df85dSJames Morse 	if (alias && !mpam_has_cmax_wd_feature(parent) && mpam_has_cmax_wd_feature(child)) {
2005880df85dSJames Morse 		parent->cmax_wd = child->cmax_wd;
2006880df85dSJames Morse 	} else if (MISMATCHED_HELPER(parent, child, mpam_has_cmax_wd_feature,
2007880df85dSJames Morse 				     cmax_wd, alias)) {
2008880df85dSJames Morse 		pr_debug("%s took the min cmax_wd\n", __func__);
2009880df85dSJames Morse 		parent->cmax_wd = min(parent->cmax_wd, child->cmax_wd);
2010880df85dSJames Morse 	}
2011880df85dSJames Morse 
2012880df85dSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_cmax_cassoc, alias)) {
2013880df85dSJames Morse 		parent->cassoc_wd = child->cassoc_wd;
2014880df85dSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_cmax_cassoc,
2015880df85dSJames Morse 				   cassoc_wd, alias)) {
2016880df85dSJames Morse 		pr_debug("%s cleared cassoc_wd\n", __func__);
2017880df85dSJames Morse 		mpam_clear_feature(mpam_feat_cmax_cassoc, parent);
2018880df85dSJames Morse 		parent->cassoc_wd = 0;
2019880df85dSJames Morse 	}
2020880df85dSJames Morse 
2021c10ca83aSJames Morse 	/* For num properties, take the minimum */
2022c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) {
2023c10ca83aSJames Morse 		parent->num_csu_mon = child->num_csu_mon;
2024c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu,
2025c10ca83aSJames Morse 				   num_csu_mon, alias)) {
2026c10ca83aSJames Morse 		pr_debug("took the min num_csu_mon\n");
2027c10ca83aSJames Morse 		parent->num_csu_mon = min(parent->num_csu_mon,
2028c10ca83aSJames Morse 					  child->num_csu_mon);
2029c10ca83aSJames Morse 	}
2030c10ca83aSJames Morse 
2031c10ca83aSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) {
2032c10ca83aSJames Morse 		parent->num_mbwu_mon = child->num_mbwu_mon;
2033c10ca83aSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu,
2034c10ca83aSJames Morse 				   num_mbwu_mon, alias)) {
2035c10ca83aSJames Morse 		pr_debug("took the min num_mbwu_mon\n");
2036c10ca83aSJames Morse 		parent->num_mbwu_mon = min(parent->num_mbwu_mon,
2037c10ca83aSJames Morse 					   child->num_mbwu_mon);
2038c10ca83aSJames Morse 	}
2039c10ca83aSJames Morse 
2040880df85dSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_intpri_part, alias)) {
2041880df85dSJames Morse 		parent->intpri_wd = child->intpri_wd;
2042880df85dSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_intpri_part,
2043880df85dSJames Morse 				   intpri_wd, alias)) {
2044880df85dSJames Morse 		pr_debug("%s took the min intpri_wd\n", __func__);
2045880df85dSJames Morse 		parent->intpri_wd = min(parent->intpri_wd, child->intpri_wd);
2046880df85dSJames Morse 	}
2047880df85dSJames Morse 
2048880df85dSJames Morse 	if (CAN_MERGE_FEAT(parent, child, mpam_feat_dspri_part, alias)) {
2049880df85dSJames Morse 		parent->dspri_wd = child->dspri_wd;
2050880df85dSJames Morse 	} else if (MISMATCHED_FEAT(parent, child, mpam_feat_dspri_part,
2051880df85dSJames Morse 				   dspri_wd, alias)) {
2052880df85dSJames Morse 		pr_debug("%s took the min dspri_wd\n", __func__);
2053880df85dSJames Morse 		parent->dspri_wd = min(parent->dspri_wd, child->dspri_wd);
2054880df85dSJames Morse 	}
2055880df85dSJames Morse 
2056880df85dSJames Morse 	/* TODO: alias support for these two */
2057880df85dSJames Morse 	/* {int,ds}pri may not have differing 0-low behaviour */
2058880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_intpri_part, parent) &&
2059880df85dSJames Morse 	    (!mpam_has_feature(mpam_feat_intpri_part, child) ||
2060880df85dSJames Morse 	     mpam_has_feature(mpam_feat_intpri_part_0_low, parent) !=
2061880df85dSJames Morse 	     mpam_has_feature(mpam_feat_intpri_part_0_low, child))) {
2062880df85dSJames Morse 		pr_debug("%s cleared intpri_part\n", __func__);
2063880df85dSJames Morse 		mpam_clear_feature(mpam_feat_intpri_part, parent);
2064880df85dSJames Morse 		mpam_clear_feature(mpam_feat_intpri_part_0_low, parent);
2065880df85dSJames Morse 	}
2066880df85dSJames Morse 	if (mpam_has_feature(mpam_feat_dspri_part, parent) &&
2067880df85dSJames Morse 	    (!mpam_has_feature(mpam_feat_dspri_part, child) ||
2068880df85dSJames Morse 	     mpam_has_feature(mpam_feat_dspri_part_0_low, parent) !=
2069880df85dSJames Morse 	     mpam_has_feature(mpam_feat_dspri_part_0_low, child))) {
2070880df85dSJames Morse 		pr_debug("%s cleared dspri_part\n", __func__);
2071880df85dSJames Morse 		mpam_clear_feature(mpam_feat_dspri_part, parent);
2072880df85dSJames Morse 		mpam_clear_feature(mpam_feat_dspri_part_0_low, parent);
2073880df85dSJames Morse 	}
2074880df85dSJames Morse 
2075c10ca83aSJames Morse 	if (alias) {
2076c10ca83aSJames Morse 		/* Merge features for aliased resources */
2077c10ca83aSJames Morse 		bitmap_or(parent->features, parent->features, child->features, MPAM_FEATURE_LAST);
2078c10ca83aSJames Morse 	} else {
2079c10ca83aSJames Morse 		/* Clear missing features for non aliasing */
2080c10ca83aSJames Morse 		bitmap_and(parent->features, parent->features, child->features, MPAM_FEATURE_LAST);
2081c10ca83aSJames Morse 	}
2082c10ca83aSJames Morse }
2083c10ca83aSJames Morse 
2084c10ca83aSJames Morse /*
2085c10ca83aSJames Morse  * If a vmsc doesn't match class feature/configuration, do the right thing(tm).
2086c10ca83aSJames Morse  * For 'num' properties we can just take the minimum.
2087c10ca83aSJames Morse  * For properties where the mismatched unused bits would make a difference, we
2088c10ca83aSJames Morse  * nobble the class feature, as we can't configure all the resources.
2089c10ca83aSJames Morse  * e.g. The L3 cache is composed of two resources with 13 and 17 portion
2090c10ca83aSJames Morse  * bitmaps respectively.
2091c10ca83aSJames Morse  */
2092c10ca83aSJames Morse static void
2093c10ca83aSJames Morse __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc)
2094c10ca83aSJames Morse {
2095c10ca83aSJames Morse 	struct mpam_props *cprops = &class->props;
2096c10ca83aSJames Morse 	struct mpam_props *vprops = &vmsc->props;
2097c10ca83aSJames Morse 	struct device *dev = &vmsc->msc->pdev->dev;
2098c10ca83aSJames Morse 
2099c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock); /* we modify class */
2100c10ca83aSJames Morse 
2101c10ca83aSJames Morse 	dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n",
2102c10ca83aSJames Morse 		(long)cprops->features, (long)vprops->features);
2103c10ca83aSJames Morse 
2104c10ca83aSJames Morse 	/* Take the safe value for any common features */
2105c10ca83aSJames Morse 	__props_mismatch(cprops, vprops, false);
2106c10ca83aSJames Morse }
2107c10ca83aSJames Morse 
2108c10ca83aSJames Morse static void
2109c10ca83aSJames Morse __vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris)
2110c10ca83aSJames Morse {
2111c10ca83aSJames Morse 	struct mpam_props *rprops = &ris->props;
2112c10ca83aSJames Morse 	struct mpam_props *vprops = &vmsc->props;
2113c10ca83aSJames Morse 	struct device *dev = &vmsc->msc->pdev->dev;
2114c10ca83aSJames Morse 
2115c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */
2116c10ca83aSJames Morse 
2117c10ca83aSJames Morse 	dev_dbg(dev, "Merging features for vmsc:0x%lx |= ris:0x%lx\n",
2118c10ca83aSJames Morse 		(long)vprops->features, (long)rprops->features);
2119c10ca83aSJames Morse 
2120c10ca83aSJames Morse 	/*
2121c10ca83aSJames Morse 	 * Merge mismatched features - Copy any features that aren't common,
2122c10ca83aSJames Morse 	 * but take the safe value for any common features.
2123c10ca83aSJames Morse 	 */
2124c10ca83aSJames Morse 	__props_mismatch(vprops, rprops, true);
2125c10ca83aSJames Morse }
2126c10ca83aSJames Morse 
2127c10ca83aSJames Morse /*
2128c10ca83aSJames Morse  * Copy the first component's first vMSC's properties and features to the
2129c10ca83aSJames Morse  * class. __class_props_mismatch() will remove conflicts.
2130c10ca83aSJames Morse  * It is not possible to have a class with no components, or a component with
2131c10ca83aSJames Morse  * no resources. The vMSC properties have already been built.
2132c10ca83aSJames Morse  */
2133c10ca83aSJames Morse static void mpam_enable_init_class_features(struct mpam_class *class)
2134c10ca83aSJames Morse {
2135c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
2136c10ca83aSJames Morse 	struct mpam_component *comp;
2137c10ca83aSJames Morse 
2138c10ca83aSJames Morse 	comp = list_first_entry(&class->components,
2139c10ca83aSJames Morse 				struct mpam_component, class_list);
2140c10ca83aSJames Morse 	vmsc = list_first_entry(&comp->vmsc,
2141c10ca83aSJames Morse 				struct mpam_vmsc, comp_list);
2142c10ca83aSJames Morse 
2143c10ca83aSJames Morse 	class->props = vmsc->props;
2144c10ca83aSJames Morse }
2145c10ca83aSJames Morse 
2146c10ca83aSJames Morse static void mpam_enable_merge_vmsc_features(struct mpam_component *comp)
2147c10ca83aSJames Morse {
2148c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
2149c10ca83aSJames Morse 	struct mpam_msc_ris *ris;
2150c10ca83aSJames Morse 	struct mpam_class *class = comp->class;
2151c10ca83aSJames Morse 
2152c10ca83aSJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
2153c10ca83aSJames Morse 		list_for_each_entry(ris, &vmsc->ris, vmsc_list) {
2154c10ca83aSJames Morse 			__vmsc_props_mismatch(vmsc, ris);
2155c10ca83aSJames Morse 			class->nrdy_usec = max(class->nrdy_usec,
2156c10ca83aSJames Morse 					       vmsc->msc->nrdy_usec);
2157c10ca83aSJames Morse 		}
2158c10ca83aSJames Morse 	}
2159c10ca83aSJames Morse }
2160c10ca83aSJames Morse 
2161c10ca83aSJames Morse static void mpam_enable_merge_class_features(struct mpam_component *comp)
2162c10ca83aSJames Morse {
2163c10ca83aSJames Morse 	struct mpam_vmsc *vmsc;
2164c10ca83aSJames Morse 	struct mpam_class *class = comp->class;
2165c10ca83aSJames Morse 
2166c10ca83aSJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list)
2167c10ca83aSJames Morse 		__class_props_mismatch(class, vmsc);
2168c10ca83aSJames Morse }
2169c10ca83aSJames Morse 
2170c10ca83aSJames Morse /*
2171c10ca83aSJames Morse  * Merge all the common resource features into class.
2172c10ca83aSJames Morse  * vmsc features are bitwise-or'd together by mpam_enable_merge_vmsc_features()
2173c10ca83aSJames Morse  * as the first step so that mpam_enable_init_class_features() can initialise
2174c10ca83aSJames Morse  * the class with a representative set of features.
2175c10ca83aSJames Morse  * Next the mpam_enable_merge_class_features() bitwise-and's all the vmsc
2176c10ca83aSJames Morse  * features to form the class features.
2177c10ca83aSJames Morse  * Other features are the min/max as appropriate.
2178c10ca83aSJames Morse  *
2179c10ca83aSJames Morse  * To avoid walking the whole tree twice, the class->nrdy_usec property is
2180c10ca83aSJames Morse  * updated when working with the vmsc as it is a max(), and doesn't need
2181c10ca83aSJames Morse  * initialising first.
2182c10ca83aSJames Morse  */
2183c10ca83aSJames Morse static void mpam_enable_merge_features(struct list_head *all_classes_list)
2184c10ca83aSJames Morse {
2185c10ca83aSJames Morse 	struct mpam_class *class;
2186c10ca83aSJames Morse 	struct mpam_component *comp;
2187c10ca83aSJames Morse 
2188c10ca83aSJames Morse 	lockdep_assert_held(&mpam_list_lock);
2189c10ca83aSJames Morse 
2190c10ca83aSJames Morse 	list_for_each_entry(class, all_classes_list, classes_list) {
2191c10ca83aSJames Morse 		list_for_each_entry(comp, &class->components, class_list)
2192c10ca83aSJames Morse 			mpam_enable_merge_vmsc_features(comp);
2193c10ca83aSJames Morse 
2194c10ca83aSJames Morse 		mpam_enable_init_class_features(class);
2195c10ca83aSJames Morse 
2196c10ca83aSJames Morse 		list_for_each_entry(comp, &class->components, class_list)
2197c10ca83aSJames Morse 			mpam_enable_merge_class_features(comp);
2198c10ca83aSJames Morse 	}
2199c10ca83aSJames Morse }
2200c10ca83aSJames Morse 
220149aa621cSJames Morse static char *mpam_errcode_names[16] = {
220249aa621cSJames Morse 	[MPAM_ERRCODE_NONE]			= "No error",
220349aa621cSJames Morse 	[MPAM_ERRCODE_PARTID_SEL_RANGE]		= "PARTID_SEL_Range",
220449aa621cSJames Morse 	[MPAM_ERRCODE_REQ_PARTID_RANGE]		= "Req_PARTID_Range",
220549aa621cSJames Morse 	[MPAM_ERRCODE_MSMONCFG_ID_RANGE]	= "MSMONCFG_ID_RANGE",
220649aa621cSJames Morse 	[MPAM_ERRCODE_REQ_PMG_RANGE]		= "Req_PMG_Range",
220749aa621cSJames Morse 	[MPAM_ERRCODE_MONITOR_RANGE]		= "Monitor_Range",
220849aa621cSJames Morse 	[MPAM_ERRCODE_INTPARTID_RANGE]		= "intPARTID_Range",
220949aa621cSJames Morse 	[MPAM_ERRCODE_UNEXPECTED_INTERNAL]	= "Unexpected_INTERNAL",
221049aa621cSJames Morse 	[MPAM_ERRCODE_UNDEFINED_RIS_PART_SEL]	= "Undefined_RIS_PART_SEL",
221149aa621cSJames Morse 	[MPAM_ERRCODE_RIS_NO_CONTROL]		= "RIS_No_Control",
221249aa621cSJames Morse 	[MPAM_ERRCODE_UNDEFINED_RIS_MON_SEL]	= "Undefined_RIS_MON_SEL",
221349aa621cSJames Morse 	[MPAM_ERRCODE_RIS_NO_MONITOR]		= "RIS_No_Monitor",
221449aa621cSJames Morse 	[12 ... 15] = "Reserved"
221549aa621cSJames Morse };
221649aa621cSJames Morse 
221749aa621cSJames Morse static int mpam_enable_msc_ecr(void *_msc)
221849aa621cSJames Morse {
221949aa621cSJames Morse 	struct mpam_msc *msc = _msc;
222049aa621cSJames Morse 
222149aa621cSJames Morse 	__mpam_write_reg(msc, MPAMF_ECR, MPAMF_ECR_INTEN);
222249aa621cSJames Morse 
222349aa621cSJames Morse 	return 0;
222449aa621cSJames Morse }
222549aa621cSJames Morse 
222649aa621cSJames Morse /* This can run in mpam_disable(), and the interrupt handler on the same CPU */
222749aa621cSJames Morse static int mpam_disable_msc_ecr(void *_msc)
222849aa621cSJames Morse {
222949aa621cSJames Morse 	struct mpam_msc *msc = _msc;
223049aa621cSJames Morse 
223149aa621cSJames Morse 	__mpam_write_reg(msc, MPAMF_ECR, 0);
223249aa621cSJames Morse 
223349aa621cSJames Morse 	return 0;
223449aa621cSJames Morse }
223549aa621cSJames Morse 
223649aa621cSJames Morse static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
223749aa621cSJames Morse {
223849aa621cSJames Morse 	u64 reg;
223949aa621cSJames Morse 	u16 partid;
224049aa621cSJames Morse 	u8 errcode, pmg, ris;
224149aa621cSJames Morse 
224249aa621cSJames Morse 	if (WARN_ON_ONCE(!msc) ||
224349aa621cSJames Morse 	    WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(),
224449aa621cSJames Morse 					   &msc->accessibility)))
224549aa621cSJames Morse 		return IRQ_NONE;
224649aa621cSJames Morse 
224749aa621cSJames Morse 	reg = mpam_msc_read_esr(msc);
224849aa621cSJames Morse 
224949aa621cSJames Morse 	errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg);
225049aa621cSJames Morse 	if (!errcode)
225149aa621cSJames Morse 		return IRQ_NONE;
225249aa621cSJames Morse 
225349aa621cSJames Morse 	/* Clear level triggered irq */
225449aa621cSJames Morse 	mpam_msc_clear_esr(msc);
225549aa621cSJames Morse 
225649aa621cSJames Morse 	partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg);
225749aa621cSJames Morse 	pmg = FIELD_GET(MPAMF_ESR_PMG, reg);
225849aa621cSJames Morse 	ris = FIELD_GET(MPAMF_ESR_RIS, reg);
225949aa621cSJames Morse 
226049aa621cSJames Morse 	pr_err_ratelimited("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n",
226149aa621cSJames Morse 			   msc->id, mpam_errcode_names[errcode], partid, pmg,
226249aa621cSJames Morse 			   ris);
226349aa621cSJames Morse 
226449aa621cSJames Morse 	/* Disable this interrupt. */
226549aa621cSJames Morse 	mpam_disable_msc_ecr(msc);
226649aa621cSJames Morse 
22673796f75aSJames Morse 	/* Are we racing with the thread disabling MPAM? */
22683796f75aSJames Morse 	if (!mpam_is_enabled())
22693796f75aSJames Morse 		return IRQ_HANDLED;
22703796f75aSJames Morse 
227149aa621cSJames Morse 	/*
227249aa621cSJames Morse 	 * Schedule the teardown work. Don't use a threaded IRQ as we can't
227349aa621cSJames Morse 	 * unregister the interrupt from the threaded part of the handler.
227449aa621cSJames Morse 	 */
227549aa621cSJames Morse 	mpam_disable_reason = "hardware error interrupt";
227649aa621cSJames Morse 	schedule_work(&mpam_broken_work);
227749aa621cSJames Morse 
227849aa621cSJames Morse 	return IRQ_HANDLED;
227949aa621cSJames Morse }
228049aa621cSJames Morse 
228149aa621cSJames Morse static irqreturn_t mpam_ppi_handler(int irq, void *dev_id)
228249aa621cSJames Morse {
228349aa621cSJames Morse 	struct mpam_msc *msc = *(struct mpam_msc **)dev_id;
228449aa621cSJames Morse 
228549aa621cSJames Morse 	return __mpam_irq_handler(irq, msc);
228649aa621cSJames Morse }
228749aa621cSJames Morse 
228849aa621cSJames Morse static irqreturn_t mpam_spi_handler(int irq, void *dev_id)
228949aa621cSJames Morse {
229049aa621cSJames Morse 	struct mpam_msc *msc = dev_id;
229149aa621cSJames Morse 
229249aa621cSJames Morse 	return __mpam_irq_handler(irq, msc);
229349aa621cSJames Morse }
229449aa621cSJames Morse 
229549aa621cSJames Morse static int mpam_register_irqs(void)
229649aa621cSJames Morse {
229749aa621cSJames Morse 	int err, irq;
229849aa621cSJames Morse 	struct mpam_msc *msc;
229949aa621cSJames Morse 
230049aa621cSJames Morse 	lockdep_assert_cpus_held();
230149aa621cSJames Morse 
230249aa621cSJames Morse 	guard(srcu)(&mpam_srcu);
230349aa621cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
230449aa621cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
230549aa621cSJames Morse 		irq = platform_get_irq_byname_optional(msc->pdev, "error");
230649aa621cSJames Morse 		if (irq <= 0)
230749aa621cSJames Morse 			continue;
230849aa621cSJames Morse 
230949aa621cSJames Morse 		/* The MPAM spec says the interrupt can be SPI, PPI or LPI */
231049aa621cSJames Morse 		/* We anticipate sharing the interrupt with other MSCs */
231149aa621cSJames Morse 		if (irq_is_percpu(irq)) {
231249aa621cSJames Morse 			err = request_percpu_irq(irq, &mpam_ppi_handler,
231349aa621cSJames Morse 						 "mpam:msc:error",
231449aa621cSJames Morse 						 msc->error_dev_id);
231549aa621cSJames Morse 			if (err)
231649aa621cSJames Morse 				return err;
231749aa621cSJames Morse 
231849aa621cSJames Morse 			msc->reenable_error_ppi = irq;
231949aa621cSJames Morse 			smp_call_function_many(&msc->accessibility,
232049aa621cSJames Morse 					       &_enable_percpu_irq, &irq,
232149aa621cSJames Morse 					       true);
232249aa621cSJames Morse 		} else {
232349aa621cSJames Morse 			err = devm_request_irq(&msc->pdev->dev, irq,
232449aa621cSJames Morse 					       &mpam_spi_handler, IRQF_SHARED,
232549aa621cSJames Morse 					       "mpam:msc:error", msc);
232649aa621cSJames Morse 			if (err)
232749aa621cSJames Morse 				return err;
232849aa621cSJames Morse 		}
232949aa621cSJames Morse 
233049aa621cSJames Morse 		mutex_lock(&msc->error_irq_lock);
233149aa621cSJames Morse 		msc->error_irq_req = true;
233249aa621cSJames Morse 		mpam_touch_msc(msc, mpam_enable_msc_ecr, msc);
233349aa621cSJames Morse 		msc->error_irq_hw_enabled = true;
233449aa621cSJames Morse 		mutex_unlock(&msc->error_irq_lock);
233549aa621cSJames Morse 	}
233649aa621cSJames Morse 
233749aa621cSJames Morse 	return 0;
233849aa621cSJames Morse }
233949aa621cSJames Morse 
234049aa621cSJames Morse static void mpam_unregister_irqs(void)
234149aa621cSJames Morse {
234249aa621cSJames Morse 	int irq;
234349aa621cSJames Morse 	struct mpam_msc *msc;
234449aa621cSJames Morse 
234549aa621cSJames Morse 	guard(cpus_read_lock)();
234649aa621cSJames Morse 	guard(srcu)(&mpam_srcu);
234749aa621cSJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
234849aa621cSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
234949aa621cSJames Morse 		irq = platform_get_irq_byname_optional(msc->pdev, "error");
235049aa621cSJames Morse 		if (irq <= 0)
235149aa621cSJames Morse 			continue;
235249aa621cSJames Morse 
235349aa621cSJames Morse 		mutex_lock(&msc->error_irq_lock);
235449aa621cSJames Morse 		if (msc->error_irq_hw_enabled) {
235549aa621cSJames Morse 			mpam_touch_msc(msc, mpam_disable_msc_ecr, msc);
235649aa621cSJames Morse 			msc->error_irq_hw_enabled = false;
235749aa621cSJames Morse 		}
235849aa621cSJames Morse 
235949aa621cSJames Morse 		if (msc->error_irq_req) {
236049aa621cSJames Morse 			if (irq_is_percpu(irq)) {
236149aa621cSJames Morse 				msc->reenable_error_ppi = 0;
236249aa621cSJames Morse 				free_percpu_irq(irq, msc->error_dev_id);
236349aa621cSJames Morse 			} else {
236449aa621cSJames Morse 				devm_free_irq(&msc->pdev->dev, irq, msc);
236549aa621cSJames Morse 			}
236649aa621cSJames Morse 			msc->error_irq_req = false;
236749aa621cSJames Morse 		}
236849aa621cSJames Morse 		mutex_unlock(&msc->error_irq_lock);
236949aa621cSJames Morse 	}
237049aa621cSJames Morse }
237149aa621cSJames Morse 
237209b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp)
237309b89d2aSJames Morse {
237441e8a149SJames Morse 	struct mpam_msc *msc;
237541e8a149SJames Morse 	struct mpam_vmsc *vmsc;
237641e8a149SJames Morse 	struct mpam_msc_ris *ris;
237741e8a149SJames Morse 
237841e8a149SJames Morse 	lockdep_assert_held(&mpam_list_lock);
237941e8a149SJames Morse 
238009b89d2aSJames Morse 	add_to_garbage(comp->cfg);
238141e8a149SJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
238241e8a149SJames Morse 		msc = vmsc->msc;
238341e8a149SJames Morse 
238441e8a149SJames Morse 		if (mpam_mon_sel_lock(msc)) {
238541e8a149SJames Morse 			list_for_each_entry(ris, &vmsc->ris, vmsc_list)
238641e8a149SJames Morse 				add_to_garbage(ris->mbwu_state);
238741e8a149SJames Morse 			mpam_mon_sel_unlock(msc);
238841e8a149SJames Morse 		}
238941e8a149SJames Morse 	}
239009b89d2aSJames Morse }
239109b89d2aSJames Morse 
239209b89d2aSJames Morse static void mpam_reset_component_cfg(struct mpam_component *comp)
239309b89d2aSJames Morse {
239409b89d2aSJames Morse 	int i;
239509b89d2aSJames Morse 	struct mpam_props *cprops = &comp->class->props;
239609b89d2aSJames Morse 
239709b89d2aSJames Morse 	mpam_assert_partid_sizes_fixed();
239809b89d2aSJames Morse 
239909b89d2aSJames Morse 	if (!comp->cfg)
240009b89d2aSJames Morse 		return;
240109b89d2aSJames Morse 
240209b89d2aSJames Morse 	for (i = 0; i <= mpam_partid_max; i++) {
240309b89d2aSJames Morse 		comp->cfg[i] = (struct mpam_config) {};
240409b89d2aSJames Morse 		if (cprops->cpbm_wd)
240509b89d2aSJames Morse 			comp->cfg[i].cpbm = GENMASK(cprops->cpbm_wd - 1, 0);
240609b89d2aSJames Morse 		if (cprops->mbw_pbm_bits)
240709b89d2aSJames Morse 			comp->cfg[i].mbw_pbm = GENMASK(cprops->mbw_pbm_bits - 1, 0);
240809b89d2aSJames Morse 		if (cprops->bwa_wd)
240909b89d2aSJames Morse 			comp->cfg[i].mbw_max = GENMASK(15, 16 - cprops->bwa_wd);
241009b89d2aSJames Morse 	}
241109b89d2aSJames Morse }
241209b89d2aSJames Morse 
241309b89d2aSJames Morse static int __allocate_component_cfg(struct mpam_component *comp)
241409b89d2aSJames Morse {
241541e8a149SJames Morse 	struct mpam_vmsc *vmsc;
241641e8a149SJames Morse 
241709b89d2aSJames Morse 	mpam_assert_partid_sizes_fixed();
241809b89d2aSJames Morse 
241909b89d2aSJames Morse 	if (comp->cfg)
242009b89d2aSJames Morse 		return 0;
242109b89d2aSJames Morse 
242209b89d2aSJames Morse 	comp->cfg = kcalloc(mpam_partid_max + 1, sizeof(*comp->cfg), GFP_KERNEL);
242309b89d2aSJames Morse 	if (!comp->cfg)
242409b89d2aSJames Morse 		return -ENOMEM;
242509b89d2aSJames Morse 
242609b89d2aSJames Morse 	/*
242709b89d2aSJames Morse 	 * The array is free()d in one go, so only cfg[0]'s structure needs
242809b89d2aSJames Morse 	 * to be initialised.
242909b89d2aSJames Morse 	 */
243009b89d2aSJames Morse 	init_garbage(&comp->cfg[0].garbage);
243109b89d2aSJames Morse 
243209b89d2aSJames Morse 	mpam_reset_component_cfg(comp);
243309b89d2aSJames Morse 
243441e8a149SJames Morse 	list_for_each_entry(vmsc, &comp->vmsc, comp_list) {
243541e8a149SJames Morse 		struct mpam_msc *msc;
243641e8a149SJames Morse 		struct mpam_msc_ris *ris;
243741e8a149SJames Morse 		struct msmon_mbwu_state *mbwu_state;
243841e8a149SJames Morse 
243941e8a149SJames Morse 		if (!vmsc->props.num_mbwu_mon)
244041e8a149SJames Morse 			continue;
244141e8a149SJames Morse 
244241e8a149SJames Morse 		msc = vmsc->msc;
244341e8a149SJames Morse 		list_for_each_entry(ris, &vmsc->ris, vmsc_list) {
244441e8a149SJames Morse 			if (!ris->props.num_mbwu_mon)
244541e8a149SJames Morse 				continue;
244641e8a149SJames Morse 
244741e8a149SJames Morse 			mbwu_state = kcalloc(ris->props.num_mbwu_mon,
244841e8a149SJames Morse 					     sizeof(*ris->mbwu_state),
244941e8a149SJames Morse 					     GFP_KERNEL);
245041e8a149SJames Morse 			if (!mbwu_state) {
245141e8a149SJames Morse 				__destroy_component_cfg(comp);
245241e8a149SJames Morse 				return -ENOMEM;
245341e8a149SJames Morse 			}
245441e8a149SJames Morse 
245541e8a149SJames Morse 			init_garbage(&mbwu_state[0].garbage);
245641e8a149SJames Morse 
245741e8a149SJames Morse 			if (mpam_mon_sel_lock(msc)) {
245841e8a149SJames Morse 				ris->mbwu_state = mbwu_state;
245941e8a149SJames Morse 				mpam_mon_sel_unlock(msc);
246041e8a149SJames Morse 			}
246141e8a149SJames Morse 		}
246241e8a149SJames Morse 	}
246341e8a149SJames Morse 
246409b89d2aSJames Morse 	return 0;
246509b89d2aSJames Morse }
246609b89d2aSJames Morse 
246709b89d2aSJames Morse static int mpam_allocate_config(void)
246809b89d2aSJames Morse {
246909b89d2aSJames Morse 	struct mpam_class *class;
247009b89d2aSJames Morse 	struct mpam_component *comp;
247109b89d2aSJames Morse 
247209b89d2aSJames Morse 	lockdep_assert_held(&mpam_list_lock);
247309b89d2aSJames Morse 
247409b89d2aSJames Morse 	list_for_each_entry(class, &mpam_classes, classes_list) {
247509b89d2aSJames Morse 		list_for_each_entry(comp, &class->components, class_list) {
247609b89d2aSJames Morse 			int err = __allocate_component_cfg(comp);
247709b89d2aSJames Morse 			if (err)
247809b89d2aSJames Morse 				return err;
247909b89d2aSJames Morse 		}
248009b89d2aSJames Morse 	}
248109b89d2aSJames Morse 
248209b89d2aSJames Morse 	return 0;
248309b89d2aSJames Morse }
248409b89d2aSJames Morse 
24858f8d0ac1SJames Morse static void mpam_enable_once(void)
24868f8d0ac1SJames Morse {
248749aa621cSJames Morse 	int err;
248849aa621cSJames Morse 
2489bd221f9fSJames Morse 	/*
2490bd221f9fSJames Morse 	 * Once the cpuhp callbacks have been changed, mpam_partid_max can no
2491bd221f9fSJames Morse 	 * longer change.
2492bd221f9fSJames Morse 	 */
2493bd221f9fSJames Morse 	spin_lock(&partid_max_lock);
2494bd221f9fSJames Morse 	partid_max_published = true;
2495bd221f9fSJames Morse 	spin_unlock(&partid_max_lock);
2496bd221f9fSJames Morse 
249749aa621cSJames Morse 	/*
249849aa621cSJames Morse 	 * If all the MSC have been probed, enabling the IRQs happens next.
249949aa621cSJames Morse 	 * That involves cross-calling to a CPU that can reach the MSC, and
250049aa621cSJames Morse 	 * the locks must be taken in this order:
250149aa621cSJames Morse 	 */
250249aa621cSJames Morse 	cpus_read_lock();
2503c10ca83aSJames Morse 	mutex_lock(&mpam_list_lock);
250409b89d2aSJames Morse 	do {
2505c10ca83aSJames Morse 		mpam_enable_merge_features(&mpam_classes);
250649aa621cSJames Morse 
250749aa621cSJames Morse 		err = mpam_register_irqs();
250809b89d2aSJames Morse 		if (err) {
250909b89d2aSJames Morse 			pr_warn("Failed to register irqs: %d\n", err);
251009b89d2aSJames Morse 			break;
251109b89d2aSJames Morse 		}
251249aa621cSJames Morse 
251309b89d2aSJames Morse 		err = mpam_allocate_config();
251409b89d2aSJames Morse 		if (err) {
251509b89d2aSJames Morse 			pr_err("Failed to allocate configuration arrays.\n");
251609b89d2aSJames Morse 			break;
251709b89d2aSJames Morse 		}
251809b89d2aSJames Morse 	} while (0);
2519c10ca83aSJames Morse 	mutex_unlock(&mpam_list_lock);
252049aa621cSJames Morse 	cpus_read_unlock();
252149aa621cSJames Morse 
252249aa621cSJames Morse 	if (err) {
252349aa621cSJames Morse 		mpam_disable_reason = "Failed to enable.";
252449aa621cSJames Morse 		schedule_work(&mpam_broken_work);
252549aa621cSJames Morse 		return;
252649aa621cSJames Morse 	}
2527c10ca83aSJames Morse 
25283796f75aSJames Morse 	static_branch_enable(&mpam_enabled);
25298f8d0ac1SJames Morse 	mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline,
25308f8d0ac1SJames Morse 				      "mpam:online");
25318f8d0ac1SJames Morse 
2532bd221f9fSJames Morse 	/* Use printk() to avoid the pr_fmt adding the function name. */
2533bd221f9fSJames Morse 	printk(KERN_INFO "MPAM enabled with %u PARTIDs and %u PMGs\n",
2534bd221f9fSJames Morse 	       mpam_partid_max + 1, mpam_pmg_max + 1);
25358f8d0ac1SJames Morse }
25368f8d0ac1SJames Morse 
25373bd04fe7SJames Morse static void mpam_reset_component_locked(struct mpam_component *comp)
25383bd04fe7SJames Morse {
25393bd04fe7SJames Morse 	struct mpam_vmsc *vmsc;
25403bd04fe7SJames Morse 
25413bd04fe7SJames Morse 	lockdep_assert_cpus_held();
254209b89d2aSJames Morse 	mpam_assert_partid_sizes_fixed();
254309b89d2aSJames Morse 
254409b89d2aSJames Morse 	mpam_reset_component_cfg(comp);
25453bd04fe7SJames Morse 
25463bd04fe7SJames Morse 	guard(srcu)(&mpam_srcu);
25473bd04fe7SJames Morse 	list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list,
25483bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
25493bd04fe7SJames Morse 		struct mpam_msc *msc = vmsc->msc;
25503bd04fe7SJames Morse 		struct mpam_msc_ris *ris;
25513bd04fe7SJames Morse 
25523bd04fe7SJames Morse 		list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
25533bd04fe7SJames Morse 					 srcu_read_lock_held(&mpam_srcu)) {
25543bd04fe7SJames Morse 			if (!ris->in_reset_state)
25553bd04fe7SJames Morse 				mpam_touch_msc(msc, mpam_reset_ris, ris);
25563bd04fe7SJames Morse 			ris->in_reset_state = true;
25573bd04fe7SJames Morse 		}
25583bd04fe7SJames Morse 	}
25593bd04fe7SJames Morse }
25603bd04fe7SJames Morse 
25613bd04fe7SJames Morse static void mpam_reset_class_locked(struct mpam_class *class)
25623bd04fe7SJames Morse {
25633bd04fe7SJames Morse 	struct mpam_component *comp;
25643bd04fe7SJames Morse 
25653bd04fe7SJames Morse 	lockdep_assert_cpus_held();
25663bd04fe7SJames Morse 
25673bd04fe7SJames Morse 	guard(srcu)(&mpam_srcu);
25683bd04fe7SJames Morse 	list_for_each_entry_srcu(comp, &class->components, class_list,
25693bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu))
25703bd04fe7SJames Morse 		mpam_reset_component_locked(comp);
25713bd04fe7SJames Morse }
25723bd04fe7SJames Morse 
25733bd04fe7SJames Morse static void mpam_reset_class(struct mpam_class *class)
25743bd04fe7SJames Morse {
25753bd04fe7SJames Morse 	cpus_read_lock();
25763bd04fe7SJames Morse 	mpam_reset_class_locked(class);
25773bd04fe7SJames Morse 	cpus_read_unlock();
25783bd04fe7SJames Morse }
25793bd04fe7SJames Morse 
25803bd04fe7SJames Morse /*
25813bd04fe7SJames Morse  * Called in response to an error IRQ.
25823bd04fe7SJames Morse  * All of MPAMs errors indicate a software bug, restore any modified
25833bd04fe7SJames Morse  * controls to their reset values.
25843bd04fe7SJames Morse  */
25858f8d0ac1SJames Morse void mpam_disable(struct work_struct *ignored)
25868f8d0ac1SJames Morse {
25873bd04fe7SJames Morse 	int idx;
25883bd04fe7SJames Morse 	struct mpam_class *class;
25898f8d0ac1SJames Morse 	struct mpam_msc *msc, *tmp;
25908f8d0ac1SJames Morse 
25918f8d0ac1SJames Morse 	mutex_lock(&mpam_cpuhp_state_lock);
25928f8d0ac1SJames Morse 	if (mpam_cpuhp_state) {
25938f8d0ac1SJames Morse 		cpuhp_remove_state(mpam_cpuhp_state);
25948f8d0ac1SJames Morse 		mpam_cpuhp_state = 0;
25958f8d0ac1SJames Morse 	}
25968f8d0ac1SJames Morse 	mutex_unlock(&mpam_cpuhp_state_lock);
25978f8d0ac1SJames Morse 
25983796f75aSJames Morse 	static_branch_disable(&mpam_enabled);
25993796f75aSJames Morse 
260049aa621cSJames Morse 	mpam_unregister_irqs();
260149aa621cSJames Morse 
26023bd04fe7SJames Morse 	idx = srcu_read_lock(&mpam_srcu);
26033bd04fe7SJames Morse 	list_for_each_entry_srcu(class, &mpam_classes, classes_list,
26043bd04fe7SJames Morse 				 srcu_read_lock_held(&mpam_srcu))
26053bd04fe7SJames Morse 		mpam_reset_class(class);
26063bd04fe7SJames Morse 	srcu_read_unlock(&mpam_srcu, idx);
26073bd04fe7SJames Morse 
26088f8d0ac1SJames Morse 	mutex_lock(&mpam_list_lock);
26098f8d0ac1SJames Morse 	list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list)
26108f8d0ac1SJames Morse 		mpam_msc_destroy(msc);
26118f8d0ac1SJames Morse 	mutex_unlock(&mpam_list_lock);
26128f8d0ac1SJames Morse 	mpam_free_garbage();
26138f8d0ac1SJames Morse 
26148f8d0ac1SJames Morse 	pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason);
26158f8d0ac1SJames Morse }
26168f8d0ac1SJames Morse 
26178f8d0ac1SJames Morse /*
26188f8d0ac1SJames Morse  * Enable mpam once all devices have been probed.
26198f8d0ac1SJames Morse  * Scheduled by mpam_discovery_cpu_online() once all devices have been created.
26208f8d0ac1SJames Morse  * Also scheduled when new devices are probed when new CPUs come online.
26218f8d0ac1SJames Morse  */
26228f8d0ac1SJames Morse void mpam_enable(struct work_struct *work)
26238f8d0ac1SJames Morse {
26248f8d0ac1SJames Morse 	static atomic_t once;
26258f8d0ac1SJames Morse 	struct mpam_msc *msc;
26268f8d0ac1SJames Morse 	bool all_devices_probed = true;
26278f8d0ac1SJames Morse 
26288f8d0ac1SJames Morse 	/* Have we probed all the hw devices? */
26298f8d0ac1SJames Morse 	guard(srcu)(&mpam_srcu);
26308f8d0ac1SJames Morse 	list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list,
26318f8d0ac1SJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
26328f8d0ac1SJames Morse 		mutex_lock(&msc->probe_lock);
26338f8d0ac1SJames Morse 		if (!msc->probed)
26348f8d0ac1SJames Morse 			all_devices_probed = false;
26358f8d0ac1SJames Morse 		mutex_unlock(&msc->probe_lock);
26368f8d0ac1SJames Morse 
26378f8d0ac1SJames Morse 		if (!all_devices_probed)
26388f8d0ac1SJames Morse 			break;
26398f8d0ac1SJames Morse 	}
26408f8d0ac1SJames Morse 
26418f8d0ac1SJames Morse 	if (all_devices_probed && !atomic_fetch_inc(&once))
26428f8d0ac1SJames Morse 		mpam_enable_once();
26438f8d0ac1SJames Morse }
26448f8d0ac1SJames Morse 
264509b89d2aSJames Morse #define maybe_update_config(cfg, feature, newcfg, member, changes) do { \
264609b89d2aSJames Morse 	if (mpam_has_feature(feature, newcfg) &&			\
264709b89d2aSJames Morse 	    (newcfg)->member != (cfg)->member) {			\
264809b89d2aSJames Morse 		(cfg)->member = (newcfg)->member;			\
264909b89d2aSJames Morse 		mpam_set_feature(feature, cfg);				\
265009b89d2aSJames Morse 									\
265109b89d2aSJames Morse 		(changes) = true;					\
265209b89d2aSJames Morse 	}								\
265309b89d2aSJames Morse } while (0)
265409b89d2aSJames Morse 
265509b89d2aSJames Morse static bool mpam_update_config(struct mpam_config *cfg,
265609b89d2aSJames Morse 			       const struct mpam_config *newcfg)
265709b89d2aSJames Morse {
265809b89d2aSJames Morse 	bool has_changes = false;
265909b89d2aSJames Morse 
266009b89d2aSJames Morse 	maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, has_changes);
266109b89d2aSJames Morse 	maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, has_changes);
266209b89d2aSJames Morse 	maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, has_changes);
266309b89d2aSJames Morse 
266409b89d2aSJames Morse 	return has_changes;
266509b89d2aSJames Morse }
266609b89d2aSJames Morse 
266709b89d2aSJames Morse int mpam_apply_config(struct mpam_component *comp, u16 partid,
266809b89d2aSJames Morse 		      struct mpam_config *cfg)
266909b89d2aSJames Morse {
267009b89d2aSJames Morse 	struct mpam_write_config_arg arg;
267109b89d2aSJames Morse 	struct mpam_msc_ris *ris;
267209b89d2aSJames Morse 	struct mpam_vmsc *vmsc;
267309b89d2aSJames Morse 	struct mpam_msc *msc;
267409b89d2aSJames Morse 
267509b89d2aSJames Morse 	lockdep_assert_cpus_held();
267609b89d2aSJames Morse 
267709b89d2aSJames Morse 	/* Don't pass in the current config! */
267809b89d2aSJames Morse 	WARN_ON_ONCE(&comp->cfg[partid] == cfg);
267909b89d2aSJames Morse 
268009b89d2aSJames Morse 	if (!mpam_update_config(&comp->cfg[partid], cfg))
268109b89d2aSJames Morse 		return 0;
268209b89d2aSJames Morse 
268309b89d2aSJames Morse 	arg.comp = comp;
268409b89d2aSJames Morse 	arg.partid = partid;
268509b89d2aSJames Morse 
268609b89d2aSJames Morse 	guard(srcu)(&mpam_srcu);
268709b89d2aSJames Morse 	list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list,
268809b89d2aSJames Morse 				 srcu_read_lock_held(&mpam_srcu)) {
268909b89d2aSJames Morse 		msc = vmsc->msc;
269009b89d2aSJames Morse 
269109b89d2aSJames Morse 		mutex_lock(&msc->cfg_lock);
269209b89d2aSJames Morse 		list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
269309b89d2aSJames Morse 					 srcu_read_lock_held(&mpam_srcu)) {
269409b89d2aSJames Morse 			arg.ris = ris;
269509b89d2aSJames Morse 			mpam_touch_msc(msc, __write_config, &arg);
269609b89d2aSJames Morse 		}
269709b89d2aSJames Morse 		mutex_unlock(&msc->cfg_lock);
269809b89d2aSJames Morse 	}
269909b89d2aSJames Morse 
270009b89d2aSJames Morse 	return 0;
270109b89d2aSJames Morse }
270209b89d2aSJames Morse 
2703f04046f2SJames Morse static int __init mpam_msc_driver_init(void)
2704f04046f2SJames Morse {
2705f04046f2SJames Morse 	if (!system_supports_mpam())
2706f04046f2SJames Morse 		return -EOPNOTSUPP;
2707f04046f2SJames Morse 
2708f04046f2SJames Morse 	init_srcu_struct(&mpam_srcu);
2709f04046f2SJames Morse 
2710f04046f2SJames Morse 	fw_num_msc = acpi_mpam_count_msc();
2711f04046f2SJames Morse 	if (fw_num_msc <= 0) {
2712f04046f2SJames Morse 		pr_err("No MSC devices found in firmware\n");
2713f04046f2SJames Morse 		return -EINVAL;
2714f04046f2SJames Morse 	}
2715f04046f2SJames Morse 
2716f04046f2SJames Morse 	return platform_driver_register(&mpam_msc_driver);
2717f04046f2SJames Morse }
2718bd221f9fSJames Morse 
2719bd221f9fSJames Morse /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */
2720f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init);
2721e3565d1fSJames Morse 
2722e3565d1fSJames Morse #ifdef CONFIG_MPAM_KUNIT_TEST
2723e3565d1fSJames Morse #include "test_mpam_devices.c"
2724e3565d1fSJames Morse #endif
2725