1f04046f2SJames Morse // SPDX-License-Identifier: GPL-2.0 2f04046f2SJames Morse // Copyright (C) 2025 Arm Ltd. 3f04046f2SJames Morse 4f04046f2SJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 5f04046f2SJames Morse 6f04046f2SJames Morse #include <linux/acpi.h> 78f8d0ac1SJames Morse #include <linux/atomic.h> 8f04046f2SJames Morse #include <linux/arm_mpam.h> 9bd221f9fSJames Morse #include <linux/bitfield.h> 10f188a36cSJames Morse #include <linux/bitmap.h> 11f04046f2SJames Morse #include <linux/cacheinfo.h> 128f8d0ac1SJames Morse #include <linux/cpu.h> 13f04046f2SJames Morse #include <linux/cpumask.h> 14f04046f2SJames Morse #include <linux/device.h> 15f04046f2SJames Morse #include <linux/errno.h> 16f04046f2SJames Morse #include <linux/gfp.h> 1749aa621cSJames Morse #include <linux/interrupt.h> 1849aa621cSJames Morse #include <linux/irq.h> 1949aa621cSJames Morse #include <linux/irqdesc.h> 20f04046f2SJames Morse #include <linux/list.h> 21f04046f2SJames Morse #include <linux/lockdep.h> 22f04046f2SJames Morse #include <linux/mutex.h> 23f04046f2SJames Morse #include <linux/platform_device.h> 24f04046f2SJames Morse #include <linux/printk.h> 25f04046f2SJames Morse #include <linux/srcu.h> 26d02beb06SJames Morse #include <linux/spinlock.h> 27f04046f2SJames Morse #include <linux/types.h> 288f8d0ac1SJames Morse #include <linux/workqueue.h> 29f04046f2SJames Morse 30f04046f2SJames Morse #include "mpam_internal.h" 31f04046f2SJames Morse 323796f75aSJames Morse DEFINE_STATIC_KEY_FALSE(mpam_enabled); /* This moves to arch code */ 333796f75aSJames Morse 34f04046f2SJames Morse /* 35f04046f2SJames Morse * mpam_list_lock protects the SRCU lists when writing. Once the 36f04046f2SJames Morse * mpam_enabled key is enabled these lists are read-only, 37f04046f2SJames Morse * unless the error interrupt disables the driver. 38f04046f2SJames Morse */ 39f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock); 40f04046f2SJames Morse static LIST_HEAD(mpam_all_msc); 41f04046f2SJames Morse 42f04046f2SJames Morse struct srcu_struct mpam_srcu; 43f04046f2SJames Morse 44f04046f2SJames Morse /* 45f04046f2SJames Morse * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 46f04046f2SJames Morse * can be enabled. 47f04046f2SJames Morse */ 48f04046f2SJames Morse static atomic_t mpam_num_msc; 49f04046f2SJames Morse 508f8d0ac1SJames Morse static int mpam_cpuhp_state; 518f8d0ac1SJames Morse static DEFINE_MUTEX(mpam_cpuhp_state_lock); 528f8d0ac1SJames Morse 538f8d0ac1SJames Morse /* 54bd221f9fSJames Morse * The smallest common values for any CPU or MSC in the system. 55bd221f9fSJames Morse * Generating traffic outside this range will result in screaming interrupts. 56bd221f9fSJames Morse */ 57bd221f9fSJames Morse u16 mpam_partid_max; 58bd221f9fSJames Morse u8 mpam_pmg_max; 59bd221f9fSJames Morse static bool partid_max_init, partid_max_published; 60bd221f9fSJames Morse static DEFINE_SPINLOCK(partid_max_lock); 61bd221f9fSJames Morse 62bd221f9fSJames Morse /* 638f8d0ac1SJames Morse * mpam is enabled once all devices have been probed from CPU online callbacks, 648f8d0ac1SJames Morse * scheduled via this work_struct. If access to an MSC depends on a CPU that 658f8d0ac1SJames Morse * was not brought online at boot, this can happen surprisingly late. 668f8d0ac1SJames Morse */ 678f8d0ac1SJames Morse static DECLARE_WORK(mpam_enable_work, &mpam_enable); 688f8d0ac1SJames Morse 698f8d0ac1SJames Morse /* 708f8d0ac1SJames Morse * All mpam error interrupts indicate a software bug. On receipt, disable the 718f8d0ac1SJames Morse * driver. 728f8d0ac1SJames Morse */ 738f8d0ac1SJames Morse static DECLARE_WORK(mpam_broken_work, &mpam_disable); 748f8d0ac1SJames Morse 758f8d0ac1SJames Morse /* When mpam is disabled, the printed reason to aid debugging */ 768f8d0ac1SJames Morse static char *mpam_disable_reason; 778f8d0ac1SJames Morse 78f04046f2SJames Morse /* 7901fb4b82SJames Morse * An MSC is a physical container for controls and monitors, each identified by 8001fb4b82SJames Morse * their RIS index. These share a base-address, interrupts and some MMIO 8101fb4b82SJames Morse * registers. A vMSC is a virtual container for RIS in an MSC that control or 8201fb4b82SJames Morse * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but 8301fb4b82SJames Morse * not all RIS in an MSC share a vMSC. 8401fb4b82SJames Morse * 8501fb4b82SJames Morse * Components are a group of vMSC that control or monitor the same thing but 8601fb4b82SJames Morse * are from different MSC, so have different base-address, interrupts etc. 8701fb4b82SJames Morse * Classes are the set components of the same type. 8801fb4b82SJames Morse * 8901fb4b82SJames Morse * The features of a vMSC is the union of the RIS it contains. 9001fb4b82SJames Morse * The features of a Class and Component are the common subset of the vMSC 9101fb4b82SJames Morse * they contain. 9201fb4b82SJames Morse * 9301fb4b82SJames Morse * e.g. The system cache may have bandwidth controls on multiple interfaces, 9401fb4b82SJames Morse * for regulating traffic from devices independently of traffic from CPUs. 9501fb4b82SJames Morse * If these are two RIS in one MSC, they will be treated as controlling 9601fb4b82SJames Morse * different things, and will not share a vMSC/component/class. 9701fb4b82SJames Morse * 9801fb4b82SJames Morse * e.g. The L2 may have one MSC and two RIS, one for cache-controls another 9901fb4b82SJames Morse * for bandwidth. These two RIS are members of the same vMSC. 10001fb4b82SJames Morse * 10101fb4b82SJames Morse * e.g. The set of RIS that make up the L2 are grouped as a component. These 10201fb4b82SJames Morse * are sometimes termed slices. They should be configured the same, as if there 10301fb4b82SJames Morse * were only one. 10401fb4b82SJames Morse * 10501fb4b82SJames Morse * e.g. The SoC probably has more than one L2, each attached to a distinct set 10601fb4b82SJames Morse * of CPUs. All the L2 components are grouped as a class. 10701fb4b82SJames Morse * 10801fb4b82SJames Morse * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, 10901fb4b82SJames Morse * then linked via struct mpam_ris to a vmsc, component and class. 11001fb4b82SJames Morse * The same MSC may exist under different class->component->vmsc paths, but the 11101fb4b82SJames Morse * RIS index will be unique. 11201fb4b82SJames Morse */ 11301fb4b82SJames Morse LIST_HEAD(mpam_classes); 11401fb4b82SJames Morse 11501fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */ 11601fb4b82SJames Morse static LLIST_HEAD(mpam_garbage); 11701fb4b82SJames Morse 11801fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage) 11901fb4b82SJames Morse { 12001fb4b82SJames Morse init_llist_node(&garbage->llist); 12101fb4b82SJames Morse } 12201fb4b82SJames Morse 12301fb4b82SJames Morse #define add_to_garbage(x) \ 12401fb4b82SJames Morse do { \ 12501fb4b82SJames Morse __typeof__(x) _x = (x); \ 12601fb4b82SJames Morse _x->garbage.to_free = _x; \ 12701fb4b82SJames Morse llist_add(&_x->garbage.llist, &mpam_garbage); \ 12801fb4b82SJames Morse } while (0) 12901fb4b82SJames Morse 13001fb4b82SJames Morse static void mpam_free_garbage(void) 13101fb4b82SJames Morse { 13201fb4b82SJames Morse struct mpam_garbage *iter, *tmp; 13301fb4b82SJames Morse struct llist_node *to_free = llist_del_all(&mpam_garbage); 13401fb4b82SJames Morse 13501fb4b82SJames Morse if (!to_free) 13601fb4b82SJames Morse return; 13701fb4b82SJames Morse 13801fb4b82SJames Morse synchronize_srcu(&mpam_srcu); 13901fb4b82SJames Morse 14001fb4b82SJames Morse llist_for_each_entry_safe(iter, tmp, to_free, llist) { 14101fb4b82SJames Morse if (iter->pdev) 14201fb4b82SJames Morse devm_kfree(&iter->pdev->dev, iter->to_free); 14301fb4b82SJames Morse else 14401fb4b82SJames Morse kfree(iter->to_free); 14501fb4b82SJames Morse } 14601fb4b82SJames Morse } 14701fb4b82SJames Morse 148*09b89d2aSJames Morse /* 149*09b89d2aSJames Morse * Once mpam is enabled, new requestors cannot further reduce the available 150*09b89d2aSJames Morse * partid. Assert that the size is fixed, and new requestors will be turned 151*09b89d2aSJames Morse * away. 152*09b89d2aSJames Morse */ 153*09b89d2aSJames Morse static void mpam_assert_partid_sizes_fixed(void) 154*09b89d2aSJames Morse { 155*09b89d2aSJames Morse WARN_ON_ONCE(!partid_max_published); 156*09b89d2aSJames Morse } 157*09b89d2aSJames Morse 1588f8d0ac1SJames Morse static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) 1598f8d0ac1SJames Morse { 1608f8d0ac1SJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 1618f8d0ac1SJames Morse 1628f8d0ac1SJames Morse return readl_relaxed(msc->mapped_hwpage + reg); 1638f8d0ac1SJames Morse } 1648f8d0ac1SJames Morse 1658f8d0ac1SJames Morse static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) 1668f8d0ac1SJames Morse { 1678f8d0ac1SJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 1688f8d0ac1SJames Morse return __mpam_read_reg(msc, reg); 1698f8d0ac1SJames Morse } 1708f8d0ac1SJames Morse 1718f8d0ac1SJames Morse #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) 1728f8d0ac1SJames Morse 173bd221f9fSJames Morse static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) 174bd221f9fSJames Morse { 175bd221f9fSJames Morse WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); 176bd221f9fSJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 177bd221f9fSJames Morse 178bd221f9fSJames Morse writel_relaxed(val, msc->mapped_hwpage + reg); 179bd221f9fSJames Morse } 180bd221f9fSJames Morse 181bd221f9fSJames Morse static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 182bd221f9fSJames Morse { 183bd221f9fSJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 184bd221f9fSJames Morse __mpam_write_reg(msc, reg, val); 185bd221f9fSJames Morse } 186bd221f9fSJames Morse 187bd221f9fSJames Morse #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val) 188bd221f9fSJames Morse 1898c90dc68SJames Morse static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) 1908c90dc68SJames Morse { 1918c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 1928c90dc68SJames Morse return __mpam_read_reg(msc, reg); 1938c90dc68SJames Morse } 1948c90dc68SJames Morse 1958c90dc68SJames Morse #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg) 1968c90dc68SJames Morse 1978c90dc68SJames Morse static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 1988c90dc68SJames Morse { 1998c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 2008c90dc68SJames Morse __mpam_write_reg(msc, reg, val); 2018c90dc68SJames Morse } 2028c90dc68SJames Morse 2038c90dc68SJames Morse #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) 2048c90dc68SJames Morse 205bd221f9fSJames Morse static u64 mpam_msc_read_idr(struct mpam_msc *msc) 206bd221f9fSJames Morse { 207bd221f9fSJames Morse u64 idr_high = 0, idr_low; 208bd221f9fSJames Morse 209bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 210bd221f9fSJames Morse 211bd221f9fSJames Morse idr_low = mpam_read_partsel_reg(msc, IDR); 212bd221f9fSJames Morse if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) 213bd221f9fSJames Morse idr_high = mpam_read_partsel_reg(msc, IDR + 4); 214bd221f9fSJames Morse 215bd221f9fSJames Morse return (idr_high << 32) | idr_low; 216bd221f9fSJames Morse } 217bd221f9fSJames Morse 21849aa621cSJames Morse static void mpam_msc_clear_esr(struct mpam_msc *msc) 21949aa621cSJames Morse { 22049aa621cSJames Morse u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR); 22149aa621cSJames Morse 22249aa621cSJames Morse if (!esr_low) 22349aa621cSJames Morse return; 22449aa621cSJames Morse 22549aa621cSJames Morse /* 22649aa621cSJames Morse * Clearing the high/low bits of MPAMF_ESR can not be atomic. 22749aa621cSJames Morse * Clear the top half first, so that the pending error bits in the 22849aa621cSJames Morse * lower half prevent hardware from updating either half of the 22949aa621cSJames Morse * register. 23049aa621cSJames Morse */ 23149aa621cSJames Morse if (msc->has_extd_esr) 23249aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ESR + 4, 0); 23349aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ESR, 0); 23449aa621cSJames Morse } 23549aa621cSJames Morse 23649aa621cSJames Morse static u64 mpam_msc_read_esr(struct mpam_msc *msc) 23749aa621cSJames Morse { 23849aa621cSJames Morse u64 esr_high = 0, esr_low; 23949aa621cSJames Morse 24049aa621cSJames Morse esr_low = __mpam_read_reg(msc, MPAMF_ESR); 24149aa621cSJames Morse if (msc->has_extd_esr) 24249aa621cSJames Morse esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4); 24349aa621cSJames Morse 24449aa621cSJames Morse return (esr_high << 32) | esr_low; 24549aa621cSJames Morse } 24649aa621cSJames Morse 247bd221f9fSJames Morse static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) 248bd221f9fSJames Morse { 249bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 250bd221f9fSJames Morse 251bd221f9fSJames Morse mpam_write_partsel_reg(msc, PART_SEL, partsel); 252bd221f9fSJames Morse } 253bd221f9fSJames Morse 254bd221f9fSJames Morse static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) 255bd221f9fSJames Morse { 256bd221f9fSJames Morse u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | 257bd221f9fSJames Morse FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); 258bd221f9fSJames Morse 259bd221f9fSJames Morse __mpam_part_sel_raw(partsel, msc); 260bd221f9fSJames Morse } 261bd221f9fSJames Morse 262bd221f9fSJames Morse int mpam_register_requestor(u16 partid_max, u8 pmg_max) 263bd221f9fSJames Morse { 264bd221f9fSJames Morse guard(spinlock)(&partid_max_lock); 265bd221f9fSJames Morse if (!partid_max_init) { 266bd221f9fSJames Morse mpam_partid_max = partid_max; 267bd221f9fSJames Morse mpam_pmg_max = pmg_max; 268bd221f9fSJames Morse partid_max_init = true; 269bd221f9fSJames Morse } else if (!partid_max_published) { 270bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, partid_max); 271bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, pmg_max); 272bd221f9fSJames Morse } else { 273bd221f9fSJames Morse /* New requestors can't lower the values */ 274bd221f9fSJames Morse if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) 275bd221f9fSJames Morse return -EBUSY; 276bd221f9fSJames Morse } 277bd221f9fSJames Morse 278bd221f9fSJames Morse return 0; 279bd221f9fSJames Morse } 280bd221f9fSJames Morse EXPORT_SYMBOL(mpam_register_requestor); 281bd221f9fSJames Morse 28201fb4b82SJames Morse static struct mpam_class * 28301fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type) 28401fb4b82SJames Morse { 28501fb4b82SJames Morse struct mpam_class *class; 28601fb4b82SJames Morse 28701fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 28801fb4b82SJames Morse 28901fb4b82SJames Morse class = kzalloc(sizeof(*class), GFP_KERNEL); 29001fb4b82SJames Morse if (!class) 29101fb4b82SJames Morse return ERR_PTR(-ENOMEM); 29201fb4b82SJames Morse init_garbage(&class->garbage); 29301fb4b82SJames Morse 29401fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->components); 29501fb4b82SJames Morse /* Affinity is updated when ris are added */ 29601fb4b82SJames Morse class->level = level_idx; 29701fb4b82SJames Morse class->type = type; 29801fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->classes_list); 29901fb4b82SJames Morse 30001fb4b82SJames Morse list_add_rcu(&class->classes_list, &mpam_classes); 30101fb4b82SJames Morse 30201fb4b82SJames Morse return class; 30301fb4b82SJames Morse } 30401fb4b82SJames Morse 30501fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class) 30601fb4b82SJames Morse { 30701fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 30801fb4b82SJames Morse 30901fb4b82SJames Morse list_del_rcu(&class->classes_list); 31001fb4b82SJames Morse add_to_garbage(class); 31101fb4b82SJames Morse } 31201fb4b82SJames Morse 31301fb4b82SJames Morse static struct mpam_class * 31401fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type) 31501fb4b82SJames Morse { 31601fb4b82SJames Morse struct mpam_class *class; 31701fb4b82SJames Morse 31801fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 31901fb4b82SJames Morse 32001fb4b82SJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 32101fb4b82SJames Morse if (class->type == type && class->level == level_idx) 32201fb4b82SJames Morse return class; 32301fb4b82SJames Morse } 32401fb4b82SJames Morse 32501fb4b82SJames Morse return mpam_class_alloc(level_idx, type); 32601fb4b82SJames Morse } 32701fb4b82SJames Morse 32801fb4b82SJames Morse static struct mpam_component * 32901fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id) 33001fb4b82SJames Morse { 33101fb4b82SJames Morse struct mpam_component *comp; 33201fb4b82SJames Morse 33301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 33401fb4b82SJames Morse 33501fb4b82SJames Morse comp = kzalloc(sizeof(*comp), GFP_KERNEL); 33601fb4b82SJames Morse if (!comp) 33701fb4b82SJames Morse return ERR_PTR(-ENOMEM); 33801fb4b82SJames Morse init_garbage(&comp->garbage); 33901fb4b82SJames Morse 34001fb4b82SJames Morse comp->comp_id = id; 34101fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->vmsc); 34201fb4b82SJames Morse /* Affinity is updated when RIS are added */ 34301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->class_list); 34401fb4b82SJames Morse comp->class = class; 34501fb4b82SJames Morse 34601fb4b82SJames Morse list_add_rcu(&comp->class_list, &class->components); 34701fb4b82SJames Morse 34801fb4b82SJames Morse return comp; 34901fb4b82SJames Morse } 35001fb4b82SJames Morse 351*09b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp); 352*09b89d2aSJames Morse 35301fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp) 35401fb4b82SJames Morse { 35501fb4b82SJames Morse struct mpam_class *class = comp->class; 35601fb4b82SJames Morse 35701fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 35801fb4b82SJames Morse 359*09b89d2aSJames Morse __destroy_component_cfg(comp); 360*09b89d2aSJames Morse 36101fb4b82SJames Morse list_del_rcu(&comp->class_list); 36201fb4b82SJames Morse add_to_garbage(comp); 36301fb4b82SJames Morse 36401fb4b82SJames Morse if (list_empty(&class->components)) 36501fb4b82SJames Morse mpam_class_destroy(class); 36601fb4b82SJames Morse } 36701fb4b82SJames Morse 36801fb4b82SJames Morse static struct mpam_component * 36901fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id) 37001fb4b82SJames Morse { 37101fb4b82SJames Morse struct mpam_component *comp; 37201fb4b82SJames Morse 37301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 37401fb4b82SJames Morse 37501fb4b82SJames Morse list_for_each_entry(comp, &class->components, class_list) { 37601fb4b82SJames Morse if (comp->comp_id == id) 37701fb4b82SJames Morse return comp; 37801fb4b82SJames Morse } 37901fb4b82SJames Morse 38001fb4b82SJames Morse return mpam_component_alloc(class, id); 38101fb4b82SJames Morse } 38201fb4b82SJames Morse 38301fb4b82SJames Morse static struct mpam_vmsc * 38401fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) 38501fb4b82SJames Morse { 38601fb4b82SJames Morse struct mpam_vmsc *vmsc; 38701fb4b82SJames Morse 38801fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 38901fb4b82SJames Morse 39001fb4b82SJames Morse vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL); 39101fb4b82SJames Morse if (!vmsc) 39201fb4b82SJames Morse return ERR_PTR(-ENOMEM); 39301fb4b82SJames Morse init_garbage(&vmsc->garbage); 39401fb4b82SJames Morse 39501fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->ris); 39601fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->comp_list); 39701fb4b82SJames Morse vmsc->comp = comp; 39801fb4b82SJames Morse vmsc->msc = msc; 39901fb4b82SJames Morse 40001fb4b82SJames Morse list_add_rcu(&vmsc->comp_list, &comp->vmsc); 40101fb4b82SJames Morse 40201fb4b82SJames Morse return vmsc; 40301fb4b82SJames Morse } 40401fb4b82SJames Morse 40501fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) 40601fb4b82SJames Morse { 40701fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 40801fb4b82SJames Morse 40901fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 41001fb4b82SJames Morse 41101fb4b82SJames Morse list_del_rcu(&vmsc->comp_list); 41201fb4b82SJames Morse add_to_garbage(vmsc); 41301fb4b82SJames Morse 41401fb4b82SJames Morse if (list_empty(&comp->vmsc)) 41501fb4b82SJames Morse mpam_component_destroy(comp); 41601fb4b82SJames Morse } 41701fb4b82SJames Morse 41801fb4b82SJames Morse static struct mpam_vmsc * 41901fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) 42001fb4b82SJames Morse { 42101fb4b82SJames Morse struct mpam_vmsc *vmsc; 42201fb4b82SJames Morse 42301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 42401fb4b82SJames Morse 42501fb4b82SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 42601fb4b82SJames Morse if (vmsc->msc->id == msc->id) 42701fb4b82SJames Morse return vmsc; 42801fb4b82SJames Morse } 42901fb4b82SJames Morse 43001fb4b82SJames Morse return mpam_vmsc_alloc(comp, msc); 43101fb4b82SJames Morse } 43201fb4b82SJames Morse 43301fb4b82SJames Morse /* 43401fb4b82SJames Morse * The cacheinfo structures are only populated when CPUs are online. 43501fb4b82SJames Morse * This helper walks the acpi tables to include offline CPUs too. 43601fb4b82SJames Morse */ 43701fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 43801fb4b82SJames Morse cpumask_t *affinity) 43901fb4b82SJames Morse { 44001fb4b82SJames Morse return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); 44101fb4b82SJames Morse } 44201fb4b82SJames Morse 44301fb4b82SJames Morse /* 44401fb4b82SJames Morse * cpumask_of_node() only knows about online CPUs. This can't tell us whether 44501fb4b82SJames Morse * a class is represented on all possible CPUs. 44601fb4b82SJames Morse */ 44701fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) 44801fb4b82SJames Morse { 44901fb4b82SJames Morse int cpu; 45001fb4b82SJames Morse 45101fb4b82SJames Morse for_each_possible_cpu(cpu) { 45201fb4b82SJames Morse if (node_id == cpu_to_node(cpu)) 45301fb4b82SJames Morse cpumask_set_cpu(cpu, affinity); 45401fb4b82SJames Morse } 45501fb4b82SJames Morse } 45601fb4b82SJames Morse 45701fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, 45801fb4b82SJames Morse enum mpam_class_types type, 45901fb4b82SJames Morse struct mpam_class *class, 46001fb4b82SJames Morse struct mpam_component *comp) 46101fb4b82SJames Morse { 46201fb4b82SJames Morse int err; 46301fb4b82SJames Morse 46401fb4b82SJames Morse switch (type) { 46501fb4b82SJames Morse case MPAM_CLASS_CACHE: 46601fb4b82SJames Morse err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, 46701fb4b82SJames Morse affinity); 46801fb4b82SJames Morse if (err) { 46901fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, 47001fb4b82SJames Morse "Failed to determine CPU affinity\n"); 47101fb4b82SJames Morse return err; 47201fb4b82SJames Morse } 47301fb4b82SJames Morse 47401fb4b82SJames Morse if (cpumask_empty(affinity)) 47501fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n"); 47601fb4b82SJames Morse 47701fb4b82SJames Morse break; 47801fb4b82SJames Morse case MPAM_CLASS_MEMORY: 47901fb4b82SJames Morse get_cpumask_from_node_id(comp->comp_id, affinity); 48001fb4b82SJames Morse /* affinity may be empty for CPU-less memory nodes */ 48101fb4b82SJames Morse break; 48201fb4b82SJames Morse case MPAM_CLASS_UNKNOWN: 48301fb4b82SJames Morse return 0; 48401fb4b82SJames Morse } 48501fb4b82SJames Morse 48601fb4b82SJames Morse cpumask_and(affinity, affinity, &msc->accessibility); 48701fb4b82SJames Morse 48801fb4b82SJames Morse return 0; 48901fb4b82SJames Morse } 49001fb4b82SJames Morse 49101fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, 49201fb4b82SJames Morse enum mpam_class_types type, u8 class_id, 49301fb4b82SJames Morse int component_id) 49401fb4b82SJames Morse { 49501fb4b82SJames Morse int err; 49601fb4b82SJames Morse struct mpam_vmsc *vmsc; 49701fb4b82SJames Morse struct mpam_msc_ris *ris; 49801fb4b82SJames Morse struct mpam_class *class; 49901fb4b82SJames Morse struct mpam_component *comp; 50001fb4b82SJames Morse struct platform_device *pdev = msc->pdev; 50101fb4b82SJames Morse 50201fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 50301fb4b82SJames Morse 50401fb4b82SJames Morse if (ris_idx > MPAM_MSC_MAX_NUM_RIS) 50501fb4b82SJames Morse return -EINVAL; 50601fb4b82SJames Morse 50701fb4b82SJames Morse if (test_and_set_bit(ris_idx, &msc->ris_idxs)) 50801fb4b82SJames Morse return -EBUSY; 50901fb4b82SJames Morse 51001fb4b82SJames Morse ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL); 51101fb4b82SJames Morse if (!ris) 51201fb4b82SJames Morse return -ENOMEM; 51301fb4b82SJames Morse init_garbage(&ris->garbage); 51401fb4b82SJames Morse ris->garbage.pdev = pdev; 51501fb4b82SJames Morse 51601fb4b82SJames Morse class = mpam_class_find(class_id, type); 51701fb4b82SJames Morse if (IS_ERR(class)) 51801fb4b82SJames Morse return PTR_ERR(class); 51901fb4b82SJames Morse 52001fb4b82SJames Morse comp = mpam_component_find(class, component_id); 52101fb4b82SJames Morse if (IS_ERR(comp)) { 52201fb4b82SJames Morse if (list_empty(&class->components)) 52301fb4b82SJames Morse mpam_class_destroy(class); 52401fb4b82SJames Morse return PTR_ERR(comp); 52501fb4b82SJames Morse } 52601fb4b82SJames Morse 52701fb4b82SJames Morse vmsc = mpam_vmsc_find(comp, msc); 52801fb4b82SJames Morse if (IS_ERR(vmsc)) { 52901fb4b82SJames Morse if (list_empty(&comp->vmsc)) 53001fb4b82SJames Morse mpam_component_destroy(comp); 53101fb4b82SJames Morse return PTR_ERR(vmsc); 53201fb4b82SJames Morse } 53301fb4b82SJames Morse 53401fb4b82SJames Morse err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); 53501fb4b82SJames Morse if (err) { 53601fb4b82SJames Morse if (list_empty(&vmsc->ris)) 53701fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 53801fb4b82SJames Morse return err; 53901fb4b82SJames Morse } 54001fb4b82SJames Morse 54101fb4b82SJames Morse ris->ris_idx = ris_idx; 54201fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->msc_list); 54301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->vmsc_list); 54401fb4b82SJames Morse ris->vmsc = vmsc; 54501fb4b82SJames Morse 54601fb4b82SJames Morse cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); 54701fb4b82SJames Morse cpumask_or(&class->affinity, &class->affinity, &ris->affinity); 54801fb4b82SJames Morse list_add_rcu(&ris->vmsc_list, &vmsc->ris); 54901fb4b82SJames Morse list_add_rcu(&ris->msc_list, &msc->ris); 55001fb4b82SJames Morse 55101fb4b82SJames Morse return 0; 55201fb4b82SJames Morse } 55301fb4b82SJames Morse 55401fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris) 55501fb4b82SJames Morse { 55601fb4b82SJames Morse struct mpam_vmsc *vmsc = ris->vmsc; 55701fb4b82SJames Morse struct mpam_msc *msc = vmsc->msc; 55801fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 55901fb4b82SJames Morse struct mpam_class *class = comp->class; 56001fb4b82SJames Morse 56101fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 56201fb4b82SJames Morse 56301fb4b82SJames Morse /* 56401fb4b82SJames Morse * It is assumed affinities don't overlap. If they do the class becomes 56501fb4b82SJames Morse * unusable immediately. 56601fb4b82SJames Morse */ 56701fb4b82SJames Morse cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); 56801fb4b82SJames Morse cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); 56901fb4b82SJames Morse clear_bit(ris->ris_idx, &msc->ris_idxs); 57001fb4b82SJames Morse list_del_rcu(&ris->msc_list); 57101fb4b82SJames Morse list_del_rcu(&ris->vmsc_list); 57201fb4b82SJames Morse add_to_garbage(ris); 57301fb4b82SJames Morse 57401fb4b82SJames Morse if (list_empty(&vmsc->ris)) 57501fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 57601fb4b82SJames Morse } 57701fb4b82SJames Morse 57801fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 57901fb4b82SJames Morse enum mpam_class_types type, u8 class_id, int component_id) 58001fb4b82SJames Morse { 58101fb4b82SJames Morse int err; 58201fb4b82SJames Morse 58301fb4b82SJames Morse mutex_lock(&mpam_list_lock); 58401fb4b82SJames Morse err = mpam_ris_create_locked(msc, ris_idx, type, class_id, 58501fb4b82SJames Morse component_id); 58601fb4b82SJames Morse mutex_unlock(&mpam_list_lock); 58701fb4b82SJames Morse if (err) 58801fb4b82SJames Morse mpam_free_garbage(); 58901fb4b82SJames Morse 59001fb4b82SJames Morse return err; 59101fb4b82SJames Morse } 59201fb4b82SJames Morse 593bd221f9fSJames Morse static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc, 594bd221f9fSJames Morse u8 ris_idx) 595bd221f9fSJames Morse { 596bd221f9fSJames Morse int err; 597bd221f9fSJames Morse struct mpam_msc_ris *ris; 598bd221f9fSJames Morse 599bd221f9fSJames Morse lockdep_assert_held(&mpam_list_lock); 600bd221f9fSJames Morse 601bd221f9fSJames Morse if (!test_bit(ris_idx, &msc->ris_idxs)) { 602bd221f9fSJames Morse err = mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN, 603bd221f9fSJames Morse 0, 0); 604bd221f9fSJames Morse if (err) 605bd221f9fSJames Morse return ERR_PTR(err); 606bd221f9fSJames Morse } 607bd221f9fSJames Morse 608bd221f9fSJames Morse list_for_each_entry(ris, &msc->ris, msc_list) { 609bd221f9fSJames Morse if (ris->ris_idx == ris_idx) 610bd221f9fSJames Morse return ris; 611bd221f9fSJames Morse } 612bd221f9fSJames Morse 613bd221f9fSJames Morse return ERR_PTR(-ENOENT); 614bd221f9fSJames Morse } 615bd221f9fSJames Morse 6168c90dc68SJames Morse /* 6178c90dc68SJames Morse * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour 6188c90dc68SJames Morse * of NRDY, software can use this bit for any purpose" - so hardware might not 6198c90dc68SJames Morse * implement this - but it isn't RES0. 6208c90dc68SJames Morse * 6218c90dc68SJames Morse * Try and see what values stick in this bit. If we can write either value, 6228c90dc68SJames Morse * its probably not implemented by hardware. 6238c90dc68SJames Morse */ 6248c90dc68SJames Morse static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg) 6258c90dc68SJames Morse { 6268c90dc68SJames Morse u32 now; 6278c90dc68SJames Morse u64 mon_sel; 6288c90dc68SJames Morse bool can_set, can_clear; 6298c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 6308c90dc68SJames Morse 6318c90dc68SJames Morse if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc))) 6328c90dc68SJames Morse return false; 6338c90dc68SJames Morse 6348c90dc68SJames Morse mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | 6358c90dc68SJames Morse FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); 6368c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, mon_sel); 6378c90dc68SJames Morse 6388c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); 6398c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 6408c90dc68SJames Morse can_set = now & MSMON___NRDY; 6418c90dc68SJames Morse 6428c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, 0); 6438c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 6448c90dc68SJames Morse can_clear = !(now & MSMON___NRDY); 6458c90dc68SJames Morse mpam_mon_sel_unlock(msc); 6468c90dc68SJames Morse 6478c90dc68SJames Morse return (!can_set || !can_clear); 6488c90dc68SJames Morse } 6498c90dc68SJames Morse 6508c90dc68SJames Morse #define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ 6518c90dc68SJames Morse _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) 6528c90dc68SJames Morse 6538c90dc68SJames Morse static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) 6548c90dc68SJames Morse { 6558c90dc68SJames Morse int err; 6568c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 6578c90dc68SJames Morse struct device *dev = &msc->pdev->dev; 6588c90dc68SJames Morse struct mpam_props *props = &ris->props; 6598c90dc68SJames Morse 6608c90dc68SJames Morse lockdep_assert_held(&msc->probe_lock); 6618c90dc68SJames Morse lockdep_assert_held(&msc->part_sel_lock); 6628c90dc68SJames Morse 6638c90dc68SJames Morse /* Cache Portion partitioning */ 6648c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { 6658c90dc68SJames Morse u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR); 6668c90dc68SJames Morse 6678c90dc68SJames Morse props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); 6688c90dc68SJames Morse if (props->cpbm_wd) 6698c90dc68SJames Morse mpam_set_feature(mpam_feat_cpor_part, props); 6708c90dc68SJames Morse } 6718c90dc68SJames Morse 6728c90dc68SJames Morse /* Memory bandwidth partitioning */ 6738c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) { 6748c90dc68SJames Morse u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR); 6758c90dc68SJames Morse 6768c90dc68SJames Morse /* portion bitmap resolution */ 6778c90dc68SJames Morse props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features); 6788c90dc68SJames Morse if (props->mbw_pbm_bits && 6798c90dc68SJames Morse FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features)) 6808c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_part, props); 6818c90dc68SJames Morse 6828c90dc68SJames Morse props->bwa_wd = FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); 6838c90dc68SJames Morse if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) 6848c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_max, props); 6858c90dc68SJames Morse } 6868c90dc68SJames Morse 6878c90dc68SJames Morse /* Performance Monitoring */ 6888c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) { 6898c90dc68SJames Morse u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR); 6908c90dc68SJames Morse 6918c90dc68SJames Morse /* 6928c90dc68SJames Morse * If the firmware max-nrdy-us property is missing, the 6938c90dc68SJames Morse * CSU counters can't be used. Should we wait forever? 6948c90dc68SJames Morse */ 6958c90dc68SJames Morse err = device_property_read_u32(&msc->pdev->dev, 6968c90dc68SJames Morse "arm,not-ready-us", 6978c90dc68SJames Morse &msc->nrdy_usec); 6988c90dc68SJames Morse 6998c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) { 7008c90dc68SJames Morse u32 csumonidr; 7018c90dc68SJames Morse 7028c90dc68SJames Morse csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR); 7038c90dc68SJames Morse props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr); 7048c90dc68SJames Morse if (props->num_csu_mon) { 7058c90dc68SJames Morse bool hw_managed; 7068c90dc68SJames Morse 7078c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu, props); 7088c90dc68SJames Morse 7098c90dc68SJames Morse /* Is NRDY hardware managed? */ 7108c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU); 7118c90dc68SJames Morse if (hw_managed) 7128c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); 7138c90dc68SJames Morse } 7148c90dc68SJames Morse 7158c90dc68SJames Morse /* 7168c90dc68SJames Morse * Accept the missing firmware property if NRDY appears 7178c90dc68SJames Morse * un-implemented. 7188c90dc68SJames Morse */ 7198c90dc68SJames Morse if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) 7208c90dc68SJames Morse dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware."); 7218c90dc68SJames Morse } 7228c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { 7238c90dc68SJames Morse bool hw_managed; 7248c90dc68SJames Morse u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR); 7258c90dc68SJames Morse 7268c90dc68SJames Morse props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr); 7278c90dc68SJames Morse if (props->num_mbwu_mon) 7288c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu, props); 7298c90dc68SJames Morse 7308c90dc68SJames Morse /* Is NRDY hardware managed? */ 7318c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU); 7328c90dc68SJames Morse if (hw_managed) 7338c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); 7348c90dc68SJames Morse 7358c90dc68SJames Morse /* 7368c90dc68SJames Morse * Don't warn about any missing firmware property for 7378c90dc68SJames Morse * MBWU NRDY - it doesn't make any sense! 7388c90dc68SJames Morse */ 7398c90dc68SJames Morse } 7408c90dc68SJames Morse } 7418c90dc68SJames Morse } 7428c90dc68SJames Morse 7438f8d0ac1SJames Morse static int mpam_msc_hw_probe(struct mpam_msc *msc) 7448f8d0ac1SJames Morse { 7458f8d0ac1SJames Morse u64 idr; 746bd221f9fSJames Morse u16 partid_max; 747bd221f9fSJames Morse u8 ris_idx, pmg_max; 748bd221f9fSJames Morse struct mpam_msc_ris *ris; 7498f8d0ac1SJames Morse struct device *dev = &msc->pdev->dev; 7508f8d0ac1SJames Morse 7518f8d0ac1SJames Morse lockdep_assert_held(&msc->probe_lock); 7528f8d0ac1SJames Morse 7538f8d0ac1SJames Morse idr = __mpam_read_reg(msc, MPAMF_AIDR); 7548f8d0ac1SJames Morse if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) { 7558f8d0ac1SJames Morse dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n"); 7568f8d0ac1SJames Morse return -EIO; 7578f8d0ac1SJames Morse } 7588f8d0ac1SJames Morse 759bd221f9fSJames Morse /* Grab an IDR value to find out how many RIS there are */ 760bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 761bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 762bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 763bd221f9fSJames Morse 764bd221f9fSJames Morse msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr); 765bd221f9fSJames Morse 766bd221f9fSJames Morse /* Use these values so partid/pmg always starts with a valid value */ 767bd221f9fSJames Morse msc->partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 768bd221f9fSJames Morse msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 769bd221f9fSJames Morse 770bd221f9fSJames Morse for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) { 771bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 772bd221f9fSJames Morse __mpam_part_sel(ris_idx, 0, msc); 773bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 774bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 775bd221f9fSJames Morse 776bd221f9fSJames Morse partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 777bd221f9fSJames Morse pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 778bd221f9fSJames Morse msc->partid_max = min(msc->partid_max, partid_max); 779bd221f9fSJames Morse msc->pmg_max = min(msc->pmg_max, pmg_max); 78049aa621cSJames Morse msc->has_extd_esr = FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr); 781bd221f9fSJames Morse 782bd221f9fSJames Morse mutex_lock(&mpam_list_lock); 783bd221f9fSJames Morse ris = mpam_get_or_create_ris(msc, ris_idx); 784bd221f9fSJames Morse mutex_unlock(&mpam_list_lock); 785bd221f9fSJames Morse if (IS_ERR(ris)) 786bd221f9fSJames Morse return PTR_ERR(ris); 7878c90dc68SJames Morse ris->idr = idr; 7888c90dc68SJames Morse 7898c90dc68SJames Morse mutex_lock(&msc->part_sel_lock); 7908c90dc68SJames Morse __mpam_part_sel(ris_idx, 0, msc); 7918c90dc68SJames Morse mpam_ris_hw_probe(ris); 7928c90dc68SJames Morse mutex_unlock(&msc->part_sel_lock); 793bd221f9fSJames Morse } 794bd221f9fSJames Morse 79549aa621cSJames Morse /* Clear any stale errors */ 79649aa621cSJames Morse mpam_msc_clear_esr(msc); 79749aa621cSJames Morse 798bd221f9fSJames Morse spin_lock(&partid_max_lock); 799bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, msc->partid_max); 800bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, msc->pmg_max); 801bd221f9fSJames Morse spin_unlock(&partid_max_lock); 802bd221f9fSJames Morse 8038f8d0ac1SJames Morse msc->probed = true; 8048f8d0ac1SJames Morse 8058f8d0ac1SJames Morse return 0; 8068f8d0ac1SJames Morse } 8078f8d0ac1SJames Morse 808f188a36cSJames Morse static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) 809f188a36cSJames Morse { 810f188a36cSJames Morse u32 num_words, msb; 811f188a36cSJames Morse u32 bm = ~0; 812f188a36cSJames Morse int i; 813f188a36cSJames Morse 814f188a36cSJames Morse lockdep_assert_held(&msc->part_sel_lock); 815f188a36cSJames Morse 816f188a36cSJames Morse if (wd == 0) 817f188a36cSJames Morse return; 818f188a36cSJames Morse 819f188a36cSJames Morse /* 820f188a36cSJames Morse * Write all ~0 to all but the last 32bit-word, which may 821f188a36cSJames Morse * have fewer bits... 822f188a36cSJames Morse */ 823f188a36cSJames Morse num_words = DIV_ROUND_UP(wd, 32); 824f188a36cSJames Morse for (i = 0; i < num_words - 1; i++, reg += sizeof(bm)) 825f188a36cSJames Morse __mpam_write_reg(msc, reg, bm); 826f188a36cSJames Morse 827f188a36cSJames Morse /* 828f188a36cSJames Morse * ....and then the last (maybe) partial 32bit word. When wd is a 829f188a36cSJames Morse * multiple of 32, msb should be 31 to write a full 32bit word. 830f188a36cSJames Morse */ 831f188a36cSJames Morse msb = (wd - 1) % 32; 832f188a36cSJames Morse bm = GENMASK(msb, 0); 833f188a36cSJames Morse __mpam_write_reg(msc, reg, bm); 834f188a36cSJames Morse } 835f188a36cSJames Morse 836*09b89d2aSJames Morse /* Called via IPI. Call while holding an SRCU reference */ 837*09b89d2aSJames Morse static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, 838*09b89d2aSJames Morse struct mpam_config *cfg) 839f188a36cSJames Morse { 840f188a36cSJames Morse struct mpam_msc *msc = ris->vmsc->msc; 841f188a36cSJames Morse struct mpam_props *rprops = &ris->props; 842f188a36cSJames Morse 843f188a36cSJames Morse mutex_lock(&msc->part_sel_lock); 844f188a36cSJames Morse __mpam_part_sel(ris->ris_idx, partid, msc); 845f188a36cSJames Morse 846*09b89d2aSJames Morse if (mpam_has_feature(mpam_feat_cpor_part, rprops) && 847*09b89d2aSJames Morse mpam_has_feature(mpam_feat_cpor_part, cfg)) { 848*09b89d2aSJames Morse if (cfg->reset_cpbm) 849f188a36cSJames Morse mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); 850*09b89d2aSJames Morse else 851*09b89d2aSJames Morse mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); 852*09b89d2aSJames Morse } 853f188a36cSJames Morse 854*09b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_part, rprops) && 855*09b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_part, cfg)) { 856*09b89d2aSJames Morse if (cfg->reset_mbw_pbm) 857f188a36cSJames Morse mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); 858*09b89d2aSJames Morse else 859*09b89d2aSJames Morse mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); 860*09b89d2aSJames Morse } 861f188a36cSJames Morse 862*09b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_min, rprops) && 863*09b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_min, cfg)) 864f188a36cSJames Morse mpam_write_partsel_reg(msc, MBW_MIN, 0); 865f188a36cSJames Morse 866*09b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_max, rprops) && 867*09b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_max, cfg)) { 868*09b89d2aSJames Morse if (cfg->reset_mbw_max) 869f188a36cSJames Morse mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX); 870*09b89d2aSJames Morse else 871*09b89d2aSJames Morse mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max); 872*09b89d2aSJames Morse } 873f188a36cSJames Morse 874f188a36cSJames Morse mutex_unlock(&msc->part_sel_lock); 875f188a36cSJames Morse } 876f188a36cSJames Morse 877*09b89d2aSJames Morse static void mpam_init_reset_cfg(struct mpam_config *reset_cfg) 878*09b89d2aSJames Morse { 879*09b89d2aSJames Morse *reset_cfg = (struct mpam_config) { 880*09b89d2aSJames Morse .reset_cpbm = true, 881*09b89d2aSJames Morse .reset_mbw_pbm = true, 882*09b89d2aSJames Morse .reset_mbw_max = true, 883*09b89d2aSJames Morse }; 884*09b89d2aSJames Morse bitmap_fill(reset_cfg->features, MPAM_FEATURE_LAST); 885*09b89d2aSJames Morse } 886*09b89d2aSJames Morse 887475228d1SJames Morse /* 888475228d1SJames Morse * Called via smp_call_on_cpu() to prevent migration, while still being 8893bd04fe7SJames Morse * pre-emptible. Caller must hold mpam_srcu. 890475228d1SJames Morse */ 891475228d1SJames Morse static int mpam_reset_ris(void *arg) 892f188a36cSJames Morse { 893f188a36cSJames Morse u16 partid, partid_max; 894*09b89d2aSJames Morse struct mpam_config reset_cfg; 895475228d1SJames Morse struct mpam_msc_ris *ris = arg; 896f188a36cSJames Morse 897f188a36cSJames Morse if (ris->in_reset_state) 898475228d1SJames Morse return 0; 899f188a36cSJames Morse 900*09b89d2aSJames Morse mpam_init_reset_cfg(&reset_cfg); 901*09b89d2aSJames Morse 902f188a36cSJames Morse spin_lock(&partid_max_lock); 903f188a36cSJames Morse partid_max = mpam_partid_max; 904f188a36cSJames Morse spin_unlock(&partid_max_lock); 905f188a36cSJames Morse for (partid = 0; partid <= partid_max; partid++) 906*09b89d2aSJames Morse mpam_reprogram_ris_partid(ris, partid, &reset_cfg); 907475228d1SJames Morse 908475228d1SJames Morse return 0; 909475228d1SJames Morse } 910475228d1SJames Morse 911475228d1SJames Morse /* 912475228d1SJames Morse * Get the preferred CPU for this MSC. If it is accessible from this CPU, 913475228d1SJames Morse * this CPU is preferred. This can be preempted/migrated, it will only result 914475228d1SJames Morse * in more work. 915475228d1SJames Morse */ 916475228d1SJames Morse static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc) 917475228d1SJames Morse { 918475228d1SJames Morse int cpu = raw_smp_processor_id(); 919475228d1SJames Morse 920475228d1SJames Morse if (cpumask_test_cpu(cpu, &msc->accessibility)) 921475228d1SJames Morse return cpu; 922475228d1SJames Morse 923475228d1SJames Morse return cpumask_first_and(&msc->accessibility, cpu_online_mask); 924475228d1SJames Morse } 925475228d1SJames Morse 926475228d1SJames Morse static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *arg) 927475228d1SJames Morse { 928475228d1SJames Morse lockdep_assert_irqs_enabled(); 929475228d1SJames Morse lockdep_assert_cpus_held(); 930475228d1SJames Morse WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu))); 931475228d1SJames Morse 932475228d1SJames Morse return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true); 933f188a36cSJames Morse } 934f188a36cSJames Morse 935*09b89d2aSJames Morse struct mpam_write_config_arg { 936f188a36cSJames Morse struct mpam_msc_ris *ris; 937*09b89d2aSJames Morse struct mpam_component *comp; 938*09b89d2aSJames Morse u16 partid; 939*09b89d2aSJames Morse }; 940f188a36cSJames Morse 941*09b89d2aSJames Morse static int __write_config(void *arg) 942*09b89d2aSJames Morse { 943*09b89d2aSJames Morse struct mpam_write_config_arg *c = arg; 944*09b89d2aSJames Morse 945*09b89d2aSJames Morse mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]); 946*09b89d2aSJames Morse 947*09b89d2aSJames Morse return 0; 948*09b89d2aSJames Morse } 949*09b89d2aSJames Morse 950*09b89d2aSJames Morse static void mpam_reprogram_msc(struct mpam_msc *msc) 951*09b89d2aSJames Morse { 952*09b89d2aSJames Morse u16 partid; 953*09b89d2aSJames Morse bool reset; 954*09b89d2aSJames Morse struct mpam_config *cfg; 955*09b89d2aSJames Morse struct mpam_msc_ris *ris; 956*09b89d2aSJames Morse struct mpam_write_config_arg arg; 957f188a36cSJames Morse 958f188a36cSJames Morse /* 959*09b89d2aSJames Morse * No lock for mpam_partid_max as partid_max_published has been 960*09b89d2aSJames Morse * set by mpam_enabled(), so the values can no longer change. 961f188a36cSJames Morse */ 962*09b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 963*09b89d2aSJames Morse 964*09b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 965*09b89d2aSJames Morse list_for_each_entry_srcu(ris, &msc->ris, msc_list, 966*09b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 967*09b89d2aSJames Morse if (!mpam_is_enabled() && !ris->in_reset_state) { 968*09b89d2aSJames Morse mpam_touch_msc(msc, &mpam_reset_ris, ris); 969*09b89d2aSJames Morse ris->in_reset_state = true; 970*09b89d2aSJames Morse continue; 971f188a36cSJames Morse } 972*09b89d2aSJames Morse 973*09b89d2aSJames Morse arg.comp = ris->vmsc->comp; 974*09b89d2aSJames Morse arg.ris = ris; 975*09b89d2aSJames Morse reset = true; 976*09b89d2aSJames Morse for (partid = 0; partid <= mpam_partid_max; partid++) { 977*09b89d2aSJames Morse cfg = &ris->vmsc->comp->cfg[partid]; 978*09b89d2aSJames Morse if (!bitmap_empty(cfg->features, MPAM_FEATURE_LAST)) 979*09b89d2aSJames Morse reset = false; 980*09b89d2aSJames Morse 981*09b89d2aSJames Morse arg.partid = partid; 982*09b89d2aSJames Morse mpam_touch_msc(msc, __write_config, &arg); 983*09b89d2aSJames Morse } 984*09b89d2aSJames Morse ris->in_reset_state = reset; 985*09b89d2aSJames Morse } 986*09b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 987f188a36cSJames Morse } 988f188a36cSJames Morse 98949aa621cSJames Morse static void _enable_percpu_irq(void *_irq) 99049aa621cSJames Morse { 99149aa621cSJames Morse int *irq = _irq; 99249aa621cSJames Morse 99349aa621cSJames Morse enable_percpu_irq(*irq, IRQ_TYPE_NONE); 99449aa621cSJames Morse } 99549aa621cSJames Morse 9968f8d0ac1SJames Morse static int mpam_cpu_online(unsigned int cpu) 9978f8d0ac1SJames Morse { 998f188a36cSJames Morse struct mpam_msc *msc; 999f188a36cSJames Morse 1000f188a36cSJames Morse guard(srcu)(&mpam_srcu); 1001f188a36cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 1002f188a36cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1003f188a36cSJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 1004f188a36cSJames Morse continue; 1005f188a36cSJames Morse 100649aa621cSJames Morse if (msc->reenable_error_ppi) 100749aa621cSJames Morse _enable_percpu_irq(&msc->reenable_error_ppi); 100849aa621cSJames Morse 1009f188a36cSJames Morse if (atomic_fetch_inc(&msc->online_refs) == 0) 1010*09b89d2aSJames Morse mpam_reprogram_msc(msc); 1011f188a36cSJames Morse } 1012f188a36cSJames Morse 10138f8d0ac1SJames Morse return 0; 10148f8d0ac1SJames Morse } 10158f8d0ac1SJames Morse 10168f8d0ac1SJames Morse /* Before mpam is enabled, try to probe new MSC */ 10178f8d0ac1SJames Morse static int mpam_discovery_cpu_online(unsigned int cpu) 10188f8d0ac1SJames Morse { 10198f8d0ac1SJames Morse int err = 0; 10208f8d0ac1SJames Morse struct mpam_msc *msc; 10218f8d0ac1SJames Morse bool new_device_probed = false; 10228f8d0ac1SJames Morse 10233796f75aSJames Morse if (mpam_is_enabled()) 10243796f75aSJames Morse return 0; 10253796f75aSJames Morse 10268f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 10278f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 10288f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 10298f8d0ac1SJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 10308f8d0ac1SJames Morse continue; 10318f8d0ac1SJames Morse 10328f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 10338f8d0ac1SJames Morse if (!msc->probed) 10348f8d0ac1SJames Morse err = mpam_msc_hw_probe(msc); 10358f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 10368f8d0ac1SJames Morse 10378f8d0ac1SJames Morse if (err) 10388f8d0ac1SJames Morse break; 10398f8d0ac1SJames Morse new_device_probed = true; 10408f8d0ac1SJames Morse } 10418f8d0ac1SJames Morse 10428f8d0ac1SJames Morse if (new_device_probed && !err) 10438f8d0ac1SJames Morse schedule_work(&mpam_enable_work); 10448f8d0ac1SJames Morse if (err) { 10458f8d0ac1SJames Morse mpam_disable_reason = "error during probing"; 10468f8d0ac1SJames Morse schedule_work(&mpam_broken_work); 10478f8d0ac1SJames Morse } 10488f8d0ac1SJames Morse 10498f8d0ac1SJames Morse return err; 10508f8d0ac1SJames Morse } 10518f8d0ac1SJames Morse 10528f8d0ac1SJames Morse static int mpam_cpu_offline(unsigned int cpu) 10538f8d0ac1SJames Morse { 1054f188a36cSJames Morse struct mpam_msc *msc; 1055f188a36cSJames Morse 1056f188a36cSJames Morse guard(srcu)(&mpam_srcu); 1057f188a36cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 1058f188a36cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1059f188a36cSJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 1060f188a36cSJames Morse continue; 1061f188a36cSJames Morse 106249aa621cSJames Morse if (msc->reenable_error_ppi) 106349aa621cSJames Morse disable_percpu_irq(msc->reenable_error_ppi); 106449aa621cSJames Morse 1065*09b89d2aSJames Morse if (atomic_dec_and_test(&msc->online_refs)) { 1066*09b89d2aSJames Morse struct mpam_msc_ris *ris; 1067*09b89d2aSJames Morse 1068*09b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 1069*09b89d2aSJames Morse list_for_each_entry_srcu(ris, &msc->ris, msc_list, 1070*09b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1071*09b89d2aSJames Morse mpam_touch_msc(msc, &mpam_reset_ris, ris); 1072*09b89d2aSJames Morse 1073*09b89d2aSJames Morse /* 1074*09b89d2aSJames Morse * The reset state for non-zero partid may be 1075*09b89d2aSJames Morse * lost while the CPUs are offline. 1076*09b89d2aSJames Morse */ 1077*09b89d2aSJames Morse ris->in_reset_state = false; 1078*09b89d2aSJames Morse } 1079*09b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 1080*09b89d2aSJames Morse } 1081f188a36cSJames Morse } 1082f188a36cSJames Morse 10838f8d0ac1SJames Morse return 0; 10848f8d0ac1SJames Morse } 10858f8d0ac1SJames Morse 10868f8d0ac1SJames Morse static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online), 10878f8d0ac1SJames Morse int (*offline)(unsigned int offline), 10888f8d0ac1SJames Morse char *name) 10898f8d0ac1SJames Morse { 10908f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 10918f8d0ac1SJames Morse if (mpam_cpuhp_state) { 10928f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 10938f8d0ac1SJames Morse mpam_cpuhp_state = 0; 10948f8d0ac1SJames Morse } 10958f8d0ac1SJames Morse 10968f8d0ac1SJames Morse mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online, 10978f8d0ac1SJames Morse offline); 10988f8d0ac1SJames Morse if (mpam_cpuhp_state <= 0) { 10998f8d0ac1SJames Morse pr_err("Failed to register cpuhp callbacks"); 11008f8d0ac1SJames Morse mpam_cpuhp_state = 0; 11018f8d0ac1SJames Morse } 11028f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 11038f8d0ac1SJames Morse } 11048f8d0ac1SJames Morse 110549aa621cSJames Morse static int __setup_ppi(struct mpam_msc *msc) 110649aa621cSJames Morse { 110749aa621cSJames Morse int cpu; 110849aa621cSJames Morse 110949aa621cSJames Morse msc->error_dev_id = alloc_percpu(struct mpam_msc *); 111049aa621cSJames Morse if (!msc->error_dev_id) 111149aa621cSJames Morse return -ENOMEM; 111249aa621cSJames Morse 111349aa621cSJames Morse for_each_cpu(cpu, &msc->accessibility) 111449aa621cSJames Morse *per_cpu_ptr(msc->error_dev_id, cpu) = msc; 111549aa621cSJames Morse 111649aa621cSJames Morse return 0; 111749aa621cSJames Morse } 111849aa621cSJames Morse 111949aa621cSJames Morse static int mpam_msc_setup_error_irq(struct mpam_msc *msc) 112049aa621cSJames Morse { 112149aa621cSJames Morse int irq; 112249aa621cSJames Morse 112349aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 112449aa621cSJames Morse if (irq <= 0) 112549aa621cSJames Morse return 0; 112649aa621cSJames Morse 112749aa621cSJames Morse /* Allocate and initialise the percpu device pointer for PPI */ 112849aa621cSJames Morse if (irq_is_percpu(irq)) 112949aa621cSJames Morse return __setup_ppi(msc); 113049aa621cSJames Morse 113149aa621cSJames Morse /* sanity check: shared interrupts can be routed anywhere? */ 113249aa621cSJames Morse if (!cpumask_equal(&msc->accessibility, cpu_possible_mask)) { 113349aa621cSJames Morse pr_err_once("msc:%u is a private resource with a shared error interrupt", 113449aa621cSJames Morse msc->id); 113549aa621cSJames Morse return -EINVAL; 113649aa621cSJames Morse } 113749aa621cSJames Morse 113849aa621cSJames Morse return 0; 113949aa621cSJames Morse } 114049aa621cSJames Morse 114101fb4b82SJames Morse /* 1142f04046f2SJames Morse * An MSC can control traffic from a set of CPUs, but may only be accessible 1143f04046f2SJames Morse * from a (hopefully wider) set of CPUs. The common reason for this is power 1144f04046f2SJames Morse * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 1145f04046f2SJames Morse * corresponding cache may also be powered off. By making accesses from 1146f04046f2SJames Morse * one of those CPUs, we ensure we don't access a cache that's powered off. 1147f04046f2SJames Morse */ 1148f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc) 1149f04046f2SJames Morse { 1150f04046f2SJames Morse u32 affinity_id; 1151f04046f2SJames Morse int err; 1152f04046f2SJames Morse 1153f04046f2SJames Morse err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 1154f04046f2SJames Morse &affinity_id); 1155f04046f2SJames Morse if (err) 1156f04046f2SJames Morse cpumask_copy(&msc->accessibility, cpu_possible_mask); 1157f04046f2SJames Morse else 1158f04046f2SJames Morse acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 1159f04046f2SJames Morse } 1160f04046f2SJames Morse 116101fb4b82SJames Morse /* 116201fb4b82SJames Morse * There are two ways of reaching a struct mpam_msc_ris. Via the 116301fb4b82SJames Morse * class->component->vmsc->ris, or via the msc. 116401fb4b82SJames Morse * When destroying the msc, the other side needs unlinking and cleaning up too. 116501fb4b82SJames Morse */ 1166f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc) 1167f04046f2SJames Morse { 1168f04046f2SJames Morse struct platform_device *pdev = msc->pdev; 116901fb4b82SJames Morse struct mpam_msc_ris *ris, *tmp; 1170f04046f2SJames Morse 1171f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 1172f04046f2SJames Morse 117301fb4b82SJames Morse list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) 117401fb4b82SJames Morse mpam_ris_destroy(ris); 117501fb4b82SJames Morse 1176f04046f2SJames Morse list_del_rcu(&msc->all_msc_list); 1177f04046f2SJames Morse platform_set_drvdata(pdev, NULL); 117801fb4b82SJames Morse 117901fb4b82SJames Morse add_to_garbage(msc); 1180f04046f2SJames Morse } 1181f04046f2SJames Morse 1182f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev) 1183f04046f2SJames Morse { 1184f04046f2SJames Morse struct mpam_msc *msc = platform_get_drvdata(pdev); 1185f04046f2SJames Morse 1186f04046f2SJames Morse mutex_lock(&mpam_list_lock); 1187f04046f2SJames Morse mpam_msc_destroy(msc); 1188f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 1189f04046f2SJames Morse 119001fb4b82SJames Morse mpam_free_garbage(); 1191f04046f2SJames Morse } 1192f04046f2SJames Morse 1193f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 1194f04046f2SJames Morse { 1195f04046f2SJames Morse int err; 1196f04046f2SJames Morse u32 tmp; 1197f04046f2SJames Morse struct mpam_msc *msc; 1198f04046f2SJames Morse struct resource *msc_res; 1199f04046f2SJames Morse struct device *dev = &pdev->dev; 1200f04046f2SJames Morse 1201f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 1202f04046f2SJames Morse 1203f04046f2SJames Morse msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 1204f04046f2SJames Morse if (!msc) 1205f04046f2SJames Morse return ERR_PTR(-ENOMEM); 120601fb4b82SJames Morse init_garbage(&msc->garbage); 120701fb4b82SJames Morse msc->garbage.pdev = pdev; 1208f04046f2SJames Morse 1209f04046f2SJames Morse err = devm_mutex_init(dev, &msc->probe_lock); 1210f04046f2SJames Morse if (err) 1211f04046f2SJames Morse return ERR_PTR(err); 1212f04046f2SJames Morse 1213f04046f2SJames Morse err = devm_mutex_init(dev, &msc->part_sel_lock); 1214f04046f2SJames Morse if (err) 1215f04046f2SJames Morse return ERR_PTR(err); 1216f04046f2SJames Morse 121749aa621cSJames Morse err = devm_mutex_init(dev, &msc->error_irq_lock); 121849aa621cSJames Morse if (err) 121949aa621cSJames Morse return ERR_PTR(err); 1220*09b89d2aSJames Morse 1221*09b89d2aSJames Morse err = devm_mutex_init(dev, &msc->cfg_lock); 1222*09b89d2aSJames Morse if (err) 1223*09b89d2aSJames Morse return ERR_PTR(err); 1224*09b89d2aSJames Morse 1225d02beb06SJames Morse mpam_mon_sel_lock_init(msc); 1226f04046f2SJames Morse msc->id = pdev->id; 1227f04046f2SJames Morse msc->pdev = pdev; 1228f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->all_msc_list); 1229f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->ris); 1230f04046f2SJames Morse 1231f04046f2SJames Morse update_msc_accessibility(msc); 1232f04046f2SJames Morse if (cpumask_empty(&msc->accessibility)) { 1233f04046f2SJames Morse dev_err_once(dev, "MSC is not accessible from any CPU!"); 1234f04046f2SJames Morse return ERR_PTR(-EINVAL); 1235f04046f2SJames Morse } 1236f04046f2SJames Morse 123749aa621cSJames Morse err = mpam_msc_setup_error_irq(msc); 123849aa621cSJames Morse if (err) 123949aa621cSJames Morse return ERR_PTR(err); 124049aa621cSJames Morse 1241f04046f2SJames Morse if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 1242f04046f2SJames Morse msc->iface = MPAM_IFACE_MMIO; 1243f04046f2SJames Morse else 1244f04046f2SJames Morse msc->iface = MPAM_IFACE_PCC; 1245f04046f2SJames Morse 1246f04046f2SJames Morse if (msc->iface == MPAM_IFACE_MMIO) { 1247f04046f2SJames Morse void __iomem *io; 1248f04046f2SJames Morse 1249f04046f2SJames Morse io = devm_platform_get_and_ioremap_resource(pdev, 0, 1250f04046f2SJames Morse &msc_res); 1251f04046f2SJames Morse if (IS_ERR(io)) { 1252f04046f2SJames Morse dev_err_once(dev, "Failed to map MSC base address\n"); 1253f04046f2SJames Morse return ERR_CAST(io); 1254f04046f2SJames Morse } 1255f04046f2SJames Morse msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 1256f04046f2SJames Morse msc->mapped_hwpage = io; 1257f04046f2SJames Morse } else { 1258f04046f2SJames Morse return ERR_PTR(-EINVAL); 1259f04046f2SJames Morse } 1260f04046f2SJames Morse 1261f04046f2SJames Morse list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 1262f04046f2SJames Morse platform_set_drvdata(pdev, msc); 1263f04046f2SJames Morse 1264f04046f2SJames Morse return msc; 1265f04046f2SJames Morse } 1266f04046f2SJames Morse 1267f04046f2SJames Morse static int fw_num_msc; 1268f04046f2SJames Morse 1269f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev) 1270f04046f2SJames Morse { 1271f04046f2SJames Morse int err; 1272f04046f2SJames Morse struct mpam_msc *msc = NULL; 1273f04046f2SJames Morse void *plat_data = pdev->dev.platform_data; 1274f04046f2SJames Morse 1275f04046f2SJames Morse mutex_lock(&mpam_list_lock); 1276f04046f2SJames Morse msc = do_mpam_msc_drv_probe(pdev); 1277f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 1278f04046f2SJames Morse 1279f04046f2SJames Morse if (IS_ERR(msc)) 1280f04046f2SJames Morse return PTR_ERR(msc); 1281f04046f2SJames Morse 1282f04046f2SJames Morse /* Create RIS entries described by firmware */ 1283f04046f2SJames Morse err = acpi_mpam_parse_resources(msc, plat_data); 1284f04046f2SJames Morse if (err) { 1285f04046f2SJames Morse mpam_msc_drv_remove(pdev); 1286f04046f2SJames Morse return err; 1287f04046f2SJames Morse } 1288f04046f2SJames Morse 1289f04046f2SJames Morse if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 12908f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL, 12918f8d0ac1SJames Morse "mpam:drv_probe"); 1292f04046f2SJames Morse 1293f04046f2SJames Morse return 0; 1294f04046f2SJames Morse } 1295f04046f2SJames Morse 1296f04046f2SJames Morse static struct platform_driver mpam_msc_driver = { 1297f04046f2SJames Morse .driver = { 1298f04046f2SJames Morse .name = "mpam_msc", 1299f04046f2SJames Morse }, 1300f04046f2SJames Morse .probe = mpam_msc_drv_probe, 1301f04046f2SJames Morse .remove = mpam_msc_drv_remove, 1302f04046f2SJames Morse }; 1303f04046f2SJames Morse 1304c10ca83aSJames Morse /* Any of these features mean the BWA_WD field is valid. */ 1305c10ca83aSJames Morse static bool mpam_has_bwa_wd_feature(struct mpam_props *props) 1306c10ca83aSJames Morse { 1307c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_min, props)) 1308c10ca83aSJames Morse return true; 1309c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_max, props)) 1310c10ca83aSJames Morse return true; 1311c10ca83aSJames Morse return false; 1312c10ca83aSJames Morse } 1313c10ca83aSJames Morse 1314c10ca83aSJames Morse #define MISMATCHED_HELPER(parent, child, helper, field, alias) \ 1315c10ca83aSJames Morse helper(parent) && \ 1316c10ca83aSJames Morse ((helper(child) && (parent)->field != (child)->field) || \ 1317c10ca83aSJames Morse (!helper(child) && !(alias))) 1318c10ca83aSJames Morse 1319c10ca83aSJames Morse #define MISMATCHED_FEAT(parent, child, feat, field, alias) \ 1320c10ca83aSJames Morse mpam_has_feature((feat), (parent)) && \ 1321c10ca83aSJames Morse ((mpam_has_feature((feat), (child)) && (parent)->field != (child)->field) || \ 1322c10ca83aSJames Morse (!mpam_has_feature((feat), (child)) && !(alias))) 1323c10ca83aSJames Morse 1324c10ca83aSJames Morse #define CAN_MERGE_FEAT(parent, child, feat, alias) \ 1325c10ca83aSJames Morse (alias) && !mpam_has_feature((feat), (parent)) && \ 1326c10ca83aSJames Morse mpam_has_feature((feat), (child)) 1327c10ca83aSJames Morse 1328c10ca83aSJames Morse /* 1329c10ca83aSJames Morse * Combine two props fields. 1330c10ca83aSJames Morse * If this is for controls that alias the same resource, it is safe to just 1331c10ca83aSJames Morse * copy the values over. If two aliasing controls implement the same scheme 1332c10ca83aSJames Morse * a safe value must be picked. 1333c10ca83aSJames Morse * For non-aliasing controls, these control different resources, and the 1334c10ca83aSJames Morse * resulting safe value must be compatible with both. When merging values in 1335c10ca83aSJames Morse * the tree, all the aliasing resources must be handled first. 1336c10ca83aSJames Morse * On mismatch, parent is modified. 1337c10ca83aSJames Morse */ 1338c10ca83aSJames Morse static void __props_mismatch(struct mpam_props *parent, 1339c10ca83aSJames Morse struct mpam_props *child, bool alias) 1340c10ca83aSJames Morse { 1341c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) { 1342c10ca83aSJames Morse parent->cpbm_wd = child->cpbm_wd; 1343c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part, 1344c10ca83aSJames Morse cpbm_wd, alias)) { 1345c10ca83aSJames Morse pr_debug("cleared cpor_part\n"); 1346c10ca83aSJames Morse mpam_clear_feature(mpam_feat_cpor_part, parent); 1347c10ca83aSJames Morse parent->cpbm_wd = 0; 1348c10ca83aSJames Morse } 1349c10ca83aSJames Morse 1350c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) { 1351c10ca83aSJames Morse parent->mbw_pbm_bits = child->mbw_pbm_bits; 1352c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part, 1353c10ca83aSJames Morse mbw_pbm_bits, alias)) { 1354c10ca83aSJames Morse pr_debug("cleared mbw_part\n"); 1355c10ca83aSJames Morse mpam_clear_feature(mpam_feat_mbw_part, parent); 1356c10ca83aSJames Morse parent->mbw_pbm_bits = 0; 1357c10ca83aSJames Morse } 1358c10ca83aSJames Morse 1359c10ca83aSJames Morse /* bwa_wd is a count of bits, fewer bits means less precision */ 1360c10ca83aSJames Morse if (alias && !mpam_has_bwa_wd_feature(parent) && 1361c10ca83aSJames Morse mpam_has_bwa_wd_feature(child)) { 1362c10ca83aSJames Morse parent->bwa_wd = child->bwa_wd; 1363c10ca83aSJames Morse } else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature, 1364c10ca83aSJames Morse bwa_wd, alias)) { 1365c10ca83aSJames Morse pr_debug("took the min bwa_wd\n"); 1366c10ca83aSJames Morse parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd); 1367c10ca83aSJames Morse } 1368c10ca83aSJames Morse 1369c10ca83aSJames Morse /* For num properties, take the minimum */ 1370c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { 1371c10ca83aSJames Morse parent->num_csu_mon = child->num_csu_mon; 1372c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu, 1373c10ca83aSJames Morse num_csu_mon, alias)) { 1374c10ca83aSJames Morse pr_debug("took the min num_csu_mon\n"); 1375c10ca83aSJames Morse parent->num_csu_mon = min(parent->num_csu_mon, 1376c10ca83aSJames Morse child->num_csu_mon); 1377c10ca83aSJames Morse } 1378c10ca83aSJames Morse 1379c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) { 1380c10ca83aSJames Morse parent->num_mbwu_mon = child->num_mbwu_mon; 1381c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu, 1382c10ca83aSJames Morse num_mbwu_mon, alias)) { 1383c10ca83aSJames Morse pr_debug("took the min num_mbwu_mon\n"); 1384c10ca83aSJames Morse parent->num_mbwu_mon = min(parent->num_mbwu_mon, 1385c10ca83aSJames Morse child->num_mbwu_mon); 1386c10ca83aSJames Morse } 1387c10ca83aSJames Morse 1388c10ca83aSJames Morse if (alias) { 1389c10ca83aSJames Morse /* Merge features for aliased resources */ 1390c10ca83aSJames Morse bitmap_or(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1391c10ca83aSJames Morse } else { 1392c10ca83aSJames Morse /* Clear missing features for non aliasing */ 1393c10ca83aSJames Morse bitmap_and(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1394c10ca83aSJames Morse } 1395c10ca83aSJames Morse } 1396c10ca83aSJames Morse 1397c10ca83aSJames Morse /* 1398c10ca83aSJames Morse * If a vmsc doesn't match class feature/configuration, do the right thing(tm). 1399c10ca83aSJames Morse * For 'num' properties we can just take the minimum. 1400c10ca83aSJames Morse * For properties where the mismatched unused bits would make a difference, we 1401c10ca83aSJames Morse * nobble the class feature, as we can't configure all the resources. 1402c10ca83aSJames Morse * e.g. The L3 cache is composed of two resources with 13 and 17 portion 1403c10ca83aSJames Morse * bitmaps respectively. 1404c10ca83aSJames Morse */ 1405c10ca83aSJames Morse static void 1406c10ca83aSJames Morse __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc) 1407c10ca83aSJames Morse { 1408c10ca83aSJames Morse struct mpam_props *cprops = &class->props; 1409c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1410c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1411c10ca83aSJames Morse 1412c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify class */ 1413c10ca83aSJames Morse 1414c10ca83aSJames Morse dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n", 1415c10ca83aSJames Morse (long)cprops->features, (long)vprops->features); 1416c10ca83aSJames Morse 1417c10ca83aSJames Morse /* Take the safe value for any common features */ 1418c10ca83aSJames Morse __props_mismatch(cprops, vprops, false); 1419c10ca83aSJames Morse } 1420c10ca83aSJames Morse 1421c10ca83aSJames Morse static void 1422c10ca83aSJames Morse __vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris) 1423c10ca83aSJames Morse { 1424c10ca83aSJames Morse struct mpam_props *rprops = &ris->props; 1425c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1426c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1427c10ca83aSJames Morse 1428c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */ 1429c10ca83aSJames Morse 1430c10ca83aSJames Morse dev_dbg(dev, "Merging features for vmsc:0x%lx |= ris:0x%lx\n", 1431c10ca83aSJames Morse (long)vprops->features, (long)rprops->features); 1432c10ca83aSJames Morse 1433c10ca83aSJames Morse /* 1434c10ca83aSJames Morse * Merge mismatched features - Copy any features that aren't common, 1435c10ca83aSJames Morse * but take the safe value for any common features. 1436c10ca83aSJames Morse */ 1437c10ca83aSJames Morse __props_mismatch(vprops, rprops, true); 1438c10ca83aSJames Morse } 1439c10ca83aSJames Morse 1440c10ca83aSJames Morse /* 1441c10ca83aSJames Morse * Copy the first component's first vMSC's properties and features to the 1442c10ca83aSJames Morse * class. __class_props_mismatch() will remove conflicts. 1443c10ca83aSJames Morse * It is not possible to have a class with no components, or a component with 1444c10ca83aSJames Morse * no resources. The vMSC properties have already been built. 1445c10ca83aSJames Morse */ 1446c10ca83aSJames Morse static void mpam_enable_init_class_features(struct mpam_class *class) 1447c10ca83aSJames Morse { 1448c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1449c10ca83aSJames Morse struct mpam_component *comp; 1450c10ca83aSJames Morse 1451c10ca83aSJames Morse comp = list_first_entry(&class->components, 1452c10ca83aSJames Morse struct mpam_component, class_list); 1453c10ca83aSJames Morse vmsc = list_first_entry(&comp->vmsc, 1454c10ca83aSJames Morse struct mpam_vmsc, comp_list); 1455c10ca83aSJames Morse 1456c10ca83aSJames Morse class->props = vmsc->props; 1457c10ca83aSJames Morse } 1458c10ca83aSJames Morse 1459c10ca83aSJames Morse static void mpam_enable_merge_vmsc_features(struct mpam_component *comp) 1460c10ca83aSJames Morse { 1461c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1462c10ca83aSJames Morse struct mpam_msc_ris *ris; 1463c10ca83aSJames Morse struct mpam_class *class = comp->class; 1464c10ca83aSJames Morse 1465c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 1466c10ca83aSJames Morse list_for_each_entry(ris, &vmsc->ris, vmsc_list) { 1467c10ca83aSJames Morse __vmsc_props_mismatch(vmsc, ris); 1468c10ca83aSJames Morse class->nrdy_usec = max(class->nrdy_usec, 1469c10ca83aSJames Morse vmsc->msc->nrdy_usec); 1470c10ca83aSJames Morse } 1471c10ca83aSJames Morse } 1472c10ca83aSJames Morse } 1473c10ca83aSJames Morse 1474c10ca83aSJames Morse static void mpam_enable_merge_class_features(struct mpam_component *comp) 1475c10ca83aSJames Morse { 1476c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1477c10ca83aSJames Morse struct mpam_class *class = comp->class; 1478c10ca83aSJames Morse 1479c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) 1480c10ca83aSJames Morse __class_props_mismatch(class, vmsc); 1481c10ca83aSJames Morse } 1482c10ca83aSJames Morse 1483c10ca83aSJames Morse /* 1484c10ca83aSJames Morse * Merge all the common resource features into class. 1485c10ca83aSJames Morse * vmsc features are bitwise-or'd together by mpam_enable_merge_vmsc_features() 1486c10ca83aSJames Morse * as the first step so that mpam_enable_init_class_features() can initialise 1487c10ca83aSJames Morse * the class with a representative set of features. 1488c10ca83aSJames Morse * Next the mpam_enable_merge_class_features() bitwise-and's all the vmsc 1489c10ca83aSJames Morse * features to form the class features. 1490c10ca83aSJames Morse * Other features are the min/max as appropriate. 1491c10ca83aSJames Morse * 1492c10ca83aSJames Morse * To avoid walking the whole tree twice, the class->nrdy_usec property is 1493c10ca83aSJames Morse * updated when working with the vmsc as it is a max(), and doesn't need 1494c10ca83aSJames Morse * initialising first. 1495c10ca83aSJames Morse */ 1496c10ca83aSJames Morse static void mpam_enable_merge_features(struct list_head *all_classes_list) 1497c10ca83aSJames Morse { 1498c10ca83aSJames Morse struct mpam_class *class; 1499c10ca83aSJames Morse struct mpam_component *comp; 1500c10ca83aSJames Morse 1501c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); 1502c10ca83aSJames Morse 1503c10ca83aSJames Morse list_for_each_entry(class, all_classes_list, classes_list) { 1504c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 1505c10ca83aSJames Morse mpam_enable_merge_vmsc_features(comp); 1506c10ca83aSJames Morse 1507c10ca83aSJames Morse mpam_enable_init_class_features(class); 1508c10ca83aSJames Morse 1509c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 1510c10ca83aSJames Morse mpam_enable_merge_class_features(comp); 1511c10ca83aSJames Morse } 1512c10ca83aSJames Morse } 1513c10ca83aSJames Morse 151449aa621cSJames Morse static char *mpam_errcode_names[16] = { 151549aa621cSJames Morse [MPAM_ERRCODE_NONE] = "No error", 151649aa621cSJames Morse [MPAM_ERRCODE_PARTID_SEL_RANGE] = "PARTID_SEL_Range", 151749aa621cSJames Morse [MPAM_ERRCODE_REQ_PARTID_RANGE] = "Req_PARTID_Range", 151849aa621cSJames Morse [MPAM_ERRCODE_MSMONCFG_ID_RANGE] = "MSMONCFG_ID_RANGE", 151949aa621cSJames Morse [MPAM_ERRCODE_REQ_PMG_RANGE] = "Req_PMG_Range", 152049aa621cSJames Morse [MPAM_ERRCODE_MONITOR_RANGE] = "Monitor_Range", 152149aa621cSJames Morse [MPAM_ERRCODE_INTPARTID_RANGE] = "intPARTID_Range", 152249aa621cSJames Morse [MPAM_ERRCODE_UNEXPECTED_INTERNAL] = "Unexpected_INTERNAL", 152349aa621cSJames Morse [MPAM_ERRCODE_UNDEFINED_RIS_PART_SEL] = "Undefined_RIS_PART_SEL", 152449aa621cSJames Morse [MPAM_ERRCODE_RIS_NO_CONTROL] = "RIS_No_Control", 152549aa621cSJames Morse [MPAM_ERRCODE_UNDEFINED_RIS_MON_SEL] = "Undefined_RIS_MON_SEL", 152649aa621cSJames Morse [MPAM_ERRCODE_RIS_NO_MONITOR] = "RIS_No_Monitor", 152749aa621cSJames Morse [12 ... 15] = "Reserved" 152849aa621cSJames Morse }; 152949aa621cSJames Morse 153049aa621cSJames Morse static int mpam_enable_msc_ecr(void *_msc) 153149aa621cSJames Morse { 153249aa621cSJames Morse struct mpam_msc *msc = _msc; 153349aa621cSJames Morse 153449aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ECR, MPAMF_ECR_INTEN); 153549aa621cSJames Morse 153649aa621cSJames Morse return 0; 153749aa621cSJames Morse } 153849aa621cSJames Morse 153949aa621cSJames Morse /* This can run in mpam_disable(), and the interrupt handler on the same CPU */ 154049aa621cSJames Morse static int mpam_disable_msc_ecr(void *_msc) 154149aa621cSJames Morse { 154249aa621cSJames Morse struct mpam_msc *msc = _msc; 154349aa621cSJames Morse 154449aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ECR, 0); 154549aa621cSJames Morse 154649aa621cSJames Morse return 0; 154749aa621cSJames Morse } 154849aa621cSJames Morse 154949aa621cSJames Morse static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) 155049aa621cSJames Morse { 155149aa621cSJames Morse u64 reg; 155249aa621cSJames Morse u16 partid; 155349aa621cSJames Morse u8 errcode, pmg, ris; 155449aa621cSJames Morse 155549aa621cSJames Morse if (WARN_ON_ONCE(!msc) || 155649aa621cSJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), 155749aa621cSJames Morse &msc->accessibility))) 155849aa621cSJames Morse return IRQ_NONE; 155949aa621cSJames Morse 156049aa621cSJames Morse reg = mpam_msc_read_esr(msc); 156149aa621cSJames Morse 156249aa621cSJames Morse errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg); 156349aa621cSJames Morse if (!errcode) 156449aa621cSJames Morse return IRQ_NONE; 156549aa621cSJames Morse 156649aa621cSJames Morse /* Clear level triggered irq */ 156749aa621cSJames Morse mpam_msc_clear_esr(msc); 156849aa621cSJames Morse 156949aa621cSJames Morse partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg); 157049aa621cSJames Morse pmg = FIELD_GET(MPAMF_ESR_PMG, reg); 157149aa621cSJames Morse ris = FIELD_GET(MPAMF_ESR_RIS, reg); 157249aa621cSJames Morse 157349aa621cSJames Morse pr_err_ratelimited("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n", 157449aa621cSJames Morse msc->id, mpam_errcode_names[errcode], partid, pmg, 157549aa621cSJames Morse ris); 157649aa621cSJames Morse 157749aa621cSJames Morse /* Disable this interrupt. */ 157849aa621cSJames Morse mpam_disable_msc_ecr(msc); 157949aa621cSJames Morse 15803796f75aSJames Morse /* Are we racing with the thread disabling MPAM? */ 15813796f75aSJames Morse if (!mpam_is_enabled()) 15823796f75aSJames Morse return IRQ_HANDLED; 15833796f75aSJames Morse 158449aa621cSJames Morse /* 158549aa621cSJames Morse * Schedule the teardown work. Don't use a threaded IRQ as we can't 158649aa621cSJames Morse * unregister the interrupt from the threaded part of the handler. 158749aa621cSJames Morse */ 158849aa621cSJames Morse mpam_disable_reason = "hardware error interrupt"; 158949aa621cSJames Morse schedule_work(&mpam_broken_work); 159049aa621cSJames Morse 159149aa621cSJames Morse return IRQ_HANDLED; 159249aa621cSJames Morse } 159349aa621cSJames Morse 159449aa621cSJames Morse static irqreturn_t mpam_ppi_handler(int irq, void *dev_id) 159549aa621cSJames Morse { 159649aa621cSJames Morse struct mpam_msc *msc = *(struct mpam_msc **)dev_id; 159749aa621cSJames Morse 159849aa621cSJames Morse return __mpam_irq_handler(irq, msc); 159949aa621cSJames Morse } 160049aa621cSJames Morse 160149aa621cSJames Morse static irqreturn_t mpam_spi_handler(int irq, void *dev_id) 160249aa621cSJames Morse { 160349aa621cSJames Morse struct mpam_msc *msc = dev_id; 160449aa621cSJames Morse 160549aa621cSJames Morse return __mpam_irq_handler(irq, msc); 160649aa621cSJames Morse } 160749aa621cSJames Morse 160849aa621cSJames Morse static int mpam_register_irqs(void) 160949aa621cSJames Morse { 161049aa621cSJames Morse int err, irq; 161149aa621cSJames Morse struct mpam_msc *msc; 161249aa621cSJames Morse 161349aa621cSJames Morse lockdep_assert_cpus_held(); 161449aa621cSJames Morse 161549aa621cSJames Morse guard(srcu)(&mpam_srcu); 161649aa621cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 161749aa621cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 161849aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 161949aa621cSJames Morse if (irq <= 0) 162049aa621cSJames Morse continue; 162149aa621cSJames Morse 162249aa621cSJames Morse /* The MPAM spec says the interrupt can be SPI, PPI or LPI */ 162349aa621cSJames Morse /* We anticipate sharing the interrupt with other MSCs */ 162449aa621cSJames Morse if (irq_is_percpu(irq)) { 162549aa621cSJames Morse err = request_percpu_irq(irq, &mpam_ppi_handler, 162649aa621cSJames Morse "mpam:msc:error", 162749aa621cSJames Morse msc->error_dev_id); 162849aa621cSJames Morse if (err) 162949aa621cSJames Morse return err; 163049aa621cSJames Morse 163149aa621cSJames Morse msc->reenable_error_ppi = irq; 163249aa621cSJames Morse smp_call_function_many(&msc->accessibility, 163349aa621cSJames Morse &_enable_percpu_irq, &irq, 163449aa621cSJames Morse true); 163549aa621cSJames Morse } else { 163649aa621cSJames Morse err = devm_request_irq(&msc->pdev->dev, irq, 163749aa621cSJames Morse &mpam_spi_handler, IRQF_SHARED, 163849aa621cSJames Morse "mpam:msc:error", msc); 163949aa621cSJames Morse if (err) 164049aa621cSJames Morse return err; 164149aa621cSJames Morse } 164249aa621cSJames Morse 164349aa621cSJames Morse mutex_lock(&msc->error_irq_lock); 164449aa621cSJames Morse msc->error_irq_req = true; 164549aa621cSJames Morse mpam_touch_msc(msc, mpam_enable_msc_ecr, msc); 164649aa621cSJames Morse msc->error_irq_hw_enabled = true; 164749aa621cSJames Morse mutex_unlock(&msc->error_irq_lock); 164849aa621cSJames Morse } 164949aa621cSJames Morse 165049aa621cSJames Morse return 0; 165149aa621cSJames Morse } 165249aa621cSJames Morse 165349aa621cSJames Morse static void mpam_unregister_irqs(void) 165449aa621cSJames Morse { 165549aa621cSJames Morse int irq; 165649aa621cSJames Morse struct mpam_msc *msc; 165749aa621cSJames Morse 165849aa621cSJames Morse guard(cpus_read_lock)(); 165949aa621cSJames Morse guard(srcu)(&mpam_srcu); 166049aa621cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 166149aa621cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 166249aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 166349aa621cSJames Morse if (irq <= 0) 166449aa621cSJames Morse continue; 166549aa621cSJames Morse 166649aa621cSJames Morse mutex_lock(&msc->error_irq_lock); 166749aa621cSJames Morse if (msc->error_irq_hw_enabled) { 166849aa621cSJames Morse mpam_touch_msc(msc, mpam_disable_msc_ecr, msc); 166949aa621cSJames Morse msc->error_irq_hw_enabled = false; 167049aa621cSJames Morse } 167149aa621cSJames Morse 167249aa621cSJames Morse if (msc->error_irq_req) { 167349aa621cSJames Morse if (irq_is_percpu(irq)) { 167449aa621cSJames Morse msc->reenable_error_ppi = 0; 167549aa621cSJames Morse free_percpu_irq(irq, msc->error_dev_id); 167649aa621cSJames Morse } else { 167749aa621cSJames Morse devm_free_irq(&msc->pdev->dev, irq, msc); 167849aa621cSJames Morse } 167949aa621cSJames Morse msc->error_irq_req = false; 168049aa621cSJames Morse } 168149aa621cSJames Morse mutex_unlock(&msc->error_irq_lock); 168249aa621cSJames Morse } 168349aa621cSJames Morse } 168449aa621cSJames Morse 1685*09b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp) 1686*09b89d2aSJames Morse { 1687*09b89d2aSJames Morse add_to_garbage(comp->cfg); 1688*09b89d2aSJames Morse } 1689*09b89d2aSJames Morse 1690*09b89d2aSJames Morse static void mpam_reset_component_cfg(struct mpam_component *comp) 1691*09b89d2aSJames Morse { 1692*09b89d2aSJames Morse int i; 1693*09b89d2aSJames Morse struct mpam_props *cprops = &comp->class->props; 1694*09b89d2aSJames Morse 1695*09b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 1696*09b89d2aSJames Morse 1697*09b89d2aSJames Morse if (!comp->cfg) 1698*09b89d2aSJames Morse return; 1699*09b89d2aSJames Morse 1700*09b89d2aSJames Morse for (i = 0; i <= mpam_partid_max; i++) { 1701*09b89d2aSJames Morse comp->cfg[i] = (struct mpam_config) {}; 1702*09b89d2aSJames Morse if (cprops->cpbm_wd) 1703*09b89d2aSJames Morse comp->cfg[i].cpbm = GENMASK(cprops->cpbm_wd - 1, 0); 1704*09b89d2aSJames Morse if (cprops->mbw_pbm_bits) 1705*09b89d2aSJames Morse comp->cfg[i].mbw_pbm = GENMASK(cprops->mbw_pbm_bits - 1, 0); 1706*09b89d2aSJames Morse if (cprops->bwa_wd) 1707*09b89d2aSJames Morse comp->cfg[i].mbw_max = GENMASK(15, 16 - cprops->bwa_wd); 1708*09b89d2aSJames Morse } 1709*09b89d2aSJames Morse } 1710*09b89d2aSJames Morse 1711*09b89d2aSJames Morse static int __allocate_component_cfg(struct mpam_component *comp) 1712*09b89d2aSJames Morse { 1713*09b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 1714*09b89d2aSJames Morse 1715*09b89d2aSJames Morse if (comp->cfg) 1716*09b89d2aSJames Morse return 0; 1717*09b89d2aSJames Morse 1718*09b89d2aSJames Morse comp->cfg = kcalloc(mpam_partid_max + 1, sizeof(*comp->cfg), GFP_KERNEL); 1719*09b89d2aSJames Morse if (!comp->cfg) 1720*09b89d2aSJames Morse return -ENOMEM; 1721*09b89d2aSJames Morse 1722*09b89d2aSJames Morse /* 1723*09b89d2aSJames Morse * The array is free()d in one go, so only cfg[0]'s structure needs 1724*09b89d2aSJames Morse * to be initialised. 1725*09b89d2aSJames Morse */ 1726*09b89d2aSJames Morse init_garbage(&comp->cfg[0].garbage); 1727*09b89d2aSJames Morse 1728*09b89d2aSJames Morse mpam_reset_component_cfg(comp); 1729*09b89d2aSJames Morse 1730*09b89d2aSJames Morse return 0; 1731*09b89d2aSJames Morse } 1732*09b89d2aSJames Morse 1733*09b89d2aSJames Morse static int mpam_allocate_config(void) 1734*09b89d2aSJames Morse { 1735*09b89d2aSJames Morse struct mpam_class *class; 1736*09b89d2aSJames Morse struct mpam_component *comp; 1737*09b89d2aSJames Morse 1738*09b89d2aSJames Morse lockdep_assert_held(&mpam_list_lock); 1739*09b89d2aSJames Morse 1740*09b89d2aSJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 1741*09b89d2aSJames Morse list_for_each_entry(comp, &class->components, class_list) { 1742*09b89d2aSJames Morse int err = __allocate_component_cfg(comp); 1743*09b89d2aSJames Morse if (err) 1744*09b89d2aSJames Morse return err; 1745*09b89d2aSJames Morse } 1746*09b89d2aSJames Morse } 1747*09b89d2aSJames Morse 1748*09b89d2aSJames Morse return 0; 1749*09b89d2aSJames Morse } 1750*09b89d2aSJames Morse 17518f8d0ac1SJames Morse static void mpam_enable_once(void) 17528f8d0ac1SJames Morse { 175349aa621cSJames Morse int err; 175449aa621cSJames Morse 1755bd221f9fSJames Morse /* 1756bd221f9fSJames Morse * Once the cpuhp callbacks have been changed, mpam_partid_max can no 1757bd221f9fSJames Morse * longer change. 1758bd221f9fSJames Morse */ 1759bd221f9fSJames Morse spin_lock(&partid_max_lock); 1760bd221f9fSJames Morse partid_max_published = true; 1761bd221f9fSJames Morse spin_unlock(&partid_max_lock); 1762bd221f9fSJames Morse 176349aa621cSJames Morse /* 176449aa621cSJames Morse * If all the MSC have been probed, enabling the IRQs happens next. 176549aa621cSJames Morse * That involves cross-calling to a CPU that can reach the MSC, and 176649aa621cSJames Morse * the locks must be taken in this order: 176749aa621cSJames Morse */ 176849aa621cSJames Morse cpus_read_lock(); 1769c10ca83aSJames Morse mutex_lock(&mpam_list_lock); 1770*09b89d2aSJames Morse do { 1771c10ca83aSJames Morse mpam_enable_merge_features(&mpam_classes); 177249aa621cSJames Morse 177349aa621cSJames Morse err = mpam_register_irqs(); 1774*09b89d2aSJames Morse if (err) { 1775*09b89d2aSJames Morse pr_warn("Failed to register irqs: %d\n", err); 1776*09b89d2aSJames Morse break; 1777*09b89d2aSJames Morse } 177849aa621cSJames Morse 1779*09b89d2aSJames Morse err = mpam_allocate_config(); 1780*09b89d2aSJames Morse if (err) { 1781*09b89d2aSJames Morse pr_err("Failed to allocate configuration arrays.\n"); 1782*09b89d2aSJames Morse break; 1783*09b89d2aSJames Morse } 1784*09b89d2aSJames Morse } while (0); 1785c10ca83aSJames Morse mutex_unlock(&mpam_list_lock); 178649aa621cSJames Morse cpus_read_unlock(); 178749aa621cSJames Morse 178849aa621cSJames Morse if (err) { 178949aa621cSJames Morse mpam_disable_reason = "Failed to enable."; 179049aa621cSJames Morse schedule_work(&mpam_broken_work); 179149aa621cSJames Morse return; 179249aa621cSJames Morse } 1793c10ca83aSJames Morse 17943796f75aSJames Morse static_branch_enable(&mpam_enabled); 17958f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline, 17968f8d0ac1SJames Morse "mpam:online"); 17978f8d0ac1SJames Morse 1798bd221f9fSJames Morse /* Use printk() to avoid the pr_fmt adding the function name. */ 1799bd221f9fSJames Morse printk(KERN_INFO "MPAM enabled with %u PARTIDs and %u PMGs\n", 1800bd221f9fSJames Morse mpam_partid_max + 1, mpam_pmg_max + 1); 18018f8d0ac1SJames Morse } 18028f8d0ac1SJames Morse 18033bd04fe7SJames Morse static void mpam_reset_component_locked(struct mpam_component *comp) 18043bd04fe7SJames Morse { 18053bd04fe7SJames Morse struct mpam_vmsc *vmsc; 18063bd04fe7SJames Morse 18073bd04fe7SJames Morse lockdep_assert_cpus_held(); 1808*09b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 1809*09b89d2aSJames Morse 1810*09b89d2aSJames Morse mpam_reset_component_cfg(comp); 18113bd04fe7SJames Morse 18123bd04fe7SJames Morse guard(srcu)(&mpam_srcu); 18133bd04fe7SJames Morse list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list, 18143bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) { 18153bd04fe7SJames Morse struct mpam_msc *msc = vmsc->msc; 18163bd04fe7SJames Morse struct mpam_msc_ris *ris; 18173bd04fe7SJames Morse 18183bd04fe7SJames Morse list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list, 18193bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) { 18203bd04fe7SJames Morse if (!ris->in_reset_state) 18213bd04fe7SJames Morse mpam_touch_msc(msc, mpam_reset_ris, ris); 18223bd04fe7SJames Morse ris->in_reset_state = true; 18233bd04fe7SJames Morse } 18243bd04fe7SJames Morse } 18253bd04fe7SJames Morse } 18263bd04fe7SJames Morse 18273bd04fe7SJames Morse static void mpam_reset_class_locked(struct mpam_class *class) 18283bd04fe7SJames Morse { 18293bd04fe7SJames Morse struct mpam_component *comp; 18303bd04fe7SJames Morse 18313bd04fe7SJames Morse lockdep_assert_cpus_held(); 18323bd04fe7SJames Morse 18333bd04fe7SJames Morse guard(srcu)(&mpam_srcu); 18343bd04fe7SJames Morse list_for_each_entry_srcu(comp, &class->components, class_list, 18353bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) 18363bd04fe7SJames Morse mpam_reset_component_locked(comp); 18373bd04fe7SJames Morse } 18383bd04fe7SJames Morse 18393bd04fe7SJames Morse static void mpam_reset_class(struct mpam_class *class) 18403bd04fe7SJames Morse { 18413bd04fe7SJames Morse cpus_read_lock(); 18423bd04fe7SJames Morse mpam_reset_class_locked(class); 18433bd04fe7SJames Morse cpus_read_unlock(); 18443bd04fe7SJames Morse } 18453bd04fe7SJames Morse 18463bd04fe7SJames Morse /* 18473bd04fe7SJames Morse * Called in response to an error IRQ. 18483bd04fe7SJames Morse * All of MPAMs errors indicate a software bug, restore any modified 18493bd04fe7SJames Morse * controls to their reset values. 18503bd04fe7SJames Morse */ 18518f8d0ac1SJames Morse void mpam_disable(struct work_struct *ignored) 18528f8d0ac1SJames Morse { 18533bd04fe7SJames Morse int idx; 18543bd04fe7SJames Morse struct mpam_class *class; 18558f8d0ac1SJames Morse struct mpam_msc *msc, *tmp; 18568f8d0ac1SJames Morse 18578f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 18588f8d0ac1SJames Morse if (mpam_cpuhp_state) { 18598f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 18608f8d0ac1SJames Morse mpam_cpuhp_state = 0; 18618f8d0ac1SJames Morse } 18628f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 18638f8d0ac1SJames Morse 18643796f75aSJames Morse static_branch_disable(&mpam_enabled); 18653796f75aSJames Morse 186649aa621cSJames Morse mpam_unregister_irqs(); 186749aa621cSJames Morse 18683bd04fe7SJames Morse idx = srcu_read_lock(&mpam_srcu); 18693bd04fe7SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 18703bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) 18713bd04fe7SJames Morse mpam_reset_class(class); 18723bd04fe7SJames Morse srcu_read_unlock(&mpam_srcu, idx); 18733bd04fe7SJames Morse 18748f8d0ac1SJames Morse mutex_lock(&mpam_list_lock); 18758f8d0ac1SJames Morse list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list) 18768f8d0ac1SJames Morse mpam_msc_destroy(msc); 18778f8d0ac1SJames Morse mutex_unlock(&mpam_list_lock); 18788f8d0ac1SJames Morse mpam_free_garbage(); 18798f8d0ac1SJames Morse 18808f8d0ac1SJames Morse pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason); 18818f8d0ac1SJames Morse } 18828f8d0ac1SJames Morse 18838f8d0ac1SJames Morse /* 18848f8d0ac1SJames Morse * Enable mpam once all devices have been probed. 18858f8d0ac1SJames Morse * Scheduled by mpam_discovery_cpu_online() once all devices have been created. 18868f8d0ac1SJames Morse * Also scheduled when new devices are probed when new CPUs come online. 18878f8d0ac1SJames Morse */ 18888f8d0ac1SJames Morse void mpam_enable(struct work_struct *work) 18898f8d0ac1SJames Morse { 18908f8d0ac1SJames Morse static atomic_t once; 18918f8d0ac1SJames Morse struct mpam_msc *msc; 18928f8d0ac1SJames Morse bool all_devices_probed = true; 18938f8d0ac1SJames Morse 18948f8d0ac1SJames Morse /* Have we probed all the hw devices? */ 18958f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 18968f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 18978f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 18988f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 18998f8d0ac1SJames Morse if (!msc->probed) 19008f8d0ac1SJames Morse all_devices_probed = false; 19018f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 19028f8d0ac1SJames Morse 19038f8d0ac1SJames Morse if (!all_devices_probed) 19048f8d0ac1SJames Morse break; 19058f8d0ac1SJames Morse } 19068f8d0ac1SJames Morse 19078f8d0ac1SJames Morse if (all_devices_probed && !atomic_fetch_inc(&once)) 19088f8d0ac1SJames Morse mpam_enable_once(); 19098f8d0ac1SJames Morse } 19108f8d0ac1SJames Morse 1911*09b89d2aSJames Morse #define maybe_update_config(cfg, feature, newcfg, member, changes) do { \ 1912*09b89d2aSJames Morse if (mpam_has_feature(feature, newcfg) && \ 1913*09b89d2aSJames Morse (newcfg)->member != (cfg)->member) { \ 1914*09b89d2aSJames Morse (cfg)->member = (newcfg)->member; \ 1915*09b89d2aSJames Morse mpam_set_feature(feature, cfg); \ 1916*09b89d2aSJames Morse \ 1917*09b89d2aSJames Morse (changes) = true; \ 1918*09b89d2aSJames Morse } \ 1919*09b89d2aSJames Morse } while (0) 1920*09b89d2aSJames Morse 1921*09b89d2aSJames Morse static bool mpam_update_config(struct mpam_config *cfg, 1922*09b89d2aSJames Morse const struct mpam_config *newcfg) 1923*09b89d2aSJames Morse { 1924*09b89d2aSJames Morse bool has_changes = false; 1925*09b89d2aSJames Morse 1926*09b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, has_changes); 1927*09b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, has_changes); 1928*09b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, has_changes); 1929*09b89d2aSJames Morse 1930*09b89d2aSJames Morse return has_changes; 1931*09b89d2aSJames Morse } 1932*09b89d2aSJames Morse 1933*09b89d2aSJames Morse int mpam_apply_config(struct mpam_component *comp, u16 partid, 1934*09b89d2aSJames Morse struct mpam_config *cfg) 1935*09b89d2aSJames Morse { 1936*09b89d2aSJames Morse struct mpam_write_config_arg arg; 1937*09b89d2aSJames Morse struct mpam_msc_ris *ris; 1938*09b89d2aSJames Morse struct mpam_vmsc *vmsc; 1939*09b89d2aSJames Morse struct mpam_msc *msc; 1940*09b89d2aSJames Morse 1941*09b89d2aSJames Morse lockdep_assert_cpus_held(); 1942*09b89d2aSJames Morse 1943*09b89d2aSJames Morse /* Don't pass in the current config! */ 1944*09b89d2aSJames Morse WARN_ON_ONCE(&comp->cfg[partid] == cfg); 1945*09b89d2aSJames Morse 1946*09b89d2aSJames Morse if (!mpam_update_config(&comp->cfg[partid], cfg)) 1947*09b89d2aSJames Morse return 0; 1948*09b89d2aSJames Morse 1949*09b89d2aSJames Morse arg.comp = comp; 1950*09b89d2aSJames Morse arg.partid = partid; 1951*09b89d2aSJames Morse 1952*09b89d2aSJames Morse guard(srcu)(&mpam_srcu); 1953*09b89d2aSJames Morse list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list, 1954*09b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1955*09b89d2aSJames Morse msc = vmsc->msc; 1956*09b89d2aSJames Morse 1957*09b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 1958*09b89d2aSJames Morse list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list, 1959*09b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1960*09b89d2aSJames Morse arg.ris = ris; 1961*09b89d2aSJames Morse mpam_touch_msc(msc, __write_config, &arg); 1962*09b89d2aSJames Morse } 1963*09b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 1964*09b89d2aSJames Morse } 1965*09b89d2aSJames Morse 1966*09b89d2aSJames Morse return 0; 1967*09b89d2aSJames Morse } 1968*09b89d2aSJames Morse 1969f04046f2SJames Morse static int __init mpam_msc_driver_init(void) 1970f04046f2SJames Morse { 1971f04046f2SJames Morse if (!system_supports_mpam()) 1972f04046f2SJames Morse return -EOPNOTSUPP; 1973f04046f2SJames Morse 1974f04046f2SJames Morse init_srcu_struct(&mpam_srcu); 1975f04046f2SJames Morse 1976f04046f2SJames Morse fw_num_msc = acpi_mpam_count_msc(); 1977f04046f2SJames Morse if (fw_num_msc <= 0) { 1978f04046f2SJames Morse pr_err("No MSC devices found in firmware\n"); 1979f04046f2SJames Morse return -EINVAL; 1980f04046f2SJames Morse } 1981f04046f2SJames Morse 1982f04046f2SJames Morse return platform_driver_register(&mpam_msc_driver); 1983f04046f2SJames Morse } 1984bd221f9fSJames Morse 1985bd221f9fSJames Morse /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ 1986f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init); 1987