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