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> 10f04046f2SJames Morse #include <linux/cacheinfo.h> 118f8d0ac1SJames Morse #include <linux/cpu.h> 12f04046f2SJames Morse #include <linux/cpumask.h> 13f04046f2SJames Morse #include <linux/device.h> 14f04046f2SJames Morse #include <linux/errno.h> 15f04046f2SJames Morse #include <linux/gfp.h> 16f04046f2SJames Morse #include <linux/list.h> 17f04046f2SJames Morse #include <linux/lockdep.h> 18f04046f2SJames Morse #include <linux/mutex.h> 19f04046f2SJames Morse #include <linux/platform_device.h> 20f04046f2SJames Morse #include <linux/printk.h> 21f04046f2SJames Morse #include <linux/srcu.h> 22d02beb06SJames Morse #include <linux/spinlock.h> 23f04046f2SJames Morse #include <linux/types.h> 248f8d0ac1SJames Morse #include <linux/workqueue.h> 25f04046f2SJames Morse 26f04046f2SJames Morse #include "mpam_internal.h" 27f04046f2SJames Morse 28f04046f2SJames Morse /* 29f04046f2SJames Morse * mpam_list_lock protects the SRCU lists when writing. Once the 30f04046f2SJames Morse * mpam_enabled key is enabled these lists are read-only, 31f04046f2SJames Morse * unless the error interrupt disables the driver. 32f04046f2SJames Morse */ 33f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock); 34f04046f2SJames Morse static LIST_HEAD(mpam_all_msc); 35f04046f2SJames Morse 36f04046f2SJames Morse struct srcu_struct mpam_srcu; 37f04046f2SJames Morse 38f04046f2SJames Morse /* 39f04046f2SJames Morse * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 40f04046f2SJames Morse * can be enabled. 41f04046f2SJames Morse */ 42f04046f2SJames Morse static atomic_t mpam_num_msc; 43f04046f2SJames Morse 448f8d0ac1SJames Morse static int mpam_cpuhp_state; 458f8d0ac1SJames Morse static DEFINE_MUTEX(mpam_cpuhp_state_lock); 468f8d0ac1SJames Morse 478f8d0ac1SJames Morse /* 48bd221f9fSJames Morse * The smallest common values for any CPU or MSC in the system. 49bd221f9fSJames Morse * Generating traffic outside this range will result in screaming interrupts. 50bd221f9fSJames Morse */ 51bd221f9fSJames Morse u16 mpam_partid_max; 52bd221f9fSJames Morse u8 mpam_pmg_max; 53bd221f9fSJames Morse static bool partid_max_init, partid_max_published; 54bd221f9fSJames Morse static DEFINE_SPINLOCK(partid_max_lock); 55bd221f9fSJames Morse 56bd221f9fSJames Morse /* 578f8d0ac1SJames Morse * mpam is enabled once all devices have been probed from CPU online callbacks, 588f8d0ac1SJames Morse * scheduled via this work_struct. If access to an MSC depends on a CPU that 598f8d0ac1SJames Morse * was not brought online at boot, this can happen surprisingly late. 608f8d0ac1SJames Morse */ 618f8d0ac1SJames Morse static DECLARE_WORK(mpam_enable_work, &mpam_enable); 628f8d0ac1SJames Morse 638f8d0ac1SJames Morse /* 648f8d0ac1SJames Morse * All mpam error interrupts indicate a software bug. On receipt, disable the 658f8d0ac1SJames Morse * driver. 668f8d0ac1SJames Morse */ 678f8d0ac1SJames Morse static DECLARE_WORK(mpam_broken_work, &mpam_disable); 688f8d0ac1SJames Morse 698f8d0ac1SJames Morse /* When mpam is disabled, the printed reason to aid debugging */ 708f8d0ac1SJames Morse static char *mpam_disable_reason; 718f8d0ac1SJames Morse 72f04046f2SJames Morse /* 7301fb4b82SJames Morse * An MSC is a physical container for controls and monitors, each identified by 7401fb4b82SJames Morse * their RIS index. These share a base-address, interrupts and some MMIO 7501fb4b82SJames Morse * registers. A vMSC is a virtual container for RIS in an MSC that control or 7601fb4b82SJames Morse * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but 7701fb4b82SJames Morse * not all RIS in an MSC share a vMSC. 7801fb4b82SJames Morse * 7901fb4b82SJames Morse * Components are a group of vMSC that control or monitor the same thing but 8001fb4b82SJames Morse * are from different MSC, so have different base-address, interrupts etc. 8101fb4b82SJames Morse * Classes are the set components of the same type. 8201fb4b82SJames Morse * 8301fb4b82SJames Morse * The features of a vMSC is the union of the RIS it contains. 8401fb4b82SJames Morse * The features of a Class and Component are the common subset of the vMSC 8501fb4b82SJames Morse * they contain. 8601fb4b82SJames Morse * 8701fb4b82SJames Morse * e.g. The system cache may have bandwidth controls on multiple interfaces, 8801fb4b82SJames Morse * for regulating traffic from devices independently of traffic from CPUs. 8901fb4b82SJames Morse * If these are two RIS in one MSC, they will be treated as controlling 9001fb4b82SJames Morse * different things, and will not share a vMSC/component/class. 9101fb4b82SJames Morse * 9201fb4b82SJames Morse * e.g. The L2 may have one MSC and two RIS, one for cache-controls another 9301fb4b82SJames Morse * for bandwidth. These two RIS are members of the same vMSC. 9401fb4b82SJames Morse * 9501fb4b82SJames Morse * e.g. The set of RIS that make up the L2 are grouped as a component. These 9601fb4b82SJames Morse * are sometimes termed slices. They should be configured the same, as if there 9701fb4b82SJames Morse * were only one. 9801fb4b82SJames Morse * 9901fb4b82SJames Morse * e.g. The SoC probably has more than one L2, each attached to a distinct set 10001fb4b82SJames Morse * of CPUs. All the L2 components are grouped as a class. 10101fb4b82SJames Morse * 10201fb4b82SJames Morse * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, 10301fb4b82SJames Morse * then linked via struct mpam_ris to a vmsc, component and class. 10401fb4b82SJames Morse * The same MSC may exist under different class->component->vmsc paths, but the 10501fb4b82SJames Morse * RIS index will be unique. 10601fb4b82SJames Morse */ 10701fb4b82SJames Morse LIST_HEAD(mpam_classes); 10801fb4b82SJames Morse 10901fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */ 11001fb4b82SJames Morse static LLIST_HEAD(mpam_garbage); 11101fb4b82SJames Morse 11201fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage) 11301fb4b82SJames Morse { 11401fb4b82SJames Morse init_llist_node(&garbage->llist); 11501fb4b82SJames Morse } 11601fb4b82SJames Morse 11701fb4b82SJames Morse #define add_to_garbage(x) \ 11801fb4b82SJames Morse do { \ 11901fb4b82SJames Morse __typeof__(x) _x = (x); \ 12001fb4b82SJames Morse _x->garbage.to_free = _x; \ 12101fb4b82SJames Morse llist_add(&_x->garbage.llist, &mpam_garbage); \ 12201fb4b82SJames Morse } while (0) 12301fb4b82SJames Morse 12401fb4b82SJames Morse static void mpam_free_garbage(void) 12501fb4b82SJames Morse { 12601fb4b82SJames Morse struct mpam_garbage *iter, *tmp; 12701fb4b82SJames Morse struct llist_node *to_free = llist_del_all(&mpam_garbage); 12801fb4b82SJames Morse 12901fb4b82SJames Morse if (!to_free) 13001fb4b82SJames Morse return; 13101fb4b82SJames Morse 13201fb4b82SJames Morse synchronize_srcu(&mpam_srcu); 13301fb4b82SJames Morse 13401fb4b82SJames Morse llist_for_each_entry_safe(iter, tmp, to_free, llist) { 13501fb4b82SJames Morse if (iter->pdev) 13601fb4b82SJames Morse devm_kfree(&iter->pdev->dev, iter->to_free); 13701fb4b82SJames Morse else 13801fb4b82SJames Morse kfree(iter->to_free); 13901fb4b82SJames Morse } 14001fb4b82SJames Morse } 14101fb4b82SJames Morse 1428f8d0ac1SJames Morse static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) 1438f8d0ac1SJames Morse { 1448f8d0ac1SJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 1458f8d0ac1SJames Morse 1468f8d0ac1SJames Morse return readl_relaxed(msc->mapped_hwpage + reg); 1478f8d0ac1SJames Morse } 1488f8d0ac1SJames Morse 1498f8d0ac1SJames Morse static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) 1508f8d0ac1SJames Morse { 1518f8d0ac1SJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 1528f8d0ac1SJames Morse return __mpam_read_reg(msc, reg); 1538f8d0ac1SJames Morse } 1548f8d0ac1SJames Morse 1558f8d0ac1SJames Morse #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) 1568f8d0ac1SJames Morse 157bd221f9fSJames Morse static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) 158bd221f9fSJames Morse { 159bd221f9fSJames Morse WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); 160bd221f9fSJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 161bd221f9fSJames Morse 162bd221f9fSJames Morse writel_relaxed(val, msc->mapped_hwpage + reg); 163bd221f9fSJames Morse } 164bd221f9fSJames Morse 165bd221f9fSJames Morse static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 166bd221f9fSJames Morse { 167bd221f9fSJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 168bd221f9fSJames Morse __mpam_write_reg(msc, reg, val); 169bd221f9fSJames Morse } 170bd221f9fSJames Morse 171bd221f9fSJames Morse #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val) 172bd221f9fSJames Morse 1738c90dc68SJames Morse static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) 1748c90dc68SJames Morse { 1758c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 1768c90dc68SJames Morse return __mpam_read_reg(msc, reg); 1778c90dc68SJames Morse } 1788c90dc68SJames Morse 1798c90dc68SJames Morse #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg) 1808c90dc68SJames Morse 1818c90dc68SJames Morse static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 1828c90dc68SJames Morse { 1838c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 1848c90dc68SJames Morse __mpam_write_reg(msc, reg, val); 1858c90dc68SJames Morse } 1868c90dc68SJames Morse 1878c90dc68SJames Morse #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) 1888c90dc68SJames Morse 189bd221f9fSJames Morse static u64 mpam_msc_read_idr(struct mpam_msc *msc) 190bd221f9fSJames Morse { 191bd221f9fSJames Morse u64 idr_high = 0, idr_low; 192bd221f9fSJames Morse 193bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 194bd221f9fSJames Morse 195bd221f9fSJames Morse idr_low = mpam_read_partsel_reg(msc, IDR); 196bd221f9fSJames Morse if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) 197bd221f9fSJames Morse idr_high = mpam_read_partsel_reg(msc, IDR + 4); 198bd221f9fSJames Morse 199bd221f9fSJames Morse return (idr_high << 32) | idr_low; 200bd221f9fSJames Morse } 201bd221f9fSJames Morse 202bd221f9fSJames Morse static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) 203bd221f9fSJames Morse { 204bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 205bd221f9fSJames Morse 206bd221f9fSJames Morse mpam_write_partsel_reg(msc, PART_SEL, partsel); 207bd221f9fSJames Morse } 208bd221f9fSJames Morse 209bd221f9fSJames Morse static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) 210bd221f9fSJames Morse { 211bd221f9fSJames Morse u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | 212bd221f9fSJames Morse FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); 213bd221f9fSJames Morse 214bd221f9fSJames Morse __mpam_part_sel_raw(partsel, msc); 215bd221f9fSJames Morse } 216bd221f9fSJames Morse 217bd221f9fSJames Morse int mpam_register_requestor(u16 partid_max, u8 pmg_max) 218bd221f9fSJames Morse { 219bd221f9fSJames Morse guard(spinlock)(&partid_max_lock); 220bd221f9fSJames Morse if (!partid_max_init) { 221bd221f9fSJames Morse mpam_partid_max = partid_max; 222bd221f9fSJames Morse mpam_pmg_max = pmg_max; 223bd221f9fSJames Morse partid_max_init = true; 224bd221f9fSJames Morse } else if (!partid_max_published) { 225bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, partid_max); 226bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, pmg_max); 227bd221f9fSJames Morse } else { 228bd221f9fSJames Morse /* New requestors can't lower the values */ 229bd221f9fSJames Morse if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) 230bd221f9fSJames Morse return -EBUSY; 231bd221f9fSJames Morse } 232bd221f9fSJames Morse 233bd221f9fSJames Morse return 0; 234bd221f9fSJames Morse } 235bd221f9fSJames Morse EXPORT_SYMBOL(mpam_register_requestor); 236bd221f9fSJames Morse 23701fb4b82SJames Morse static struct mpam_class * 23801fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type) 23901fb4b82SJames Morse { 24001fb4b82SJames Morse struct mpam_class *class; 24101fb4b82SJames Morse 24201fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 24301fb4b82SJames Morse 24401fb4b82SJames Morse class = kzalloc(sizeof(*class), GFP_KERNEL); 24501fb4b82SJames Morse if (!class) 24601fb4b82SJames Morse return ERR_PTR(-ENOMEM); 24701fb4b82SJames Morse init_garbage(&class->garbage); 24801fb4b82SJames Morse 24901fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->components); 25001fb4b82SJames Morse /* Affinity is updated when ris are added */ 25101fb4b82SJames Morse class->level = level_idx; 25201fb4b82SJames Morse class->type = type; 25301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->classes_list); 25401fb4b82SJames Morse 25501fb4b82SJames Morse list_add_rcu(&class->classes_list, &mpam_classes); 25601fb4b82SJames Morse 25701fb4b82SJames Morse return class; 25801fb4b82SJames Morse } 25901fb4b82SJames Morse 26001fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class) 26101fb4b82SJames Morse { 26201fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 26301fb4b82SJames Morse 26401fb4b82SJames Morse list_del_rcu(&class->classes_list); 26501fb4b82SJames Morse add_to_garbage(class); 26601fb4b82SJames Morse } 26701fb4b82SJames Morse 26801fb4b82SJames Morse static struct mpam_class * 26901fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type) 27001fb4b82SJames Morse { 27101fb4b82SJames Morse struct mpam_class *class; 27201fb4b82SJames Morse 27301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 27401fb4b82SJames Morse 27501fb4b82SJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 27601fb4b82SJames Morse if (class->type == type && class->level == level_idx) 27701fb4b82SJames Morse return class; 27801fb4b82SJames Morse } 27901fb4b82SJames Morse 28001fb4b82SJames Morse return mpam_class_alloc(level_idx, type); 28101fb4b82SJames Morse } 28201fb4b82SJames Morse 28301fb4b82SJames Morse static struct mpam_component * 28401fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id) 28501fb4b82SJames Morse { 28601fb4b82SJames Morse struct mpam_component *comp; 28701fb4b82SJames Morse 28801fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 28901fb4b82SJames Morse 29001fb4b82SJames Morse comp = kzalloc(sizeof(*comp), GFP_KERNEL); 29101fb4b82SJames Morse if (!comp) 29201fb4b82SJames Morse return ERR_PTR(-ENOMEM); 29301fb4b82SJames Morse init_garbage(&comp->garbage); 29401fb4b82SJames Morse 29501fb4b82SJames Morse comp->comp_id = id; 29601fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->vmsc); 29701fb4b82SJames Morse /* Affinity is updated when RIS are added */ 29801fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->class_list); 29901fb4b82SJames Morse comp->class = class; 30001fb4b82SJames Morse 30101fb4b82SJames Morse list_add_rcu(&comp->class_list, &class->components); 30201fb4b82SJames Morse 30301fb4b82SJames Morse return comp; 30401fb4b82SJames Morse } 30501fb4b82SJames Morse 30601fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp) 30701fb4b82SJames Morse { 30801fb4b82SJames Morse struct mpam_class *class = comp->class; 30901fb4b82SJames Morse 31001fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 31101fb4b82SJames Morse 31201fb4b82SJames Morse list_del_rcu(&comp->class_list); 31301fb4b82SJames Morse add_to_garbage(comp); 31401fb4b82SJames Morse 31501fb4b82SJames Morse if (list_empty(&class->components)) 31601fb4b82SJames Morse mpam_class_destroy(class); 31701fb4b82SJames Morse } 31801fb4b82SJames Morse 31901fb4b82SJames Morse static struct mpam_component * 32001fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id) 32101fb4b82SJames Morse { 32201fb4b82SJames Morse struct mpam_component *comp; 32301fb4b82SJames Morse 32401fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 32501fb4b82SJames Morse 32601fb4b82SJames Morse list_for_each_entry(comp, &class->components, class_list) { 32701fb4b82SJames Morse if (comp->comp_id == id) 32801fb4b82SJames Morse return comp; 32901fb4b82SJames Morse } 33001fb4b82SJames Morse 33101fb4b82SJames Morse return mpam_component_alloc(class, id); 33201fb4b82SJames Morse } 33301fb4b82SJames Morse 33401fb4b82SJames Morse static struct mpam_vmsc * 33501fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) 33601fb4b82SJames Morse { 33701fb4b82SJames Morse struct mpam_vmsc *vmsc; 33801fb4b82SJames Morse 33901fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 34001fb4b82SJames Morse 34101fb4b82SJames Morse vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL); 34201fb4b82SJames Morse if (!vmsc) 34301fb4b82SJames Morse return ERR_PTR(-ENOMEM); 34401fb4b82SJames Morse init_garbage(&vmsc->garbage); 34501fb4b82SJames Morse 34601fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->ris); 34701fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->comp_list); 34801fb4b82SJames Morse vmsc->comp = comp; 34901fb4b82SJames Morse vmsc->msc = msc; 35001fb4b82SJames Morse 35101fb4b82SJames Morse list_add_rcu(&vmsc->comp_list, &comp->vmsc); 35201fb4b82SJames Morse 35301fb4b82SJames Morse return vmsc; 35401fb4b82SJames Morse } 35501fb4b82SJames Morse 35601fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) 35701fb4b82SJames Morse { 35801fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 35901fb4b82SJames Morse 36001fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 36101fb4b82SJames Morse 36201fb4b82SJames Morse list_del_rcu(&vmsc->comp_list); 36301fb4b82SJames Morse add_to_garbage(vmsc); 36401fb4b82SJames Morse 36501fb4b82SJames Morse if (list_empty(&comp->vmsc)) 36601fb4b82SJames Morse mpam_component_destroy(comp); 36701fb4b82SJames Morse } 36801fb4b82SJames Morse 36901fb4b82SJames Morse static struct mpam_vmsc * 37001fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) 37101fb4b82SJames Morse { 37201fb4b82SJames Morse struct mpam_vmsc *vmsc; 37301fb4b82SJames Morse 37401fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 37501fb4b82SJames Morse 37601fb4b82SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 37701fb4b82SJames Morse if (vmsc->msc->id == msc->id) 37801fb4b82SJames Morse return vmsc; 37901fb4b82SJames Morse } 38001fb4b82SJames Morse 38101fb4b82SJames Morse return mpam_vmsc_alloc(comp, msc); 38201fb4b82SJames Morse } 38301fb4b82SJames Morse 38401fb4b82SJames Morse /* 38501fb4b82SJames Morse * The cacheinfo structures are only populated when CPUs are online. 38601fb4b82SJames Morse * This helper walks the acpi tables to include offline CPUs too. 38701fb4b82SJames Morse */ 38801fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 38901fb4b82SJames Morse cpumask_t *affinity) 39001fb4b82SJames Morse { 39101fb4b82SJames Morse return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); 39201fb4b82SJames Morse } 39301fb4b82SJames Morse 39401fb4b82SJames Morse /* 39501fb4b82SJames Morse * cpumask_of_node() only knows about online CPUs. This can't tell us whether 39601fb4b82SJames Morse * a class is represented on all possible CPUs. 39701fb4b82SJames Morse */ 39801fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) 39901fb4b82SJames Morse { 40001fb4b82SJames Morse int cpu; 40101fb4b82SJames Morse 40201fb4b82SJames Morse for_each_possible_cpu(cpu) { 40301fb4b82SJames Morse if (node_id == cpu_to_node(cpu)) 40401fb4b82SJames Morse cpumask_set_cpu(cpu, affinity); 40501fb4b82SJames Morse } 40601fb4b82SJames Morse } 40701fb4b82SJames Morse 40801fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, 40901fb4b82SJames Morse enum mpam_class_types type, 41001fb4b82SJames Morse struct mpam_class *class, 41101fb4b82SJames Morse struct mpam_component *comp) 41201fb4b82SJames Morse { 41301fb4b82SJames Morse int err; 41401fb4b82SJames Morse 41501fb4b82SJames Morse switch (type) { 41601fb4b82SJames Morse case MPAM_CLASS_CACHE: 41701fb4b82SJames Morse err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, 41801fb4b82SJames Morse affinity); 41901fb4b82SJames Morse if (err) { 42001fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, 42101fb4b82SJames Morse "Failed to determine CPU affinity\n"); 42201fb4b82SJames Morse return err; 42301fb4b82SJames Morse } 42401fb4b82SJames Morse 42501fb4b82SJames Morse if (cpumask_empty(affinity)) 42601fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n"); 42701fb4b82SJames Morse 42801fb4b82SJames Morse break; 42901fb4b82SJames Morse case MPAM_CLASS_MEMORY: 43001fb4b82SJames Morse get_cpumask_from_node_id(comp->comp_id, affinity); 43101fb4b82SJames Morse /* affinity may be empty for CPU-less memory nodes */ 43201fb4b82SJames Morse break; 43301fb4b82SJames Morse case MPAM_CLASS_UNKNOWN: 43401fb4b82SJames Morse return 0; 43501fb4b82SJames Morse } 43601fb4b82SJames Morse 43701fb4b82SJames Morse cpumask_and(affinity, affinity, &msc->accessibility); 43801fb4b82SJames Morse 43901fb4b82SJames Morse return 0; 44001fb4b82SJames Morse } 44101fb4b82SJames Morse 44201fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, 44301fb4b82SJames Morse enum mpam_class_types type, u8 class_id, 44401fb4b82SJames Morse int component_id) 44501fb4b82SJames Morse { 44601fb4b82SJames Morse int err; 44701fb4b82SJames Morse struct mpam_vmsc *vmsc; 44801fb4b82SJames Morse struct mpam_msc_ris *ris; 44901fb4b82SJames Morse struct mpam_class *class; 45001fb4b82SJames Morse struct mpam_component *comp; 45101fb4b82SJames Morse struct platform_device *pdev = msc->pdev; 45201fb4b82SJames Morse 45301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 45401fb4b82SJames Morse 45501fb4b82SJames Morse if (ris_idx > MPAM_MSC_MAX_NUM_RIS) 45601fb4b82SJames Morse return -EINVAL; 45701fb4b82SJames Morse 45801fb4b82SJames Morse if (test_and_set_bit(ris_idx, &msc->ris_idxs)) 45901fb4b82SJames Morse return -EBUSY; 46001fb4b82SJames Morse 46101fb4b82SJames Morse ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL); 46201fb4b82SJames Morse if (!ris) 46301fb4b82SJames Morse return -ENOMEM; 46401fb4b82SJames Morse init_garbage(&ris->garbage); 46501fb4b82SJames Morse ris->garbage.pdev = pdev; 46601fb4b82SJames Morse 46701fb4b82SJames Morse class = mpam_class_find(class_id, type); 46801fb4b82SJames Morse if (IS_ERR(class)) 46901fb4b82SJames Morse return PTR_ERR(class); 47001fb4b82SJames Morse 47101fb4b82SJames Morse comp = mpam_component_find(class, component_id); 47201fb4b82SJames Morse if (IS_ERR(comp)) { 47301fb4b82SJames Morse if (list_empty(&class->components)) 47401fb4b82SJames Morse mpam_class_destroy(class); 47501fb4b82SJames Morse return PTR_ERR(comp); 47601fb4b82SJames Morse } 47701fb4b82SJames Morse 47801fb4b82SJames Morse vmsc = mpam_vmsc_find(comp, msc); 47901fb4b82SJames Morse if (IS_ERR(vmsc)) { 48001fb4b82SJames Morse if (list_empty(&comp->vmsc)) 48101fb4b82SJames Morse mpam_component_destroy(comp); 48201fb4b82SJames Morse return PTR_ERR(vmsc); 48301fb4b82SJames Morse } 48401fb4b82SJames Morse 48501fb4b82SJames Morse err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); 48601fb4b82SJames Morse if (err) { 48701fb4b82SJames Morse if (list_empty(&vmsc->ris)) 48801fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 48901fb4b82SJames Morse return err; 49001fb4b82SJames Morse } 49101fb4b82SJames Morse 49201fb4b82SJames Morse ris->ris_idx = ris_idx; 49301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->msc_list); 49401fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->vmsc_list); 49501fb4b82SJames Morse ris->vmsc = vmsc; 49601fb4b82SJames Morse 49701fb4b82SJames Morse cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); 49801fb4b82SJames Morse cpumask_or(&class->affinity, &class->affinity, &ris->affinity); 49901fb4b82SJames Morse list_add_rcu(&ris->vmsc_list, &vmsc->ris); 50001fb4b82SJames Morse list_add_rcu(&ris->msc_list, &msc->ris); 50101fb4b82SJames Morse 50201fb4b82SJames Morse return 0; 50301fb4b82SJames Morse } 50401fb4b82SJames Morse 50501fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris) 50601fb4b82SJames Morse { 50701fb4b82SJames Morse struct mpam_vmsc *vmsc = ris->vmsc; 50801fb4b82SJames Morse struct mpam_msc *msc = vmsc->msc; 50901fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 51001fb4b82SJames Morse struct mpam_class *class = comp->class; 51101fb4b82SJames Morse 51201fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 51301fb4b82SJames Morse 51401fb4b82SJames Morse /* 51501fb4b82SJames Morse * It is assumed affinities don't overlap. If they do the class becomes 51601fb4b82SJames Morse * unusable immediately. 51701fb4b82SJames Morse */ 51801fb4b82SJames Morse cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); 51901fb4b82SJames Morse cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); 52001fb4b82SJames Morse clear_bit(ris->ris_idx, &msc->ris_idxs); 52101fb4b82SJames Morse list_del_rcu(&ris->msc_list); 52201fb4b82SJames Morse list_del_rcu(&ris->vmsc_list); 52301fb4b82SJames Morse add_to_garbage(ris); 52401fb4b82SJames Morse 52501fb4b82SJames Morse if (list_empty(&vmsc->ris)) 52601fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 52701fb4b82SJames Morse } 52801fb4b82SJames Morse 52901fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 53001fb4b82SJames Morse enum mpam_class_types type, u8 class_id, int component_id) 53101fb4b82SJames Morse { 53201fb4b82SJames Morse int err; 53301fb4b82SJames Morse 53401fb4b82SJames Morse mutex_lock(&mpam_list_lock); 53501fb4b82SJames Morse err = mpam_ris_create_locked(msc, ris_idx, type, class_id, 53601fb4b82SJames Morse component_id); 53701fb4b82SJames Morse mutex_unlock(&mpam_list_lock); 53801fb4b82SJames Morse if (err) 53901fb4b82SJames Morse mpam_free_garbage(); 54001fb4b82SJames Morse 54101fb4b82SJames Morse return err; 54201fb4b82SJames Morse } 54301fb4b82SJames Morse 544bd221f9fSJames Morse static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc, 545bd221f9fSJames Morse u8 ris_idx) 546bd221f9fSJames Morse { 547bd221f9fSJames Morse int err; 548bd221f9fSJames Morse struct mpam_msc_ris *ris; 549bd221f9fSJames Morse 550bd221f9fSJames Morse lockdep_assert_held(&mpam_list_lock); 551bd221f9fSJames Morse 552bd221f9fSJames Morse if (!test_bit(ris_idx, &msc->ris_idxs)) { 553bd221f9fSJames Morse err = mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN, 554bd221f9fSJames Morse 0, 0); 555bd221f9fSJames Morse if (err) 556bd221f9fSJames Morse return ERR_PTR(err); 557bd221f9fSJames Morse } 558bd221f9fSJames Morse 559bd221f9fSJames Morse list_for_each_entry(ris, &msc->ris, msc_list) { 560bd221f9fSJames Morse if (ris->ris_idx == ris_idx) 561bd221f9fSJames Morse return ris; 562bd221f9fSJames Morse } 563bd221f9fSJames Morse 564bd221f9fSJames Morse return ERR_PTR(-ENOENT); 565bd221f9fSJames Morse } 566bd221f9fSJames Morse 5678c90dc68SJames Morse /* 5688c90dc68SJames Morse * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour 5698c90dc68SJames Morse * of NRDY, software can use this bit for any purpose" - so hardware might not 5708c90dc68SJames Morse * implement this - but it isn't RES0. 5718c90dc68SJames Morse * 5728c90dc68SJames Morse * Try and see what values stick in this bit. If we can write either value, 5738c90dc68SJames Morse * its probably not implemented by hardware. 5748c90dc68SJames Morse */ 5758c90dc68SJames Morse static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg) 5768c90dc68SJames Morse { 5778c90dc68SJames Morse u32 now; 5788c90dc68SJames Morse u64 mon_sel; 5798c90dc68SJames Morse bool can_set, can_clear; 5808c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 5818c90dc68SJames Morse 5828c90dc68SJames Morse if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc))) 5838c90dc68SJames Morse return false; 5848c90dc68SJames Morse 5858c90dc68SJames Morse mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | 5868c90dc68SJames Morse FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); 5878c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, mon_sel); 5888c90dc68SJames Morse 5898c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); 5908c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 5918c90dc68SJames Morse can_set = now & MSMON___NRDY; 5928c90dc68SJames Morse 5938c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, 0); 5948c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 5958c90dc68SJames Morse can_clear = !(now & MSMON___NRDY); 5968c90dc68SJames Morse mpam_mon_sel_unlock(msc); 5978c90dc68SJames Morse 5988c90dc68SJames Morse return (!can_set || !can_clear); 5998c90dc68SJames Morse } 6008c90dc68SJames Morse 6018c90dc68SJames Morse #define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ 6028c90dc68SJames Morse _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) 6038c90dc68SJames Morse 6048c90dc68SJames Morse static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) 6058c90dc68SJames Morse { 6068c90dc68SJames Morse int err; 6078c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 6088c90dc68SJames Morse struct device *dev = &msc->pdev->dev; 6098c90dc68SJames Morse struct mpam_props *props = &ris->props; 6108c90dc68SJames Morse 6118c90dc68SJames Morse lockdep_assert_held(&msc->probe_lock); 6128c90dc68SJames Morse lockdep_assert_held(&msc->part_sel_lock); 6138c90dc68SJames Morse 6148c90dc68SJames Morse /* Cache Portion partitioning */ 6158c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { 6168c90dc68SJames Morse u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR); 6178c90dc68SJames Morse 6188c90dc68SJames Morse props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); 6198c90dc68SJames Morse if (props->cpbm_wd) 6208c90dc68SJames Morse mpam_set_feature(mpam_feat_cpor_part, props); 6218c90dc68SJames Morse } 6228c90dc68SJames Morse 6238c90dc68SJames Morse /* Memory bandwidth partitioning */ 6248c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) { 6258c90dc68SJames Morse u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR); 6268c90dc68SJames Morse 6278c90dc68SJames Morse /* portion bitmap resolution */ 6288c90dc68SJames Morse props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features); 6298c90dc68SJames Morse if (props->mbw_pbm_bits && 6308c90dc68SJames Morse FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features)) 6318c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_part, props); 6328c90dc68SJames Morse 6338c90dc68SJames Morse props->bwa_wd = FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); 6348c90dc68SJames Morse if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) 6358c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_max, props); 6368c90dc68SJames Morse } 6378c90dc68SJames Morse 6388c90dc68SJames Morse /* Performance Monitoring */ 6398c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) { 6408c90dc68SJames Morse u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR); 6418c90dc68SJames Morse 6428c90dc68SJames Morse /* 6438c90dc68SJames Morse * If the firmware max-nrdy-us property is missing, the 6448c90dc68SJames Morse * CSU counters can't be used. Should we wait forever? 6458c90dc68SJames Morse */ 6468c90dc68SJames Morse err = device_property_read_u32(&msc->pdev->dev, 6478c90dc68SJames Morse "arm,not-ready-us", 6488c90dc68SJames Morse &msc->nrdy_usec); 6498c90dc68SJames Morse 6508c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) { 6518c90dc68SJames Morse u32 csumonidr; 6528c90dc68SJames Morse 6538c90dc68SJames Morse csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR); 6548c90dc68SJames Morse props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr); 6558c90dc68SJames Morse if (props->num_csu_mon) { 6568c90dc68SJames Morse bool hw_managed; 6578c90dc68SJames Morse 6588c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu, props); 6598c90dc68SJames Morse 6608c90dc68SJames Morse /* Is NRDY hardware managed? */ 6618c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU); 6628c90dc68SJames Morse if (hw_managed) 6638c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); 6648c90dc68SJames Morse } 6658c90dc68SJames Morse 6668c90dc68SJames Morse /* 6678c90dc68SJames Morse * Accept the missing firmware property if NRDY appears 6688c90dc68SJames Morse * un-implemented. 6698c90dc68SJames Morse */ 6708c90dc68SJames Morse if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) 6718c90dc68SJames Morse dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware."); 6728c90dc68SJames Morse } 6738c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { 6748c90dc68SJames Morse bool hw_managed; 6758c90dc68SJames Morse u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR); 6768c90dc68SJames Morse 6778c90dc68SJames Morse props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr); 6788c90dc68SJames Morse if (props->num_mbwu_mon) 6798c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu, props); 6808c90dc68SJames Morse 6818c90dc68SJames Morse /* Is NRDY hardware managed? */ 6828c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU); 6838c90dc68SJames Morse if (hw_managed) 6848c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); 6858c90dc68SJames Morse 6868c90dc68SJames Morse /* 6878c90dc68SJames Morse * Don't warn about any missing firmware property for 6888c90dc68SJames Morse * MBWU NRDY - it doesn't make any sense! 6898c90dc68SJames Morse */ 6908c90dc68SJames Morse } 6918c90dc68SJames Morse } 6928c90dc68SJames Morse } 6938c90dc68SJames Morse 6948f8d0ac1SJames Morse static int mpam_msc_hw_probe(struct mpam_msc *msc) 6958f8d0ac1SJames Morse { 6968f8d0ac1SJames Morse u64 idr; 697bd221f9fSJames Morse u16 partid_max; 698bd221f9fSJames Morse u8 ris_idx, pmg_max; 699bd221f9fSJames Morse struct mpam_msc_ris *ris; 7008f8d0ac1SJames Morse struct device *dev = &msc->pdev->dev; 7018f8d0ac1SJames Morse 7028f8d0ac1SJames Morse lockdep_assert_held(&msc->probe_lock); 7038f8d0ac1SJames Morse 7048f8d0ac1SJames Morse idr = __mpam_read_reg(msc, MPAMF_AIDR); 7058f8d0ac1SJames Morse if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) { 7068f8d0ac1SJames Morse dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n"); 7078f8d0ac1SJames Morse return -EIO; 7088f8d0ac1SJames Morse } 7098f8d0ac1SJames Morse 710bd221f9fSJames Morse /* Grab an IDR value to find out how many RIS there are */ 711bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 712bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 713bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 714bd221f9fSJames Morse 715bd221f9fSJames Morse msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr); 716bd221f9fSJames Morse 717bd221f9fSJames Morse /* Use these values so partid/pmg always starts with a valid value */ 718bd221f9fSJames Morse msc->partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 719bd221f9fSJames Morse msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 720bd221f9fSJames Morse 721bd221f9fSJames Morse for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) { 722bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 723bd221f9fSJames Morse __mpam_part_sel(ris_idx, 0, msc); 724bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 725bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 726bd221f9fSJames Morse 727bd221f9fSJames Morse partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 728bd221f9fSJames Morse pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 729bd221f9fSJames Morse msc->partid_max = min(msc->partid_max, partid_max); 730bd221f9fSJames Morse msc->pmg_max = min(msc->pmg_max, pmg_max); 731bd221f9fSJames Morse 732bd221f9fSJames Morse mutex_lock(&mpam_list_lock); 733bd221f9fSJames Morse ris = mpam_get_or_create_ris(msc, ris_idx); 734bd221f9fSJames Morse mutex_unlock(&mpam_list_lock); 735bd221f9fSJames Morse if (IS_ERR(ris)) 736bd221f9fSJames Morse return PTR_ERR(ris); 7378c90dc68SJames Morse ris->idr = idr; 7388c90dc68SJames Morse 7398c90dc68SJames Morse mutex_lock(&msc->part_sel_lock); 7408c90dc68SJames Morse __mpam_part_sel(ris_idx, 0, msc); 7418c90dc68SJames Morse mpam_ris_hw_probe(ris); 7428c90dc68SJames Morse mutex_unlock(&msc->part_sel_lock); 743bd221f9fSJames Morse } 744bd221f9fSJames Morse 745bd221f9fSJames Morse spin_lock(&partid_max_lock); 746bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, msc->partid_max); 747bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, msc->pmg_max); 748bd221f9fSJames Morse spin_unlock(&partid_max_lock); 749bd221f9fSJames Morse 7508f8d0ac1SJames Morse msc->probed = true; 7518f8d0ac1SJames Morse 7528f8d0ac1SJames Morse return 0; 7538f8d0ac1SJames Morse } 7548f8d0ac1SJames Morse 7558f8d0ac1SJames Morse static int mpam_cpu_online(unsigned int cpu) 7568f8d0ac1SJames Morse { 7578f8d0ac1SJames Morse return 0; 7588f8d0ac1SJames Morse } 7598f8d0ac1SJames Morse 7608f8d0ac1SJames Morse /* Before mpam is enabled, try to probe new MSC */ 7618f8d0ac1SJames Morse static int mpam_discovery_cpu_online(unsigned int cpu) 7628f8d0ac1SJames Morse { 7638f8d0ac1SJames Morse int err = 0; 7648f8d0ac1SJames Morse struct mpam_msc *msc; 7658f8d0ac1SJames Morse bool new_device_probed = false; 7668f8d0ac1SJames Morse 7678f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 7688f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 7698f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 7708f8d0ac1SJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 7718f8d0ac1SJames Morse continue; 7728f8d0ac1SJames Morse 7738f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 7748f8d0ac1SJames Morse if (!msc->probed) 7758f8d0ac1SJames Morse err = mpam_msc_hw_probe(msc); 7768f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 7778f8d0ac1SJames Morse 7788f8d0ac1SJames Morse if (err) 7798f8d0ac1SJames Morse break; 7808f8d0ac1SJames Morse new_device_probed = true; 7818f8d0ac1SJames Morse } 7828f8d0ac1SJames Morse 7838f8d0ac1SJames Morse if (new_device_probed && !err) 7848f8d0ac1SJames Morse schedule_work(&mpam_enable_work); 7858f8d0ac1SJames Morse if (err) { 7868f8d0ac1SJames Morse mpam_disable_reason = "error during probing"; 7878f8d0ac1SJames Morse schedule_work(&mpam_broken_work); 7888f8d0ac1SJames Morse } 7898f8d0ac1SJames Morse 7908f8d0ac1SJames Morse return err; 7918f8d0ac1SJames Morse } 7928f8d0ac1SJames Morse 7938f8d0ac1SJames Morse static int mpam_cpu_offline(unsigned int cpu) 7948f8d0ac1SJames Morse { 7958f8d0ac1SJames Morse return 0; 7968f8d0ac1SJames Morse } 7978f8d0ac1SJames Morse 7988f8d0ac1SJames Morse static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online), 7998f8d0ac1SJames Morse int (*offline)(unsigned int offline), 8008f8d0ac1SJames Morse char *name) 8018f8d0ac1SJames Morse { 8028f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 8038f8d0ac1SJames Morse if (mpam_cpuhp_state) { 8048f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 8058f8d0ac1SJames Morse mpam_cpuhp_state = 0; 8068f8d0ac1SJames Morse } 8078f8d0ac1SJames Morse 8088f8d0ac1SJames Morse mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online, 8098f8d0ac1SJames Morse offline); 8108f8d0ac1SJames Morse if (mpam_cpuhp_state <= 0) { 8118f8d0ac1SJames Morse pr_err("Failed to register cpuhp callbacks"); 8128f8d0ac1SJames Morse mpam_cpuhp_state = 0; 8138f8d0ac1SJames Morse } 8148f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 8158f8d0ac1SJames Morse } 8168f8d0ac1SJames Morse 81701fb4b82SJames Morse /* 818f04046f2SJames Morse * An MSC can control traffic from a set of CPUs, but may only be accessible 819f04046f2SJames Morse * from a (hopefully wider) set of CPUs. The common reason for this is power 820f04046f2SJames Morse * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 821f04046f2SJames Morse * corresponding cache may also be powered off. By making accesses from 822f04046f2SJames Morse * one of those CPUs, we ensure we don't access a cache that's powered off. 823f04046f2SJames Morse */ 824f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc) 825f04046f2SJames Morse { 826f04046f2SJames Morse u32 affinity_id; 827f04046f2SJames Morse int err; 828f04046f2SJames Morse 829f04046f2SJames Morse err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 830f04046f2SJames Morse &affinity_id); 831f04046f2SJames Morse if (err) 832f04046f2SJames Morse cpumask_copy(&msc->accessibility, cpu_possible_mask); 833f04046f2SJames Morse else 834f04046f2SJames Morse acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 835f04046f2SJames Morse } 836f04046f2SJames Morse 83701fb4b82SJames Morse /* 83801fb4b82SJames Morse * There are two ways of reaching a struct mpam_msc_ris. Via the 83901fb4b82SJames Morse * class->component->vmsc->ris, or via the msc. 84001fb4b82SJames Morse * When destroying the msc, the other side needs unlinking and cleaning up too. 84101fb4b82SJames Morse */ 842f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc) 843f04046f2SJames Morse { 844f04046f2SJames Morse struct platform_device *pdev = msc->pdev; 84501fb4b82SJames Morse struct mpam_msc_ris *ris, *tmp; 846f04046f2SJames Morse 847f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 848f04046f2SJames Morse 84901fb4b82SJames Morse list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) 85001fb4b82SJames Morse mpam_ris_destroy(ris); 85101fb4b82SJames Morse 852f04046f2SJames Morse list_del_rcu(&msc->all_msc_list); 853f04046f2SJames Morse platform_set_drvdata(pdev, NULL); 85401fb4b82SJames Morse 85501fb4b82SJames Morse add_to_garbage(msc); 856f04046f2SJames Morse } 857f04046f2SJames Morse 858f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev) 859f04046f2SJames Morse { 860f04046f2SJames Morse struct mpam_msc *msc = platform_get_drvdata(pdev); 861f04046f2SJames Morse 862f04046f2SJames Morse mutex_lock(&mpam_list_lock); 863f04046f2SJames Morse mpam_msc_destroy(msc); 864f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 865f04046f2SJames Morse 86601fb4b82SJames Morse mpam_free_garbage(); 867f04046f2SJames Morse } 868f04046f2SJames Morse 869f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 870f04046f2SJames Morse { 871f04046f2SJames Morse int err; 872f04046f2SJames Morse u32 tmp; 873f04046f2SJames Morse struct mpam_msc *msc; 874f04046f2SJames Morse struct resource *msc_res; 875f04046f2SJames Morse struct device *dev = &pdev->dev; 876f04046f2SJames Morse 877f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 878f04046f2SJames Morse 879f04046f2SJames Morse msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 880f04046f2SJames Morse if (!msc) 881f04046f2SJames Morse return ERR_PTR(-ENOMEM); 88201fb4b82SJames Morse init_garbage(&msc->garbage); 88301fb4b82SJames Morse msc->garbage.pdev = pdev; 884f04046f2SJames Morse 885f04046f2SJames Morse err = devm_mutex_init(dev, &msc->probe_lock); 886f04046f2SJames Morse if (err) 887f04046f2SJames Morse return ERR_PTR(err); 888f04046f2SJames Morse 889f04046f2SJames Morse err = devm_mutex_init(dev, &msc->part_sel_lock); 890f04046f2SJames Morse if (err) 891f04046f2SJames Morse return ERR_PTR(err); 892f04046f2SJames Morse 893d02beb06SJames Morse mpam_mon_sel_lock_init(msc); 894f04046f2SJames Morse msc->id = pdev->id; 895f04046f2SJames Morse msc->pdev = pdev; 896f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->all_msc_list); 897f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->ris); 898f04046f2SJames Morse 899f04046f2SJames Morse update_msc_accessibility(msc); 900f04046f2SJames Morse if (cpumask_empty(&msc->accessibility)) { 901f04046f2SJames Morse dev_err_once(dev, "MSC is not accessible from any CPU!"); 902f04046f2SJames Morse return ERR_PTR(-EINVAL); 903f04046f2SJames Morse } 904f04046f2SJames Morse 905f04046f2SJames Morse if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 906f04046f2SJames Morse msc->iface = MPAM_IFACE_MMIO; 907f04046f2SJames Morse else 908f04046f2SJames Morse msc->iface = MPAM_IFACE_PCC; 909f04046f2SJames Morse 910f04046f2SJames Morse if (msc->iface == MPAM_IFACE_MMIO) { 911f04046f2SJames Morse void __iomem *io; 912f04046f2SJames Morse 913f04046f2SJames Morse io = devm_platform_get_and_ioremap_resource(pdev, 0, 914f04046f2SJames Morse &msc_res); 915f04046f2SJames Morse if (IS_ERR(io)) { 916f04046f2SJames Morse dev_err_once(dev, "Failed to map MSC base address\n"); 917f04046f2SJames Morse return ERR_CAST(io); 918f04046f2SJames Morse } 919f04046f2SJames Morse msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 920f04046f2SJames Morse msc->mapped_hwpage = io; 921f04046f2SJames Morse } else { 922f04046f2SJames Morse return ERR_PTR(-EINVAL); 923f04046f2SJames Morse } 924f04046f2SJames Morse 925f04046f2SJames Morse list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 926f04046f2SJames Morse platform_set_drvdata(pdev, msc); 927f04046f2SJames Morse 928f04046f2SJames Morse return msc; 929f04046f2SJames Morse } 930f04046f2SJames Morse 931f04046f2SJames Morse static int fw_num_msc; 932f04046f2SJames Morse 933f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev) 934f04046f2SJames Morse { 935f04046f2SJames Morse int err; 936f04046f2SJames Morse struct mpam_msc *msc = NULL; 937f04046f2SJames Morse void *plat_data = pdev->dev.platform_data; 938f04046f2SJames Morse 939f04046f2SJames Morse mutex_lock(&mpam_list_lock); 940f04046f2SJames Morse msc = do_mpam_msc_drv_probe(pdev); 941f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 942f04046f2SJames Morse 943f04046f2SJames Morse if (IS_ERR(msc)) 944f04046f2SJames Morse return PTR_ERR(msc); 945f04046f2SJames Morse 946f04046f2SJames Morse /* Create RIS entries described by firmware */ 947f04046f2SJames Morse err = acpi_mpam_parse_resources(msc, plat_data); 948f04046f2SJames Morse if (err) { 949f04046f2SJames Morse mpam_msc_drv_remove(pdev); 950f04046f2SJames Morse return err; 951f04046f2SJames Morse } 952f04046f2SJames Morse 953f04046f2SJames Morse if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 9548f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL, 9558f8d0ac1SJames Morse "mpam:drv_probe"); 956f04046f2SJames Morse 957f04046f2SJames Morse return 0; 958f04046f2SJames Morse } 959f04046f2SJames Morse 960f04046f2SJames Morse static struct platform_driver mpam_msc_driver = { 961f04046f2SJames Morse .driver = { 962f04046f2SJames Morse .name = "mpam_msc", 963f04046f2SJames Morse }, 964f04046f2SJames Morse .probe = mpam_msc_drv_probe, 965f04046f2SJames Morse .remove = mpam_msc_drv_remove, 966f04046f2SJames Morse }; 967f04046f2SJames Morse 968*c10ca83aSJames Morse /* Any of these features mean the BWA_WD field is valid. */ 969*c10ca83aSJames Morse static bool mpam_has_bwa_wd_feature(struct mpam_props *props) 970*c10ca83aSJames Morse { 971*c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_min, props)) 972*c10ca83aSJames Morse return true; 973*c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_max, props)) 974*c10ca83aSJames Morse return true; 975*c10ca83aSJames Morse return false; 976*c10ca83aSJames Morse } 977*c10ca83aSJames Morse 978*c10ca83aSJames Morse #define MISMATCHED_HELPER(parent, child, helper, field, alias) \ 979*c10ca83aSJames Morse helper(parent) && \ 980*c10ca83aSJames Morse ((helper(child) && (parent)->field != (child)->field) || \ 981*c10ca83aSJames Morse (!helper(child) && !(alias))) 982*c10ca83aSJames Morse 983*c10ca83aSJames Morse #define MISMATCHED_FEAT(parent, child, feat, field, alias) \ 984*c10ca83aSJames Morse mpam_has_feature((feat), (parent)) && \ 985*c10ca83aSJames Morse ((mpam_has_feature((feat), (child)) && (parent)->field != (child)->field) || \ 986*c10ca83aSJames Morse (!mpam_has_feature((feat), (child)) && !(alias))) 987*c10ca83aSJames Morse 988*c10ca83aSJames Morse #define CAN_MERGE_FEAT(parent, child, feat, alias) \ 989*c10ca83aSJames Morse (alias) && !mpam_has_feature((feat), (parent)) && \ 990*c10ca83aSJames Morse mpam_has_feature((feat), (child)) 991*c10ca83aSJames Morse 992*c10ca83aSJames Morse /* 993*c10ca83aSJames Morse * Combine two props fields. 994*c10ca83aSJames Morse * If this is for controls that alias the same resource, it is safe to just 995*c10ca83aSJames Morse * copy the values over. If two aliasing controls implement the same scheme 996*c10ca83aSJames Morse * a safe value must be picked. 997*c10ca83aSJames Morse * For non-aliasing controls, these control different resources, and the 998*c10ca83aSJames Morse * resulting safe value must be compatible with both. When merging values in 999*c10ca83aSJames Morse * the tree, all the aliasing resources must be handled first. 1000*c10ca83aSJames Morse * On mismatch, parent is modified. 1001*c10ca83aSJames Morse */ 1002*c10ca83aSJames Morse static void __props_mismatch(struct mpam_props *parent, 1003*c10ca83aSJames Morse struct mpam_props *child, bool alias) 1004*c10ca83aSJames Morse { 1005*c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) { 1006*c10ca83aSJames Morse parent->cpbm_wd = child->cpbm_wd; 1007*c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part, 1008*c10ca83aSJames Morse cpbm_wd, alias)) { 1009*c10ca83aSJames Morse pr_debug("cleared cpor_part\n"); 1010*c10ca83aSJames Morse mpam_clear_feature(mpam_feat_cpor_part, parent); 1011*c10ca83aSJames Morse parent->cpbm_wd = 0; 1012*c10ca83aSJames Morse } 1013*c10ca83aSJames Morse 1014*c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) { 1015*c10ca83aSJames Morse parent->mbw_pbm_bits = child->mbw_pbm_bits; 1016*c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part, 1017*c10ca83aSJames Morse mbw_pbm_bits, alias)) { 1018*c10ca83aSJames Morse pr_debug("cleared mbw_part\n"); 1019*c10ca83aSJames Morse mpam_clear_feature(mpam_feat_mbw_part, parent); 1020*c10ca83aSJames Morse parent->mbw_pbm_bits = 0; 1021*c10ca83aSJames Morse } 1022*c10ca83aSJames Morse 1023*c10ca83aSJames Morse /* bwa_wd is a count of bits, fewer bits means less precision */ 1024*c10ca83aSJames Morse if (alias && !mpam_has_bwa_wd_feature(parent) && 1025*c10ca83aSJames Morse mpam_has_bwa_wd_feature(child)) { 1026*c10ca83aSJames Morse parent->bwa_wd = child->bwa_wd; 1027*c10ca83aSJames Morse } else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature, 1028*c10ca83aSJames Morse bwa_wd, alias)) { 1029*c10ca83aSJames Morse pr_debug("took the min bwa_wd\n"); 1030*c10ca83aSJames Morse parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd); 1031*c10ca83aSJames Morse } 1032*c10ca83aSJames Morse 1033*c10ca83aSJames Morse /* For num properties, take the minimum */ 1034*c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { 1035*c10ca83aSJames Morse parent->num_csu_mon = child->num_csu_mon; 1036*c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu, 1037*c10ca83aSJames Morse num_csu_mon, alias)) { 1038*c10ca83aSJames Morse pr_debug("took the min num_csu_mon\n"); 1039*c10ca83aSJames Morse parent->num_csu_mon = min(parent->num_csu_mon, 1040*c10ca83aSJames Morse child->num_csu_mon); 1041*c10ca83aSJames Morse } 1042*c10ca83aSJames Morse 1043*c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) { 1044*c10ca83aSJames Morse parent->num_mbwu_mon = child->num_mbwu_mon; 1045*c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu, 1046*c10ca83aSJames Morse num_mbwu_mon, alias)) { 1047*c10ca83aSJames Morse pr_debug("took the min num_mbwu_mon\n"); 1048*c10ca83aSJames Morse parent->num_mbwu_mon = min(parent->num_mbwu_mon, 1049*c10ca83aSJames Morse child->num_mbwu_mon); 1050*c10ca83aSJames Morse } 1051*c10ca83aSJames Morse 1052*c10ca83aSJames Morse if (alias) { 1053*c10ca83aSJames Morse /* Merge features for aliased resources */ 1054*c10ca83aSJames Morse bitmap_or(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1055*c10ca83aSJames Morse } else { 1056*c10ca83aSJames Morse /* Clear missing features for non aliasing */ 1057*c10ca83aSJames Morse bitmap_and(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1058*c10ca83aSJames Morse } 1059*c10ca83aSJames Morse } 1060*c10ca83aSJames Morse 1061*c10ca83aSJames Morse /* 1062*c10ca83aSJames Morse * If a vmsc doesn't match class feature/configuration, do the right thing(tm). 1063*c10ca83aSJames Morse * For 'num' properties we can just take the minimum. 1064*c10ca83aSJames Morse * For properties where the mismatched unused bits would make a difference, we 1065*c10ca83aSJames Morse * nobble the class feature, as we can't configure all the resources. 1066*c10ca83aSJames Morse * e.g. The L3 cache is composed of two resources with 13 and 17 portion 1067*c10ca83aSJames Morse * bitmaps respectively. 1068*c10ca83aSJames Morse */ 1069*c10ca83aSJames Morse static void 1070*c10ca83aSJames Morse __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc) 1071*c10ca83aSJames Morse { 1072*c10ca83aSJames Morse struct mpam_props *cprops = &class->props; 1073*c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1074*c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1075*c10ca83aSJames Morse 1076*c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify class */ 1077*c10ca83aSJames Morse 1078*c10ca83aSJames Morse dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n", 1079*c10ca83aSJames Morse (long)cprops->features, (long)vprops->features); 1080*c10ca83aSJames Morse 1081*c10ca83aSJames Morse /* Take the safe value for any common features */ 1082*c10ca83aSJames Morse __props_mismatch(cprops, vprops, false); 1083*c10ca83aSJames Morse } 1084*c10ca83aSJames Morse 1085*c10ca83aSJames Morse static void 1086*c10ca83aSJames Morse __vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris) 1087*c10ca83aSJames Morse { 1088*c10ca83aSJames Morse struct mpam_props *rprops = &ris->props; 1089*c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1090*c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1091*c10ca83aSJames Morse 1092*c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */ 1093*c10ca83aSJames Morse 1094*c10ca83aSJames Morse dev_dbg(dev, "Merging features for vmsc:0x%lx |= ris:0x%lx\n", 1095*c10ca83aSJames Morse (long)vprops->features, (long)rprops->features); 1096*c10ca83aSJames Morse 1097*c10ca83aSJames Morse /* 1098*c10ca83aSJames Morse * Merge mismatched features - Copy any features that aren't common, 1099*c10ca83aSJames Morse * but take the safe value for any common features. 1100*c10ca83aSJames Morse */ 1101*c10ca83aSJames Morse __props_mismatch(vprops, rprops, true); 1102*c10ca83aSJames Morse } 1103*c10ca83aSJames Morse 1104*c10ca83aSJames Morse /* 1105*c10ca83aSJames Morse * Copy the first component's first vMSC's properties and features to the 1106*c10ca83aSJames Morse * class. __class_props_mismatch() will remove conflicts. 1107*c10ca83aSJames Morse * It is not possible to have a class with no components, or a component with 1108*c10ca83aSJames Morse * no resources. The vMSC properties have already been built. 1109*c10ca83aSJames Morse */ 1110*c10ca83aSJames Morse static void mpam_enable_init_class_features(struct mpam_class *class) 1111*c10ca83aSJames Morse { 1112*c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1113*c10ca83aSJames Morse struct mpam_component *comp; 1114*c10ca83aSJames Morse 1115*c10ca83aSJames Morse comp = list_first_entry(&class->components, 1116*c10ca83aSJames Morse struct mpam_component, class_list); 1117*c10ca83aSJames Morse vmsc = list_first_entry(&comp->vmsc, 1118*c10ca83aSJames Morse struct mpam_vmsc, comp_list); 1119*c10ca83aSJames Morse 1120*c10ca83aSJames Morse class->props = vmsc->props; 1121*c10ca83aSJames Morse } 1122*c10ca83aSJames Morse 1123*c10ca83aSJames Morse static void mpam_enable_merge_vmsc_features(struct mpam_component *comp) 1124*c10ca83aSJames Morse { 1125*c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1126*c10ca83aSJames Morse struct mpam_msc_ris *ris; 1127*c10ca83aSJames Morse struct mpam_class *class = comp->class; 1128*c10ca83aSJames Morse 1129*c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 1130*c10ca83aSJames Morse list_for_each_entry(ris, &vmsc->ris, vmsc_list) { 1131*c10ca83aSJames Morse __vmsc_props_mismatch(vmsc, ris); 1132*c10ca83aSJames Morse class->nrdy_usec = max(class->nrdy_usec, 1133*c10ca83aSJames Morse vmsc->msc->nrdy_usec); 1134*c10ca83aSJames Morse } 1135*c10ca83aSJames Morse } 1136*c10ca83aSJames Morse } 1137*c10ca83aSJames Morse 1138*c10ca83aSJames Morse static void mpam_enable_merge_class_features(struct mpam_component *comp) 1139*c10ca83aSJames Morse { 1140*c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1141*c10ca83aSJames Morse struct mpam_class *class = comp->class; 1142*c10ca83aSJames Morse 1143*c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) 1144*c10ca83aSJames Morse __class_props_mismatch(class, vmsc); 1145*c10ca83aSJames Morse } 1146*c10ca83aSJames Morse 1147*c10ca83aSJames Morse /* 1148*c10ca83aSJames Morse * Merge all the common resource features into class. 1149*c10ca83aSJames Morse * vmsc features are bitwise-or'd together by mpam_enable_merge_vmsc_features() 1150*c10ca83aSJames Morse * as the first step so that mpam_enable_init_class_features() can initialise 1151*c10ca83aSJames Morse * the class with a representative set of features. 1152*c10ca83aSJames Morse * Next the mpam_enable_merge_class_features() bitwise-and's all the vmsc 1153*c10ca83aSJames Morse * features to form the class features. 1154*c10ca83aSJames Morse * Other features are the min/max as appropriate. 1155*c10ca83aSJames Morse * 1156*c10ca83aSJames Morse * To avoid walking the whole tree twice, the class->nrdy_usec property is 1157*c10ca83aSJames Morse * updated when working with the vmsc as it is a max(), and doesn't need 1158*c10ca83aSJames Morse * initialising first. 1159*c10ca83aSJames Morse */ 1160*c10ca83aSJames Morse static void mpam_enable_merge_features(struct list_head *all_classes_list) 1161*c10ca83aSJames Morse { 1162*c10ca83aSJames Morse struct mpam_class *class; 1163*c10ca83aSJames Morse struct mpam_component *comp; 1164*c10ca83aSJames Morse 1165*c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); 1166*c10ca83aSJames Morse 1167*c10ca83aSJames Morse list_for_each_entry(class, all_classes_list, classes_list) { 1168*c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 1169*c10ca83aSJames Morse mpam_enable_merge_vmsc_features(comp); 1170*c10ca83aSJames Morse 1171*c10ca83aSJames Morse mpam_enable_init_class_features(class); 1172*c10ca83aSJames Morse 1173*c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 1174*c10ca83aSJames Morse mpam_enable_merge_class_features(comp); 1175*c10ca83aSJames Morse } 1176*c10ca83aSJames Morse } 1177*c10ca83aSJames Morse 11788f8d0ac1SJames Morse static void mpam_enable_once(void) 11798f8d0ac1SJames Morse { 1180bd221f9fSJames Morse /* 1181bd221f9fSJames Morse * Once the cpuhp callbacks have been changed, mpam_partid_max can no 1182bd221f9fSJames Morse * longer change. 1183bd221f9fSJames Morse */ 1184bd221f9fSJames Morse spin_lock(&partid_max_lock); 1185bd221f9fSJames Morse partid_max_published = true; 1186bd221f9fSJames Morse spin_unlock(&partid_max_lock); 1187bd221f9fSJames Morse 1188*c10ca83aSJames Morse mutex_lock(&mpam_list_lock); 1189*c10ca83aSJames Morse mpam_enable_merge_features(&mpam_classes); 1190*c10ca83aSJames Morse mutex_unlock(&mpam_list_lock); 1191*c10ca83aSJames Morse 11928f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline, 11938f8d0ac1SJames Morse "mpam:online"); 11948f8d0ac1SJames Morse 1195bd221f9fSJames Morse /* Use printk() to avoid the pr_fmt adding the function name. */ 1196bd221f9fSJames Morse printk(KERN_INFO "MPAM enabled with %u PARTIDs and %u PMGs\n", 1197bd221f9fSJames Morse mpam_partid_max + 1, mpam_pmg_max + 1); 11988f8d0ac1SJames Morse } 11998f8d0ac1SJames Morse 12008f8d0ac1SJames Morse void mpam_disable(struct work_struct *ignored) 12018f8d0ac1SJames Morse { 12028f8d0ac1SJames Morse struct mpam_msc *msc, *tmp; 12038f8d0ac1SJames Morse 12048f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 12058f8d0ac1SJames Morse if (mpam_cpuhp_state) { 12068f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 12078f8d0ac1SJames Morse mpam_cpuhp_state = 0; 12088f8d0ac1SJames Morse } 12098f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 12108f8d0ac1SJames Morse 12118f8d0ac1SJames Morse mutex_lock(&mpam_list_lock); 12128f8d0ac1SJames Morse list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list) 12138f8d0ac1SJames Morse mpam_msc_destroy(msc); 12148f8d0ac1SJames Morse mutex_unlock(&mpam_list_lock); 12158f8d0ac1SJames Morse mpam_free_garbage(); 12168f8d0ac1SJames Morse 12178f8d0ac1SJames Morse pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason); 12188f8d0ac1SJames Morse } 12198f8d0ac1SJames Morse 12208f8d0ac1SJames Morse /* 12218f8d0ac1SJames Morse * Enable mpam once all devices have been probed. 12228f8d0ac1SJames Morse * Scheduled by mpam_discovery_cpu_online() once all devices have been created. 12238f8d0ac1SJames Morse * Also scheduled when new devices are probed when new CPUs come online. 12248f8d0ac1SJames Morse */ 12258f8d0ac1SJames Morse void mpam_enable(struct work_struct *work) 12268f8d0ac1SJames Morse { 12278f8d0ac1SJames Morse static atomic_t once; 12288f8d0ac1SJames Morse struct mpam_msc *msc; 12298f8d0ac1SJames Morse bool all_devices_probed = true; 12308f8d0ac1SJames Morse 12318f8d0ac1SJames Morse /* Have we probed all the hw devices? */ 12328f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 12338f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 12348f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 12358f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 12368f8d0ac1SJames Morse if (!msc->probed) 12378f8d0ac1SJames Morse all_devices_probed = false; 12388f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 12398f8d0ac1SJames Morse 12408f8d0ac1SJames Morse if (!all_devices_probed) 12418f8d0ac1SJames Morse break; 12428f8d0ac1SJames Morse } 12438f8d0ac1SJames Morse 12448f8d0ac1SJames Morse if (all_devices_probed && !atomic_fetch_inc(&once)) 12458f8d0ac1SJames Morse mpam_enable_once(); 12468f8d0ac1SJames Morse } 12478f8d0ac1SJames Morse 1248f04046f2SJames Morse static int __init mpam_msc_driver_init(void) 1249f04046f2SJames Morse { 1250f04046f2SJames Morse if (!system_supports_mpam()) 1251f04046f2SJames Morse return -EOPNOTSUPP; 1252f04046f2SJames Morse 1253f04046f2SJames Morse init_srcu_struct(&mpam_srcu); 1254f04046f2SJames Morse 1255f04046f2SJames Morse fw_num_msc = acpi_mpam_count_msc(); 1256f04046f2SJames Morse if (fw_num_msc <= 0) { 1257f04046f2SJames Morse pr_err("No MSC devices found in firmware\n"); 1258f04046f2SJames Morse return -EINVAL; 1259f04046f2SJames Morse } 1260f04046f2SJames Morse 1261f04046f2SJames Morse return platform_driver_register(&mpam_msc_driver); 1262f04046f2SJames Morse } 1263bd221f9fSJames Morse 1264bd221f9fSJames Morse /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ 1265f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init); 1266