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
init_garbage(struct mpam_garbage * garbage)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
mpam_free_garbage(void)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 */
mpam_assert_partid_sizes_fixed(void)15309b89d2aSJames Morse static void mpam_assert_partid_sizes_fixed(void)
15409b89d2aSJames Morse {
15509b89d2aSJames Morse WARN_ON_ONCE(!partid_max_published);
15609b89d2aSJames Morse }
15709b89d2aSJames Morse
__mpam_read_reg(struct mpam_msc * msc,u16 reg)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
_mpam_read_partsel_reg(struct mpam_msc * msc,u16 reg)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
__mpam_write_reg(struct mpam_msc * msc,u16 reg,u32 val)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
_mpam_write_partsel_reg(struct mpam_msc * msc,u16 reg,u32 val)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
_mpam_read_monsel_reg(struct mpam_msc * msc,u16 reg)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
_mpam_write_monsel_reg(struct mpam_msc * msc,u16 reg,u32 val)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
mpam_msc_read_idr(struct mpam_msc * msc)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
mpam_msc_clear_esr(struct mpam_msc * msc)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
mpam_msc_read_esr(struct mpam_msc * msc)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
__mpam_part_sel_raw(u32 partsel,struct mpam_msc * msc)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
__mpam_part_sel(u8 ris_idx,u16 partid,struct mpam_msc * msc)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
__mpam_intpart_sel(u8 ris_idx,u16 intpartid,struct mpam_msc * msc)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
mpam_register_requestor(u16 partid_max,u8 pmg_max)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 *
mpam_class_alloc(u8 level_idx,enum mpam_class_types type)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
mpam_class_destroy(struct mpam_class * class)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 *
mpam_class_find(u8 level_idx,enum mpam_class_types type)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 *
mpam_component_alloc(struct mpam_class * class,int id)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
mpam_component_destroy(struct mpam_component * comp)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 *
mpam_component_find(struct mpam_class * class,int id)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 *
mpam_vmsc_alloc(struct mpam_component * comp,struct mpam_msc * msc)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
mpam_vmsc_destroy(struct mpam_vmsc * vmsc)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 *
mpam_vmsc_find(struct mpam_component * comp,struct mpam_msc * msc)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 */
mpam_get_cpumask_from_cache_id(unsigned long cache_id,u32 cache_level,cpumask_t * affinity)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 */
get_cpumask_from_node_id(u32 node_id,cpumask_t * affinity)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
mpam_ris_get_affinity(struct mpam_msc * msc,cpumask_t * affinity,enum mpam_class_types type,struct mpam_class * class,struct mpam_component * comp)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
mpam_ris_create_locked(struct mpam_msc * msc,u8 ris_idx,enum mpam_class_types type,u8 class_id,int component_id)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
mpam_ris_destroy(struct mpam_msc_ris * ris)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
mpam_ris_create(struct mpam_msc * msc,u8 ris_idx,enum mpam_class_types type,u8 class_id,int component_id)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
mpam_get_or_create_ris(struct mpam_msc * msc,u8 ris_idx)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 */
_mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris * ris,u32 mon_reg)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
mpam_ris_hw_probe(struct mpam_msc_ris * ris)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
mpam_msc_hw_probe(struct mpam_msc * msc)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
mpam_ris_has_mbwu_long_counter(struct mpam_msc_ris * ris)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
mpam_msc_read_mbwu_l(struct mpam_msc * msc)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
mpam_msc_zero_mbwu_l(struct mpam_msc * msc)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
gen_msmon_ctl_flt_vals(struct mon_read * m,u32 * ctl_val,u32 * flt_val)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
read_msmon_ctl_flt_vals(struct mon_read * m,u32 * ctl_val,u32 * flt_val)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. */
clean_msmon_ctl_val(u32 * cur_ctl)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
write_msmon_ctl_flt_vals(struct mon_read * m,u32 ctl_val,u32 flt_val)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
mpam_msmon_overflow_val(enum mpam_device_features type)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
__ris_msmon_read(void * arg)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
_msmon_read(struct mpam_component * comp,struct mon_read * arg)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
mpam_msmon_choose_counter(struct mpam_class * class)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
mpam_msmon_read(struct mpam_component * comp,struct mon_cfg * ctx,enum mpam_device_features type,u64 * val)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
mpam_msmon_reset_mbwu(struct mpam_component * comp,struct mon_cfg * ctx)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
mpam_reset_msc_bitmap(struct mpam_msc * msc,u16 reg,u16 wd)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 */
mpam_reprogram_ris_partid(struct mpam_msc_ris * ris,u16 partid,struct mpam_config * cfg)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 */
mpam_restore_mbwu_state(void * _ris)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 */
mpam_save_mbwu_state(void * arg)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
mpam_init_reset_cfg(struct mpam_config * reset_cfg)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 */
mpam_reset_ris(void * arg)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 */
mpam_get_msc_preferred_cpu(struct mpam_msc * msc)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
mpam_touch_msc(struct mpam_msc * msc,int (* fn)(void * a),void * arg)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
__write_config(void * arg)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
mpam_reprogram_msc(struct mpam_msc * msc)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
_enable_percpu_irq(void * _irq)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
mpam_cpu_online(unsigned int cpu)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 */
mpam_discovery_cpu_online(unsigned int cpu)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
mpam_cpu_offline(unsigned int cpu)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
mpam_register_cpuhp_callbacks(int (* online)(unsigned int online),int (* offline)(unsigned int offline),char * name)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
__setup_ppi(struct mpam_msc * msc)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
mpam_msc_setup_error_irq(struct mpam_msc * msc)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 */
update_msc_accessibility(struct mpam_msc * msc)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 */
mpam_msc_destroy(struct mpam_msc * msc)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
mpam_msc_drv_remove(struct platform_device * pdev)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
do_mpam_msc_drv_probe(struct platform_device * pdev)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
mpam_msc_drv_probe(struct platform_device * pdev)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. */
mpam_has_bwa_wd_feature(struct mpam_props * props)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. */
mpam_has_cmax_wd_feature(struct mpam_props * props)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 */
__props_mismatch(struct mpam_props * parent,struct mpam_props * child,bool alias)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
__class_props_mismatch(struct mpam_class * class,struct mpam_vmsc * vmsc)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
__vmsc_props_mismatch(struct mpam_vmsc * vmsc,struct mpam_msc_ris * ris)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 */
mpam_enable_init_class_features(struct mpam_class * class)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
mpam_enable_merge_vmsc_features(struct mpam_component * comp)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
mpam_enable_merge_class_features(struct mpam_component * comp)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 */
mpam_enable_merge_features(struct list_head * all_classes_list)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
mpam_enable_msc_ecr(void * _msc)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 */
mpam_disable_msc_ecr(void * _msc)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
__mpam_irq_handler(int irq,struct mpam_msc * msc)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
mpam_ppi_handler(int irq,void * dev_id)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
mpam_spi_handler(int irq,void * dev_id)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
mpam_register_irqs(void)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
mpam_unregister_irqs(void)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
__destroy_component_cfg(struct mpam_component * comp)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
mpam_reset_component_cfg(struct mpam_component * comp)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
__allocate_component_cfg(struct mpam_component * comp)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
mpam_allocate_config(void)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
mpam_enable_once(void)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
mpam_reset_component_locked(struct mpam_component * comp)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
mpam_reset_class_locked(struct mpam_class * class)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
mpam_reset_class(struct mpam_class * class)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 */
mpam_disable(struct work_struct * ignored)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 */
mpam_enable(struct work_struct * work)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
mpam_update_config(struct mpam_config * cfg,const struct mpam_config * newcfg)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
mpam_apply_config(struct mpam_component * comp,u16 partid,struct mpam_config * cfg)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
mpam_msc_driver_init(void)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