1f04046f2SJames Morse // SPDX-License-Identifier: GPL-2.0 2f04046f2SJames Morse // Copyright (C) 2025 Arm Ltd. 3f04046f2SJames Morse 4f04046f2SJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 5f04046f2SJames Morse 6f04046f2SJames Morse #include <linux/acpi.h> 78f8d0ac1SJames Morse #include <linux/atomic.h> 8f04046f2SJames Morse #include <linux/arm_mpam.h> 9bd221f9fSJames Morse #include <linux/bitfield.h> 10f188a36cSJames Morse #include <linux/bitmap.h> 11f04046f2SJames Morse #include <linux/cacheinfo.h> 128f8d0ac1SJames Morse #include <linux/cpu.h> 13f04046f2SJames Morse #include <linux/cpumask.h> 14f04046f2SJames Morse #include <linux/device.h> 15f04046f2SJames Morse #include <linux/errno.h> 16f04046f2SJames Morse #include <linux/gfp.h> 1749aa621cSJames Morse #include <linux/interrupt.h> 1849aa621cSJames Morse #include <linux/irq.h> 1949aa621cSJames Morse #include <linux/irqdesc.h> 20f04046f2SJames Morse #include <linux/list.h> 21f04046f2SJames Morse #include <linux/lockdep.h> 22f04046f2SJames Morse #include <linux/mutex.h> 23f04046f2SJames Morse #include <linux/platform_device.h> 24f04046f2SJames Morse #include <linux/printk.h> 25f04046f2SJames Morse #include <linux/srcu.h> 26d02beb06SJames Morse #include <linux/spinlock.h> 27f04046f2SJames Morse #include <linux/types.h> 288f8d0ac1SJames Morse #include <linux/workqueue.h> 29f04046f2SJames Morse 30f04046f2SJames Morse #include "mpam_internal.h" 31f04046f2SJames Morse 323796f75aSJames Morse DEFINE_STATIC_KEY_FALSE(mpam_enabled); /* This moves to arch code */ 333796f75aSJames Morse 34f04046f2SJames Morse /* 35f04046f2SJames Morse * mpam_list_lock protects the SRCU lists when writing. Once the 36f04046f2SJames Morse * mpam_enabled key is enabled these lists are read-only, 37f04046f2SJames Morse * unless the error interrupt disables the driver. 38f04046f2SJames Morse */ 39f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock); 40f04046f2SJames Morse static LIST_HEAD(mpam_all_msc); 41f04046f2SJames Morse 42f04046f2SJames Morse struct srcu_struct mpam_srcu; 43f04046f2SJames Morse 44f04046f2SJames Morse /* 45f04046f2SJames Morse * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 46f04046f2SJames Morse * can be enabled. 47f04046f2SJames Morse */ 48f04046f2SJames Morse static atomic_t mpam_num_msc; 49f04046f2SJames Morse 508f8d0ac1SJames Morse static int mpam_cpuhp_state; 518f8d0ac1SJames Morse static DEFINE_MUTEX(mpam_cpuhp_state_lock); 528f8d0ac1SJames Morse 538f8d0ac1SJames Morse /* 54bd221f9fSJames Morse * The smallest common values for any CPU or MSC in the system. 55bd221f9fSJames Morse * Generating traffic outside this range will result in screaming interrupts. 56bd221f9fSJames Morse */ 57bd221f9fSJames Morse u16 mpam_partid_max; 58bd221f9fSJames Morse u8 mpam_pmg_max; 59bd221f9fSJames Morse static bool partid_max_init, partid_max_published; 60bd221f9fSJames Morse static DEFINE_SPINLOCK(partid_max_lock); 61bd221f9fSJames Morse 62bd221f9fSJames Morse /* 638f8d0ac1SJames Morse * mpam is enabled once all devices have been probed from CPU online callbacks, 648f8d0ac1SJames Morse * scheduled via this work_struct. If access to an MSC depends on a CPU that 658f8d0ac1SJames Morse * was not brought online at boot, this can happen surprisingly late. 668f8d0ac1SJames Morse */ 678f8d0ac1SJames Morse static DECLARE_WORK(mpam_enable_work, &mpam_enable); 688f8d0ac1SJames Morse 698f8d0ac1SJames Morse /* 708f8d0ac1SJames Morse * All mpam error interrupts indicate a software bug. On receipt, disable the 718f8d0ac1SJames Morse * driver. 728f8d0ac1SJames Morse */ 738f8d0ac1SJames Morse static DECLARE_WORK(mpam_broken_work, &mpam_disable); 748f8d0ac1SJames Morse 758f8d0ac1SJames Morse /* When mpam is disabled, the printed reason to aid debugging */ 768f8d0ac1SJames Morse static char *mpam_disable_reason; 778f8d0ac1SJames Morse 78f04046f2SJames Morse /* 7901fb4b82SJames Morse * An MSC is a physical container for controls and monitors, each identified by 8001fb4b82SJames Morse * their RIS index. These share a base-address, interrupts and some MMIO 8101fb4b82SJames Morse * registers. A vMSC is a virtual container for RIS in an MSC that control or 8201fb4b82SJames Morse * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but 8301fb4b82SJames Morse * not all RIS in an MSC share a vMSC. 8401fb4b82SJames Morse * 8501fb4b82SJames Morse * Components are a group of vMSC that control or monitor the same thing but 8601fb4b82SJames Morse * are from different MSC, so have different base-address, interrupts etc. 8701fb4b82SJames Morse * Classes are the set components of the same type. 8801fb4b82SJames Morse * 8901fb4b82SJames Morse * The features of a vMSC is the union of the RIS it contains. 9001fb4b82SJames Morse * The features of a Class and Component are the common subset of the vMSC 9101fb4b82SJames Morse * they contain. 9201fb4b82SJames Morse * 9301fb4b82SJames Morse * e.g. The system cache may have bandwidth controls on multiple interfaces, 9401fb4b82SJames Morse * for regulating traffic from devices independently of traffic from CPUs. 9501fb4b82SJames Morse * If these are two RIS in one MSC, they will be treated as controlling 9601fb4b82SJames Morse * different things, and will not share a vMSC/component/class. 9701fb4b82SJames Morse * 9801fb4b82SJames Morse * e.g. The L2 may have one MSC and two RIS, one for cache-controls another 9901fb4b82SJames Morse * for bandwidth. These two RIS are members of the same vMSC. 10001fb4b82SJames Morse * 10101fb4b82SJames Morse * e.g. The set of RIS that make up the L2 are grouped as a component. These 10201fb4b82SJames Morse * are sometimes termed slices. They should be configured the same, as if there 10301fb4b82SJames Morse * were only one. 10401fb4b82SJames Morse * 10501fb4b82SJames Morse * e.g. The SoC probably has more than one L2, each attached to a distinct set 10601fb4b82SJames Morse * of CPUs. All the L2 components are grouped as a class. 10701fb4b82SJames Morse * 10801fb4b82SJames Morse * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, 10901fb4b82SJames Morse * then linked via struct mpam_ris to a vmsc, component and class. 11001fb4b82SJames Morse * The same MSC may exist under different class->component->vmsc paths, but the 11101fb4b82SJames Morse * RIS index will be unique. 11201fb4b82SJames Morse */ 11301fb4b82SJames Morse LIST_HEAD(mpam_classes); 11401fb4b82SJames Morse 11501fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */ 11601fb4b82SJames Morse static LLIST_HEAD(mpam_garbage); 11701fb4b82SJames Morse 11801fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage) 11901fb4b82SJames Morse { 12001fb4b82SJames Morse init_llist_node(&garbage->llist); 12101fb4b82SJames Morse } 12201fb4b82SJames Morse 12301fb4b82SJames Morse #define add_to_garbage(x) \ 12401fb4b82SJames Morse do { \ 12501fb4b82SJames Morse __typeof__(x) _x = (x); \ 12601fb4b82SJames Morse _x->garbage.to_free = _x; \ 12701fb4b82SJames Morse llist_add(&_x->garbage.llist, &mpam_garbage); \ 12801fb4b82SJames Morse } while (0) 12901fb4b82SJames Morse 13001fb4b82SJames Morse static void mpam_free_garbage(void) 13101fb4b82SJames Morse { 13201fb4b82SJames Morse struct mpam_garbage *iter, *tmp; 13301fb4b82SJames Morse struct llist_node *to_free = llist_del_all(&mpam_garbage); 13401fb4b82SJames Morse 13501fb4b82SJames Morse if (!to_free) 13601fb4b82SJames Morse return; 13701fb4b82SJames Morse 13801fb4b82SJames Morse synchronize_srcu(&mpam_srcu); 13901fb4b82SJames Morse 14001fb4b82SJames Morse llist_for_each_entry_safe(iter, tmp, to_free, llist) { 14101fb4b82SJames Morse if (iter->pdev) 14201fb4b82SJames Morse devm_kfree(&iter->pdev->dev, iter->to_free); 14301fb4b82SJames Morse else 14401fb4b82SJames Morse kfree(iter->to_free); 14501fb4b82SJames Morse } 14601fb4b82SJames Morse } 14701fb4b82SJames Morse 14809b89d2aSJames Morse /* 14909b89d2aSJames Morse * Once mpam is enabled, new requestors cannot further reduce the available 15009b89d2aSJames Morse * partid. Assert that the size is fixed, and new requestors will be turned 15109b89d2aSJames Morse * away. 15209b89d2aSJames Morse */ 15309b89d2aSJames Morse static void mpam_assert_partid_sizes_fixed(void) 15409b89d2aSJames Morse { 15509b89d2aSJames Morse WARN_ON_ONCE(!partid_max_published); 15609b89d2aSJames Morse } 15709b89d2aSJames Morse 1588f8d0ac1SJames Morse static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) 1598f8d0ac1SJames Morse { 1608f8d0ac1SJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 1618f8d0ac1SJames Morse 1628f8d0ac1SJames Morse return readl_relaxed(msc->mapped_hwpage + reg); 1638f8d0ac1SJames Morse } 1648f8d0ac1SJames Morse 1658f8d0ac1SJames Morse static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) 1668f8d0ac1SJames Morse { 1678f8d0ac1SJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 1688f8d0ac1SJames Morse return __mpam_read_reg(msc, reg); 1698f8d0ac1SJames Morse } 1708f8d0ac1SJames Morse 1718f8d0ac1SJames Morse #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) 1728f8d0ac1SJames Morse 173bd221f9fSJames Morse static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) 174bd221f9fSJames Morse { 175bd221f9fSJames Morse WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); 176bd221f9fSJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 177bd221f9fSJames Morse 178bd221f9fSJames Morse writel_relaxed(val, msc->mapped_hwpage + reg); 179bd221f9fSJames Morse } 180bd221f9fSJames Morse 181bd221f9fSJames Morse static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 182bd221f9fSJames Morse { 183bd221f9fSJames Morse lockdep_assert_held_once(&msc->part_sel_lock); 184bd221f9fSJames Morse __mpam_write_reg(msc, reg, val); 185bd221f9fSJames Morse } 186bd221f9fSJames Morse 187bd221f9fSJames Morse #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val) 188bd221f9fSJames Morse 1898c90dc68SJames Morse static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) 1908c90dc68SJames Morse { 1918c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 1928c90dc68SJames Morse return __mpam_read_reg(msc, reg); 1938c90dc68SJames Morse } 1948c90dc68SJames Morse 1958c90dc68SJames Morse #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg) 1968c90dc68SJames Morse 1978c90dc68SJames Morse static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val) 1988c90dc68SJames Morse { 1998c90dc68SJames Morse mpam_mon_sel_lock_held(msc); 2008c90dc68SJames Morse __mpam_write_reg(msc, reg, val); 2018c90dc68SJames Morse } 2028c90dc68SJames Morse 2038c90dc68SJames Morse #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) 2048c90dc68SJames Morse 205bd221f9fSJames Morse static u64 mpam_msc_read_idr(struct mpam_msc *msc) 206bd221f9fSJames Morse { 207bd221f9fSJames Morse u64 idr_high = 0, idr_low; 208bd221f9fSJames Morse 209bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 210bd221f9fSJames Morse 211bd221f9fSJames Morse idr_low = mpam_read_partsel_reg(msc, IDR); 212bd221f9fSJames Morse if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) 213bd221f9fSJames Morse idr_high = mpam_read_partsel_reg(msc, IDR + 4); 214bd221f9fSJames Morse 215bd221f9fSJames Morse return (idr_high << 32) | idr_low; 216bd221f9fSJames Morse } 217bd221f9fSJames Morse 21849aa621cSJames Morse static void mpam_msc_clear_esr(struct mpam_msc *msc) 21949aa621cSJames Morse { 22049aa621cSJames Morse u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR); 22149aa621cSJames Morse 22249aa621cSJames Morse if (!esr_low) 22349aa621cSJames Morse return; 22449aa621cSJames Morse 22549aa621cSJames Morse /* 22649aa621cSJames Morse * Clearing the high/low bits of MPAMF_ESR can not be atomic. 22749aa621cSJames Morse * Clear the top half first, so that the pending error bits in the 22849aa621cSJames Morse * lower half prevent hardware from updating either half of the 22949aa621cSJames Morse * register. 23049aa621cSJames Morse */ 23149aa621cSJames Morse if (msc->has_extd_esr) 23249aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ESR + 4, 0); 23349aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ESR, 0); 23449aa621cSJames Morse } 23549aa621cSJames Morse 23649aa621cSJames Morse static u64 mpam_msc_read_esr(struct mpam_msc *msc) 23749aa621cSJames Morse { 23849aa621cSJames Morse u64 esr_high = 0, esr_low; 23949aa621cSJames Morse 24049aa621cSJames Morse esr_low = __mpam_read_reg(msc, MPAMF_ESR); 24149aa621cSJames Morse if (msc->has_extd_esr) 24249aa621cSJames Morse esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4); 24349aa621cSJames Morse 24449aa621cSJames Morse return (esr_high << 32) | esr_low; 24549aa621cSJames Morse } 24649aa621cSJames Morse 247bd221f9fSJames Morse static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) 248bd221f9fSJames Morse { 249bd221f9fSJames Morse lockdep_assert_held(&msc->part_sel_lock); 250bd221f9fSJames Morse 251bd221f9fSJames Morse mpam_write_partsel_reg(msc, PART_SEL, partsel); 252bd221f9fSJames Morse } 253bd221f9fSJames Morse 254bd221f9fSJames Morse static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) 255bd221f9fSJames Morse { 256bd221f9fSJames Morse u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | 257bd221f9fSJames Morse FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); 258bd221f9fSJames Morse 259bd221f9fSJames Morse __mpam_part_sel_raw(partsel, msc); 260bd221f9fSJames Morse } 261bd221f9fSJames Morse 262880df85dSJames Morse static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc *msc) 263880df85dSJames Morse { 264880df85dSJames Morse u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | 265880df85dSJames Morse FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) | 266880df85dSJames Morse MPAMCFG_PART_SEL_INTERNAL; 267880df85dSJames Morse 268880df85dSJames Morse __mpam_part_sel_raw(partsel, msc); 269880df85dSJames Morse } 270880df85dSJames Morse 271bd221f9fSJames Morse int mpam_register_requestor(u16 partid_max, u8 pmg_max) 272bd221f9fSJames Morse { 273bd221f9fSJames Morse guard(spinlock)(&partid_max_lock); 274bd221f9fSJames Morse if (!partid_max_init) { 275bd221f9fSJames Morse mpam_partid_max = partid_max; 276bd221f9fSJames Morse mpam_pmg_max = pmg_max; 277bd221f9fSJames Morse partid_max_init = true; 278bd221f9fSJames Morse } else if (!partid_max_published) { 279bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, partid_max); 280bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, pmg_max); 281bd221f9fSJames Morse } else { 282bd221f9fSJames Morse /* New requestors can't lower the values */ 283bd221f9fSJames Morse if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) 284bd221f9fSJames Morse return -EBUSY; 285bd221f9fSJames Morse } 286bd221f9fSJames Morse 287bd221f9fSJames Morse return 0; 288bd221f9fSJames Morse } 289bd221f9fSJames Morse EXPORT_SYMBOL(mpam_register_requestor); 290bd221f9fSJames Morse 29101fb4b82SJames Morse static struct mpam_class * 29201fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type) 29301fb4b82SJames Morse { 29401fb4b82SJames Morse struct mpam_class *class; 29501fb4b82SJames Morse 29601fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 29701fb4b82SJames Morse 29801fb4b82SJames Morse class = kzalloc(sizeof(*class), GFP_KERNEL); 29901fb4b82SJames Morse if (!class) 30001fb4b82SJames Morse return ERR_PTR(-ENOMEM); 30101fb4b82SJames Morse init_garbage(&class->garbage); 30201fb4b82SJames Morse 30301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->components); 30401fb4b82SJames Morse /* Affinity is updated when ris are added */ 30501fb4b82SJames Morse class->level = level_idx; 30601fb4b82SJames Morse class->type = type; 30701fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->classes_list); 308c891bae6SJames Morse ida_init(&class->ida_csu_mon); 309c891bae6SJames Morse ida_init(&class->ida_mbwu_mon); 31001fb4b82SJames Morse 31101fb4b82SJames Morse list_add_rcu(&class->classes_list, &mpam_classes); 31201fb4b82SJames Morse 31301fb4b82SJames Morse return class; 31401fb4b82SJames Morse } 31501fb4b82SJames Morse 31601fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class) 31701fb4b82SJames Morse { 31801fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 31901fb4b82SJames Morse 32001fb4b82SJames Morse list_del_rcu(&class->classes_list); 32101fb4b82SJames Morse add_to_garbage(class); 32201fb4b82SJames Morse } 32301fb4b82SJames Morse 32401fb4b82SJames Morse static struct mpam_class * 32501fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type) 32601fb4b82SJames Morse { 32701fb4b82SJames Morse struct mpam_class *class; 32801fb4b82SJames Morse 32901fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 33001fb4b82SJames Morse 33101fb4b82SJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 33201fb4b82SJames Morse if (class->type == type && class->level == level_idx) 33301fb4b82SJames Morse return class; 33401fb4b82SJames Morse } 33501fb4b82SJames Morse 33601fb4b82SJames Morse return mpam_class_alloc(level_idx, type); 33701fb4b82SJames Morse } 33801fb4b82SJames Morse 33901fb4b82SJames Morse static struct mpam_component * 34001fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id) 34101fb4b82SJames Morse { 34201fb4b82SJames Morse struct mpam_component *comp; 34301fb4b82SJames Morse 34401fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 34501fb4b82SJames Morse 34601fb4b82SJames Morse comp = kzalloc(sizeof(*comp), GFP_KERNEL); 34701fb4b82SJames Morse if (!comp) 34801fb4b82SJames Morse return ERR_PTR(-ENOMEM); 34901fb4b82SJames Morse init_garbage(&comp->garbage); 35001fb4b82SJames Morse 35101fb4b82SJames Morse comp->comp_id = id; 35201fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->vmsc); 35301fb4b82SJames Morse /* Affinity is updated when RIS are added */ 35401fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->class_list); 35501fb4b82SJames Morse comp->class = class; 35601fb4b82SJames Morse 35701fb4b82SJames Morse list_add_rcu(&comp->class_list, &class->components); 35801fb4b82SJames Morse 35901fb4b82SJames Morse return comp; 36001fb4b82SJames Morse } 36101fb4b82SJames Morse 36209b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp); 36309b89d2aSJames Morse 36401fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp) 36501fb4b82SJames Morse { 36601fb4b82SJames Morse struct mpam_class *class = comp->class; 36701fb4b82SJames Morse 36801fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 36901fb4b82SJames Morse 37009b89d2aSJames Morse __destroy_component_cfg(comp); 37109b89d2aSJames Morse 37201fb4b82SJames Morse list_del_rcu(&comp->class_list); 37301fb4b82SJames Morse add_to_garbage(comp); 37401fb4b82SJames Morse 37501fb4b82SJames Morse if (list_empty(&class->components)) 37601fb4b82SJames Morse mpam_class_destroy(class); 37701fb4b82SJames Morse } 37801fb4b82SJames Morse 37901fb4b82SJames Morse static struct mpam_component * 38001fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id) 38101fb4b82SJames Morse { 38201fb4b82SJames Morse struct mpam_component *comp; 38301fb4b82SJames Morse 38401fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 38501fb4b82SJames Morse 38601fb4b82SJames Morse list_for_each_entry(comp, &class->components, class_list) { 38701fb4b82SJames Morse if (comp->comp_id == id) 38801fb4b82SJames Morse return comp; 38901fb4b82SJames Morse } 39001fb4b82SJames Morse 39101fb4b82SJames Morse return mpam_component_alloc(class, id); 39201fb4b82SJames Morse } 39301fb4b82SJames Morse 39401fb4b82SJames Morse static struct mpam_vmsc * 39501fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) 39601fb4b82SJames Morse { 39701fb4b82SJames Morse struct mpam_vmsc *vmsc; 39801fb4b82SJames Morse 39901fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 40001fb4b82SJames Morse 40101fb4b82SJames Morse vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL); 40201fb4b82SJames Morse if (!vmsc) 40301fb4b82SJames Morse return ERR_PTR(-ENOMEM); 40401fb4b82SJames Morse init_garbage(&vmsc->garbage); 40501fb4b82SJames Morse 40601fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->ris); 40701fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->comp_list); 40801fb4b82SJames Morse vmsc->comp = comp; 40901fb4b82SJames Morse vmsc->msc = msc; 41001fb4b82SJames Morse 41101fb4b82SJames Morse list_add_rcu(&vmsc->comp_list, &comp->vmsc); 41201fb4b82SJames Morse 41301fb4b82SJames Morse return vmsc; 41401fb4b82SJames Morse } 41501fb4b82SJames Morse 41601fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) 41701fb4b82SJames Morse { 41801fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 41901fb4b82SJames Morse 42001fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 42101fb4b82SJames Morse 42201fb4b82SJames Morse list_del_rcu(&vmsc->comp_list); 42301fb4b82SJames Morse add_to_garbage(vmsc); 42401fb4b82SJames Morse 42501fb4b82SJames Morse if (list_empty(&comp->vmsc)) 42601fb4b82SJames Morse mpam_component_destroy(comp); 42701fb4b82SJames Morse } 42801fb4b82SJames Morse 42901fb4b82SJames Morse static struct mpam_vmsc * 43001fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) 43101fb4b82SJames Morse { 43201fb4b82SJames Morse struct mpam_vmsc *vmsc; 43301fb4b82SJames Morse 43401fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 43501fb4b82SJames Morse 43601fb4b82SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 43701fb4b82SJames Morse if (vmsc->msc->id == msc->id) 43801fb4b82SJames Morse return vmsc; 43901fb4b82SJames Morse } 44001fb4b82SJames Morse 44101fb4b82SJames Morse return mpam_vmsc_alloc(comp, msc); 44201fb4b82SJames Morse } 44301fb4b82SJames Morse 44401fb4b82SJames Morse /* 44501fb4b82SJames Morse * The cacheinfo structures are only populated when CPUs are online. 44601fb4b82SJames Morse * This helper walks the acpi tables to include offline CPUs too. 44701fb4b82SJames Morse */ 44801fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 44901fb4b82SJames Morse cpumask_t *affinity) 45001fb4b82SJames Morse { 45101fb4b82SJames Morse return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); 45201fb4b82SJames Morse } 45301fb4b82SJames Morse 45401fb4b82SJames Morse /* 45501fb4b82SJames Morse * cpumask_of_node() only knows about online CPUs. This can't tell us whether 45601fb4b82SJames Morse * a class is represented on all possible CPUs. 45701fb4b82SJames Morse */ 45801fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) 45901fb4b82SJames Morse { 46001fb4b82SJames Morse int cpu; 46101fb4b82SJames Morse 46201fb4b82SJames Morse for_each_possible_cpu(cpu) { 46301fb4b82SJames Morse if (node_id == cpu_to_node(cpu)) 46401fb4b82SJames Morse cpumask_set_cpu(cpu, affinity); 46501fb4b82SJames Morse } 46601fb4b82SJames Morse } 46701fb4b82SJames Morse 46801fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, 46901fb4b82SJames Morse enum mpam_class_types type, 47001fb4b82SJames Morse struct mpam_class *class, 47101fb4b82SJames Morse struct mpam_component *comp) 47201fb4b82SJames Morse { 47301fb4b82SJames Morse int err; 47401fb4b82SJames Morse 47501fb4b82SJames Morse switch (type) { 47601fb4b82SJames Morse case MPAM_CLASS_CACHE: 47701fb4b82SJames Morse err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, 47801fb4b82SJames Morse affinity); 47901fb4b82SJames Morse if (err) { 48001fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, 48101fb4b82SJames Morse "Failed to determine CPU affinity\n"); 48201fb4b82SJames Morse return err; 48301fb4b82SJames Morse } 48401fb4b82SJames Morse 48501fb4b82SJames Morse if (cpumask_empty(affinity)) 48601fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n"); 48701fb4b82SJames Morse 48801fb4b82SJames Morse break; 48901fb4b82SJames Morse case MPAM_CLASS_MEMORY: 49001fb4b82SJames Morse get_cpumask_from_node_id(comp->comp_id, affinity); 49101fb4b82SJames Morse /* affinity may be empty for CPU-less memory nodes */ 49201fb4b82SJames Morse break; 49301fb4b82SJames Morse case MPAM_CLASS_UNKNOWN: 49401fb4b82SJames Morse return 0; 49501fb4b82SJames Morse } 49601fb4b82SJames Morse 49701fb4b82SJames Morse cpumask_and(affinity, affinity, &msc->accessibility); 49801fb4b82SJames Morse 49901fb4b82SJames Morse return 0; 50001fb4b82SJames Morse } 50101fb4b82SJames Morse 50201fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, 50301fb4b82SJames Morse enum mpam_class_types type, u8 class_id, 50401fb4b82SJames Morse int component_id) 50501fb4b82SJames Morse { 50601fb4b82SJames Morse int err; 50701fb4b82SJames Morse struct mpam_vmsc *vmsc; 50801fb4b82SJames Morse struct mpam_msc_ris *ris; 50901fb4b82SJames Morse struct mpam_class *class; 51001fb4b82SJames Morse struct mpam_component *comp; 51101fb4b82SJames Morse struct platform_device *pdev = msc->pdev; 51201fb4b82SJames Morse 51301fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 51401fb4b82SJames Morse 51501fb4b82SJames Morse if (ris_idx > MPAM_MSC_MAX_NUM_RIS) 51601fb4b82SJames Morse return -EINVAL; 51701fb4b82SJames Morse 51801fb4b82SJames Morse if (test_and_set_bit(ris_idx, &msc->ris_idxs)) 51901fb4b82SJames Morse return -EBUSY; 52001fb4b82SJames Morse 52101fb4b82SJames Morse ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL); 52201fb4b82SJames Morse if (!ris) 52301fb4b82SJames Morse return -ENOMEM; 52401fb4b82SJames Morse init_garbage(&ris->garbage); 52501fb4b82SJames Morse ris->garbage.pdev = pdev; 52601fb4b82SJames Morse 52701fb4b82SJames Morse class = mpam_class_find(class_id, type); 52801fb4b82SJames Morse if (IS_ERR(class)) 52901fb4b82SJames Morse return PTR_ERR(class); 53001fb4b82SJames Morse 53101fb4b82SJames Morse comp = mpam_component_find(class, component_id); 53201fb4b82SJames Morse if (IS_ERR(comp)) { 53301fb4b82SJames Morse if (list_empty(&class->components)) 53401fb4b82SJames Morse mpam_class_destroy(class); 53501fb4b82SJames Morse return PTR_ERR(comp); 53601fb4b82SJames Morse } 53701fb4b82SJames Morse 53801fb4b82SJames Morse vmsc = mpam_vmsc_find(comp, msc); 53901fb4b82SJames Morse if (IS_ERR(vmsc)) { 54001fb4b82SJames Morse if (list_empty(&comp->vmsc)) 54101fb4b82SJames Morse mpam_component_destroy(comp); 54201fb4b82SJames Morse return PTR_ERR(vmsc); 54301fb4b82SJames Morse } 54401fb4b82SJames Morse 54501fb4b82SJames Morse err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); 54601fb4b82SJames Morse if (err) { 54701fb4b82SJames Morse if (list_empty(&vmsc->ris)) 54801fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 54901fb4b82SJames Morse return err; 55001fb4b82SJames Morse } 55101fb4b82SJames Morse 55201fb4b82SJames Morse ris->ris_idx = ris_idx; 55301fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->msc_list); 55401fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->vmsc_list); 55501fb4b82SJames Morse ris->vmsc = vmsc; 55601fb4b82SJames Morse 55701fb4b82SJames Morse cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); 55801fb4b82SJames Morse cpumask_or(&class->affinity, &class->affinity, &ris->affinity); 55901fb4b82SJames Morse list_add_rcu(&ris->vmsc_list, &vmsc->ris); 56001fb4b82SJames Morse list_add_rcu(&ris->msc_list, &msc->ris); 56101fb4b82SJames Morse 56201fb4b82SJames Morse return 0; 56301fb4b82SJames Morse } 56401fb4b82SJames Morse 56501fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris) 56601fb4b82SJames Morse { 56701fb4b82SJames Morse struct mpam_vmsc *vmsc = ris->vmsc; 56801fb4b82SJames Morse struct mpam_msc *msc = vmsc->msc; 56901fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 57001fb4b82SJames Morse struct mpam_class *class = comp->class; 57101fb4b82SJames Morse 57201fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 57301fb4b82SJames Morse 57401fb4b82SJames Morse /* 57501fb4b82SJames Morse * It is assumed affinities don't overlap. If they do the class becomes 57601fb4b82SJames Morse * unusable immediately. 57701fb4b82SJames Morse */ 57801fb4b82SJames Morse cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); 57901fb4b82SJames Morse cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); 58001fb4b82SJames Morse clear_bit(ris->ris_idx, &msc->ris_idxs); 58101fb4b82SJames Morse list_del_rcu(&ris->msc_list); 58201fb4b82SJames Morse list_del_rcu(&ris->vmsc_list); 58301fb4b82SJames Morse add_to_garbage(ris); 58401fb4b82SJames Morse 58501fb4b82SJames Morse if (list_empty(&vmsc->ris)) 58601fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 58701fb4b82SJames Morse } 58801fb4b82SJames Morse 58901fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 59001fb4b82SJames Morse enum mpam_class_types type, u8 class_id, int component_id) 59101fb4b82SJames Morse { 59201fb4b82SJames Morse int err; 59301fb4b82SJames Morse 59401fb4b82SJames Morse mutex_lock(&mpam_list_lock); 59501fb4b82SJames Morse err = mpam_ris_create_locked(msc, ris_idx, type, class_id, 59601fb4b82SJames Morse component_id); 59701fb4b82SJames Morse mutex_unlock(&mpam_list_lock); 59801fb4b82SJames Morse if (err) 59901fb4b82SJames Morse mpam_free_garbage(); 60001fb4b82SJames Morse 60101fb4b82SJames Morse return err; 60201fb4b82SJames Morse } 60301fb4b82SJames Morse 604bd221f9fSJames Morse static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc, 605bd221f9fSJames Morse u8 ris_idx) 606bd221f9fSJames Morse { 607bd221f9fSJames Morse int err; 608bd221f9fSJames Morse struct mpam_msc_ris *ris; 609bd221f9fSJames Morse 610bd221f9fSJames Morse lockdep_assert_held(&mpam_list_lock); 611bd221f9fSJames Morse 612bd221f9fSJames Morse if (!test_bit(ris_idx, &msc->ris_idxs)) { 613bd221f9fSJames Morse err = mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN, 614bd221f9fSJames Morse 0, 0); 615bd221f9fSJames Morse if (err) 616bd221f9fSJames Morse return ERR_PTR(err); 617bd221f9fSJames Morse } 618bd221f9fSJames Morse 619bd221f9fSJames Morse list_for_each_entry(ris, &msc->ris, msc_list) { 620bd221f9fSJames Morse if (ris->ris_idx == ris_idx) 621bd221f9fSJames Morse return ris; 622bd221f9fSJames Morse } 623bd221f9fSJames Morse 624bd221f9fSJames Morse return ERR_PTR(-ENOENT); 625bd221f9fSJames Morse } 626bd221f9fSJames Morse 6278c90dc68SJames Morse /* 6288c90dc68SJames Morse * IHI009A.a has this nugget: "If a monitor does not support automatic behaviour 6298c90dc68SJames Morse * of NRDY, software can use this bit for any purpose" - so hardware might not 6308c90dc68SJames Morse * implement this - but it isn't RES0. 6318c90dc68SJames Morse * 6328c90dc68SJames Morse * Try and see what values stick in this bit. If we can write either value, 6338c90dc68SJames Morse * its probably not implemented by hardware. 6348c90dc68SJames Morse */ 6358c90dc68SJames Morse static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris *ris, u32 mon_reg) 6368c90dc68SJames Morse { 6378c90dc68SJames Morse u32 now; 6388c90dc68SJames Morse u64 mon_sel; 6398c90dc68SJames Morse bool can_set, can_clear; 6408c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 6418c90dc68SJames Morse 6428c90dc68SJames Morse if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc))) 6438c90dc68SJames Morse return false; 6448c90dc68SJames Morse 6458c90dc68SJames Morse mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | 6468c90dc68SJames Morse FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); 6478c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, mon_sel); 6488c90dc68SJames Morse 6498c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); 6508c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 6518c90dc68SJames Morse can_set = now & MSMON___NRDY; 6528c90dc68SJames Morse 6538c90dc68SJames Morse _mpam_write_monsel_reg(msc, mon_reg, 0); 6548c90dc68SJames Morse now = _mpam_read_monsel_reg(msc, mon_reg); 6558c90dc68SJames Morse can_clear = !(now & MSMON___NRDY); 6568c90dc68SJames Morse mpam_mon_sel_unlock(msc); 6578c90dc68SJames Morse 6588c90dc68SJames Morse return (!can_set || !can_clear); 6598c90dc68SJames Morse } 6608c90dc68SJames Morse 6618c90dc68SJames Morse #define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ 6628c90dc68SJames Morse _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) 6638c90dc68SJames Morse 6648c90dc68SJames Morse static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) 6658c90dc68SJames Morse { 6668c90dc68SJames Morse int err; 6678c90dc68SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 6688c90dc68SJames Morse struct device *dev = &msc->pdev->dev; 6698c90dc68SJames Morse struct mpam_props *props = &ris->props; 670880df85dSJames Morse struct mpam_class *class = ris->vmsc->comp->class; 6718c90dc68SJames Morse 6728c90dc68SJames Morse lockdep_assert_held(&msc->probe_lock); 6738c90dc68SJames Morse lockdep_assert_held(&msc->part_sel_lock); 6748c90dc68SJames Morse 675880df85dSJames Morse /* Cache Capacity Partitioning */ 676880df85dSJames Morse if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) { 677880df85dSJames Morse u32 ccap_features = mpam_read_partsel_reg(msc, CCAP_IDR); 678880df85dSJames Morse 679880df85dSJames Morse props->cmax_wd = FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features); 680880df85dSJames Morse if (props->cmax_wd && 681880df85dSJames Morse FIELD_GET(MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM, ccap_features)) 682880df85dSJames Morse mpam_set_feature(mpam_feat_cmax_softlim, props); 683880df85dSJames Morse 684880df85dSJames Morse if (props->cmax_wd && 685880df85dSJames Morse !FIELD_GET(MPAMF_CCAP_IDR_NO_CMAX, ccap_features)) 686880df85dSJames Morse mpam_set_feature(mpam_feat_cmax_cmax, props); 687880df85dSJames Morse 688880df85dSJames Morse if (props->cmax_wd && 689880df85dSJames Morse FIELD_GET(MPAMF_CCAP_IDR_HAS_CMIN, ccap_features)) 690880df85dSJames Morse mpam_set_feature(mpam_feat_cmax_cmin, props); 691880df85dSJames Morse 692880df85dSJames Morse props->cassoc_wd = FIELD_GET(MPAMF_CCAP_IDR_CASSOC_WD, ccap_features); 693880df85dSJames Morse if (props->cassoc_wd && 694880df85dSJames Morse FIELD_GET(MPAMF_CCAP_IDR_HAS_CASSOC, ccap_features)) 695880df85dSJames Morse mpam_set_feature(mpam_feat_cmax_cassoc, props); 696880df85dSJames Morse } 697880df85dSJames Morse 6988c90dc68SJames Morse /* Cache Portion partitioning */ 6998c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { 7008c90dc68SJames Morse u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR); 7018c90dc68SJames Morse 7028c90dc68SJames Morse props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); 7038c90dc68SJames Morse if (props->cpbm_wd) 7048c90dc68SJames Morse mpam_set_feature(mpam_feat_cpor_part, props); 7058c90dc68SJames Morse } 7068c90dc68SJames Morse 7078c90dc68SJames Morse /* Memory bandwidth partitioning */ 7088c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) { 7098c90dc68SJames Morse u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR); 7108c90dc68SJames Morse 7118c90dc68SJames Morse /* portion bitmap resolution */ 7128c90dc68SJames Morse props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features); 7138c90dc68SJames Morse if (props->mbw_pbm_bits && 7148c90dc68SJames Morse FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features)) 7158c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_part, props); 7168c90dc68SJames Morse 7178c90dc68SJames Morse props->bwa_wd = FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); 7188c90dc68SJames Morse if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) 7198c90dc68SJames Morse mpam_set_feature(mpam_feat_mbw_max, props); 720880df85dSJames Morse 721880df85dSJames Morse if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features)) 722880df85dSJames Morse mpam_set_feature(mpam_feat_mbw_min, props); 723880df85dSJames Morse 724880df85dSJames Morse if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_PROP, mbw_features)) 725880df85dSJames Morse mpam_set_feature(mpam_feat_mbw_prop, props); 726880df85dSJames Morse } 727880df85dSJames Morse 728880df85dSJames Morse /* Priority partitioning */ 729880df85dSJames Morse if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) { 730880df85dSJames Morse u32 pri_features = mpam_read_partsel_reg(msc, PRI_IDR); 731880df85dSJames Morse 732880df85dSJames Morse props->intpri_wd = FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features); 733880df85dSJames Morse if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features)) { 734880df85dSJames Morse mpam_set_feature(mpam_feat_intpri_part, props); 735880df85dSJames Morse if (FIELD_GET(MPAMF_PRI_IDR_INTPRI_0_IS_LOW, pri_features)) 736880df85dSJames Morse mpam_set_feature(mpam_feat_intpri_part_0_low, props); 737880df85dSJames Morse } 738880df85dSJames Morse 739880df85dSJames Morse props->dspri_wd = FIELD_GET(MPAMF_PRI_IDR_DSPRI_WD, pri_features); 740880df85dSJames Morse if (props->dspri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_DSPRI, pri_features)) { 741880df85dSJames Morse mpam_set_feature(mpam_feat_dspri_part, props); 742880df85dSJames Morse if (FIELD_GET(MPAMF_PRI_IDR_DSPRI_0_IS_LOW, pri_features)) 743880df85dSJames Morse mpam_set_feature(mpam_feat_dspri_part_0_low, props); 744880df85dSJames Morse } 7458c90dc68SJames Morse } 7468c90dc68SJames Morse 7478c90dc68SJames Morse /* Performance Monitoring */ 7488c90dc68SJames Morse if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) { 7498c90dc68SJames Morse u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR); 7508c90dc68SJames Morse 7518c90dc68SJames Morse /* 7528c90dc68SJames Morse * If the firmware max-nrdy-us property is missing, the 7538c90dc68SJames Morse * CSU counters can't be used. Should we wait forever? 7548c90dc68SJames Morse */ 7558c90dc68SJames Morse err = device_property_read_u32(&msc->pdev->dev, 7568c90dc68SJames Morse "arm,not-ready-us", 7578c90dc68SJames Morse &msc->nrdy_usec); 7588c90dc68SJames Morse 7598c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) { 7608c90dc68SJames Morse u32 csumonidr; 7618c90dc68SJames Morse 7628c90dc68SJames Morse csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR); 7638c90dc68SJames Morse props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr); 7648c90dc68SJames Morse if (props->num_csu_mon) { 7658c90dc68SJames Morse bool hw_managed; 7668c90dc68SJames Morse 7678c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu, props); 7688c90dc68SJames Morse 769880df85dSJames Morse if (FIELD_GET(MPAMF_CSUMON_IDR_HAS_XCL, csumonidr)) 770880df85dSJames Morse mpam_set_feature(mpam_feat_msmon_csu_xcl, props); 771880df85dSJames Morse 7728c90dc68SJames Morse /* Is NRDY hardware managed? */ 7738c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, CSU); 7748c90dc68SJames Morse if (hw_managed) 7758c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); 7768c90dc68SJames Morse } 7778c90dc68SJames Morse 7788c90dc68SJames Morse /* 7798c90dc68SJames Morse * Accept the missing firmware property if NRDY appears 7808c90dc68SJames Morse * un-implemented. 7818c90dc68SJames Morse */ 7828c90dc68SJames Morse if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) 7838c90dc68SJames Morse dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware."); 7848c90dc68SJames Morse } 7858c90dc68SJames Morse if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { 7868c90dc68SJames Morse bool hw_managed; 7878c90dc68SJames Morse u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR); 7888c90dc68SJames Morse 7898c90dc68SJames Morse props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr); 7908c90dc68SJames Morse if (props->num_mbwu_mon) 7918c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu, props); 7928c90dc68SJames Morse 793880df85dSJames Morse if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumon_idr)) 794880df85dSJames Morse mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props); 795880df85dSJames Morse 7968c90dc68SJames Morse /* Is NRDY hardware managed? */ 7978c90dc68SJames Morse hw_managed = mpam_ris_hw_probe_hw_nrdy(ris, MBWU); 7988c90dc68SJames Morse if (hw_managed) 7998c90dc68SJames Morse mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); 8008c90dc68SJames Morse 8018c90dc68SJames Morse /* 8028c90dc68SJames Morse * Don't warn about any missing firmware property for 8038c90dc68SJames Morse * MBWU NRDY - it doesn't make any sense! 8048c90dc68SJames Morse */ 8058c90dc68SJames Morse } 8068c90dc68SJames Morse } 807880df85dSJames Morse 808880df85dSJames Morse /* 809880df85dSJames Morse * RIS with PARTID narrowing don't have enough storage for one 810880df85dSJames Morse * configuration per PARTID. If these are in a class we could use, 811880df85dSJames Morse * reduce the supported partid_max to match the number of intpartid. 812880df85dSJames Morse * If the class is unknown, just ignore it. 813880df85dSJames Morse */ 814880df85dSJames Morse if (FIELD_GET(MPAMF_IDR_HAS_PARTID_NRW, ris->idr) && 815880df85dSJames Morse class->type != MPAM_CLASS_UNKNOWN) { 816880df85dSJames Morse u32 nrwidr = mpam_read_partsel_reg(msc, PARTID_NRW_IDR); 817880df85dSJames Morse u16 partid_max = FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr); 818880df85dSJames Morse 819880df85dSJames Morse mpam_set_feature(mpam_feat_partid_nrw, props); 820880df85dSJames Morse msc->partid_max = min(msc->partid_max, partid_max); 821880df85dSJames Morse } 8228c90dc68SJames Morse } 8238c90dc68SJames Morse 8248f8d0ac1SJames Morse static int mpam_msc_hw_probe(struct mpam_msc *msc) 8258f8d0ac1SJames Morse { 8268f8d0ac1SJames Morse u64 idr; 827bd221f9fSJames Morse u16 partid_max; 828bd221f9fSJames Morse u8 ris_idx, pmg_max; 829bd221f9fSJames Morse struct mpam_msc_ris *ris; 8308f8d0ac1SJames Morse struct device *dev = &msc->pdev->dev; 8318f8d0ac1SJames Morse 8328f8d0ac1SJames Morse lockdep_assert_held(&msc->probe_lock); 8338f8d0ac1SJames Morse 8348f8d0ac1SJames Morse idr = __mpam_read_reg(msc, MPAMF_AIDR); 8358f8d0ac1SJames Morse if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) { 8368f8d0ac1SJames Morse dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n"); 8378f8d0ac1SJames Morse return -EIO; 8388f8d0ac1SJames Morse } 8398f8d0ac1SJames Morse 840bd221f9fSJames Morse /* Grab an IDR value to find out how many RIS there are */ 841bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 842bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 843bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 844bd221f9fSJames Morse 845bd221f9fSJames Morse msc->ris_max = FIELD_GET(MPAMF_IDR_RIS_MAX, idr); 846bd221f9fSJames Morse 847bd221f9fSJames Morse /* Use these values so partid/pmg always starts with a valid value */ 848bd221f9fSJames Morse msc->partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 849bd221f9fSJames Morse msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 850bd221f9fSJames Morse 851bd221f9fSJames Morse for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) { 852bd221f9fSJames Morse mutex_lock(&msc->part_sel_lock); 853bd221f9fSJames Morse __mpam_part_sel(ris_idx, 0, msc); 854bd221f9fSJames Morse idr = mpam_msc_read_idr(msc); 855bd221f9fSJames Morse mutex_unlock(&msc->part_sel_lock); 856bd221f9fSJames Morse 857bd221f9fSJames Morse partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); 858bd221f9fSJames Morse pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr); 859bd221f9fSJames Morse msc->partid_max = min(msc->partid_max, partid_max); 860bd221f9fSJames Morse msc->pmg_max = min(msc->pmg_max, pmg_max); 86149aa621cSJames Morse msc->has_extd_esr = FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr); 862bd221f9fSJames Morse 863bd221f9fSJames Morse mutex_lock(&mpam_list_lock); 864bd221f9fSJames Morse ris = mpam_get_or_create_ris(msc, ris_idx); 865bd221f9fSJames Morse mutex_unlock(&mpam_list_lock); 866bd221f9fSJames Morse if (IS_ERR(ris)) 867bd221f9fSJames Morse return PTR_ERR(ris); 8688c90dc68SJames Morse ris->idr = idr; 8698c90dc68SJames Morse 8708c90dc68SJames Morse mutex_lock(&msc->part_sel_lock); 8718c90dc68SJames Morse __mpam_part_sel(ris_idx, 0, msc); 8728c90dc68SJames Morse mpam_ris_hw_probe(ris); 8738c90dc68SJames Morse mutex_unlock(&msc->part_sel_lock); 874bd221f9fSJames Morse } 875bd221f9fSJames Morse 87649aa621cSJames Morse /* Clear any stale errors */ 87749aa621cSJames Morse mpam_msc_clear_esr(msc); 87849aa621cSJames Morse 879bd221f9fSJames Morse spin_lock(&partid_max_lock); 880bd221f9fSJames Morse mpam_partid_max = min(mpam_partid_max, msc->partid_max); 881bd221f9fSJames Morse mpam_pmg_max = min(mpam_pmg_max, msc->pmg_max); 882bd221f9fSJames Morse spin_unlock(&partid_max_lock); 883bd221f9fSJames Morse 8848f8d0ac1SJames Morse msc->probed = true; 8858f8d0ac1SJames Morse 8868f8d0ac1SJames Morse return 0; 8878f8d0ac1SJames Morse } 8888f8d0ac1SJames Morse 889823e7c37SJames Morse struct mon_read { 890823e7c37SJames Morse struct mpam_msc_ris *ris; 891823e7c37SJames Morse struct mon_cfg *ctx; 892823e7c37SJames Morse enum mpam_device_features type; 893823e7c37SJames Morse u64 *val; 894823e7c37SJames Morse int err; 895823e7c37SJames Morse }; 896823e7c37SJames Morse 897823e7c37SJames Morse static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, 898823e7c37SJames Morse u32 *flt_val) 899823e7c37SJames Morse { 900823e7c37SJames Morse struct mon_cfg *ctx = m->ctx; 901823e7c37SJames Morse 902823e7c37SJames Morse /* 903823e7c37SJames Morse * For CSU counters its implementation-defined what happens when not 904823e7c37SJames Morse * filtering by partid. 905823e7c37SJames Morse */ 906823e7c37SJames Morse *ctl_val = MSMON_CFG_x_CTL_MATCH_PARTID; 907823e7c37SJames Morse 908823e7c37SJames Morse *flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid); 909823e7c37SJames Morse 910823e7c37SJames Morse if (m->ctx->match_pmg) { 911823e7c37SJames Morse *ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG; 912823e7c37SJames Morse *flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg); 913823e7c37SJames Morse } 914823e7c37SJames Morse 915823e7c37SJames Morse switch (m->type) { 916823e7c37SJames Morse case mpam_feat_msmon_csu: 917823e7c37SJames Morse *ctl_val |= MSMON_CFG_CSU_CTL_TYPE_CSU; 918823e7c37SJames Morse 919823e7c37SJames Morse if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props)) 920823e7c37SJames Morse *flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL, ctx->csu_exclude_clean); 921823e7c37SJames Morse 922823e7c37SJames Morse break; 923823e7c37SJames Morse case mpam_feat_msmon_mbwu: 924823e7c37SJames Morse *ctl_val |= MSMON_CFG_MBWU_CTL_TYPE_MBWU; 925823e7c37SJames Morse 926823e7c37SJames Morse if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props)) 927823e7c37SJames Morse *flt_val |= FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts); 928823e7c37SJames Morse 929823e7c37SJames Morse break; 930823e7c37SJames Morse default: 931823e7c37SJames Morse pr_warn("Unexpected monitor type %d\n", m->type); 932823e7c37SJames Morse } 933823e7c37SJames Morse } 934823e7c37SJames Morse 935823e7c37SJames Morse static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, 936823e7c37SJames Morse u32 *flt_val) 937823e7c37SJames Morse { 938823e7c37SJames Morse struct mpam_msc *msc = m->ris->vmsc->msc; 939823e7c37SJames Morse 940823e7c37SJames Morse switch (m->type) { 941823e7c37SJames Morse case mpam_feat_msmon_csu: 942823e7c37SJames Morse *ctl_val = mpam_read_monsel_reg(msc, CFG_CSU_CTL); 943823e7c37SJames Morse *flt_val = mpam_read_monsel_reg(msc, CFG_CSU_FLT); 944823e7c37SJames Morse break; 945823e7c37SJames Morse case mpam_feat_msmon_mbwu: 946823e7c37SJames Morse *ctl_val = mpam_read_monsel_reg(msc, CFG_MBWU_CTL); 947823e7c37SJames Morse *flt_val = mpam_read_monsel_reg(msc, CFG_MBWU_FLT); 948823e7c37SJames Morse break; 949823e7c37SJames Morse default: 950823e7c37SJames Morse pr_warn("Unexpected monitor type %d\n", m->type); 951823e7c37SJames Morse } 952823e7c37SJames Morse } 953823e7c37SJames Morse 954823e7c37SJames Morse /* Remove values set by the hardware to prevent apparent mismatches. */ 955823e7c37SJames Morse static inline void clean_msmon_ctl_val(u32 *cur_ctl) 956823e7c37SJames Morse { 957823e7c37SJames Morse *cur_ctl &= ~MSMON_CFG_x_CTL_OFLOW_STATUS; 958823e7c37SJames Morse } 959823e7c37SJames Morse 960823e7c37SJames Morse static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, 961823e7c37SJames Morse u32 flt_val) 962823e7c37SJames Morse { 963823e7c37SJames Morse struct mpam_msc *msc = m->ris->vmsc->msc; 964823e7c37SJames Morse 965823e7c37SJames Morse /* 966823e7c37SJames Morse * Write the ctl_val with the enable bit cleared, reset the counter, 967823e7c37SJames Morse * then enable counter. 968823e7c37SJames Morse */ 969823e7c37SJames Morse switch (m->type) { 970823e7c37SJames Morse case mpam_feat_msmon_csu: 971823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val); 972823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val); 973823e7c37SJames Morse mpam_write_monsel_reg(msc, CSU, 0); 974823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); 975823e7c37SJames Morse break; 976823e7c37SJames Morse case mpam_feat_msmon_mbwu: 977823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val); 978823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); 979823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); 980823e7c37SJames Morse /* Counting monitors require NRDY to be reset by software */ 981823e7c37SJames Morse mpam_write_monsel_reg(msc, MBWU, 0); 982823e7c37SJames Morse break; 983823e7c37SJames Morse default: 984823e7c37SJames Morse pr_warn("Unexpected monitor type %d\n", m->type); 985823e7c37SJames Morse } 986823e7c37SJames Morse } 987823e7c37SJames Morse 988823e7c37SJames Morse static void __ris_msmon_read(void *arg) 989823e7c37SJames Morse { 990823e7c37SJames Morse u64 now; 991823e7c37SJames Morse bool nrdy = false; 992823e7c37SJames Morse bool config_mismatch; 993823e7c37SJames Morse struct mon_read *m = arg; 994823e7c37SJames Morse struct mon_cfg *ctx = m->ctx; 995823e7c37SJames Morse struct mpam_msc_ris *ris = m->ris; 996*41e8a149SJames Morse struct msmon_mbwu_state *mbwu_state; 997823e7c37SJames Morse struct mpam_props *rprops = &ris->props; 998823e7c37SJames Morse struct mpam_msc *msc = m->ris->vmsc->msc; 999823e7c37SJames Morse u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; 1000823e7c37SJames Morse 1001823e7c37SJames Morse if (!mpam_mon_sel_lock(msc)) { 1002823e7c37SJames Morse m->err = -EIO; 1003823e7c37SJames Morse return; 1004823e7c37SJames Morse } 1005823e7c37SJames Morse mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) | 1006823e7c37SJames Morse FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); 1007823e7c37SJames Morse mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); 1008823e7c37SJames Morse 1009823e7c37SJames Morse /* 1010823e7c37SJames Morse * Read the existing configuration to avoid re-writing the same values. 1011823e7c37SJames Morse * This saves waiting for 'nrdy' on subsequent reads. 1012823e7c37SJames Morse */ 1013823e7c37SJames Morse read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt); 1014823e7c37SJames Morse clean_msmon_ctl_val(&cur_ctl); 1015823e7c37SJames Morse gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val); 1016823e7c37SJames Morse config_mismatch = cur_flt != flt_val || 1017823e7c37SJames Morse cur_ctl != (ctl_val | MSMON_CFG_x_CTL_EN); 1018823e7c37SJames Morse 1019823e7c37SJames Morse if (config_mismatch) 1020823e7c37SJames Morse write_msmon_ctl_flt_vals(m, ctl_val, flt_val); 1021823e7c37SJames Morse 1022823e7c37SJames Morse switch (m->type) { 1023823e7c37SJames Morse case mpam_feat_msmon_csu: 1024823e7c37SJames Morse now = mpam_read_monsel_reg(msc, CSU); 1025823e7c37SJames Morse if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) 1026823e7c37SJames Morse nrdy = now & MSMON___NRDY; 1027*41e8a149SJames Morse now = FIELD_GET(MSMON___VALUE, now); 1028823e7c37SJames Morse break; 1029823e7c37SJames Morse case mpam_feat_msmon_mbwu: 1030823e7c37SJames Morse now = mpam_read_monsel_reg(msc, MBWU); 1031823e7c37SJames Morse if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) 1032823e7c37SJames Morse nrdy = now & MSMON___NRDY; 1033*41e8a149SJames Morse now = FIELD_GET(MSMON___VALUE, now); 1034*41e8a149SJames Morse 1035*41e8a149SJames Morse if (nrdy) 1036*41e8a149SJames Morse break; 1037*41e8a149SJames Morse 1038*41e8a149SJames Morse mbwu_state = &ris->mbwu_state[ctx->mon]; 1039*41e8a149SJames Morse 1040*41e8a149SJames Morse /* Include bandwidth consumed before the last hardware reset */ 1041*41e8a149SJames Morse now += mbwu_state->correction; 1042823e7c37SJames Morse break; 1043823e7c37SJames Morse default: 1044823e7c37SJames Morse m->err = -EINVAL; 1045823e7c37SJames Morse } 1046823e7c37SJames Morse mpam_mon_sel_unlock(msc); 1047823e7c37SJames Morse 1048823e7c37SJames Morse if (nrdy) { 1049823e7c37SJames Morse m->err = -EBUSY; 1050823e7c37SJames Morse return; 1051823e7c37SJames Morse } 1052823e7c37SJames Morse 1053823e7c37SJames Morse *m->val += now; 1054823e7c37SJames Morse } 1055823e7c37SJames Morse 1056823e7c37SJames Morse static int _msmon_read(struct mpam_component *comp, struct mon_read *arg) 1057823e7c37SJames Morse { 1058823e7c37SJames Morse int err, any_err = 0; 1059823e7c37SJames Morse struct mpam_vmsc *vmsc; 1060823e7c37SJames Morse 1061823e7c37SJames Morse guard(srcu)(&mpam_srcu); 1062823e7c37SJames Morse list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list, 1063823e7c37SJames Morse srcu_read_lock_held(&mpam_srcu)) { 1064823e7c37SJames Morse struct mpam_msc *msc = vmsc->msc; 1065823e7c37SJames Morse struct mpam_msc_ris *ris; 1066823e7c37SJames Morse 1067823e7c37SJames Morse list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list, 1068823e7c37SJames Morse srcu_read_lock_held(&mpam_srcu)) { 1069823e7c37SJames Morse arg->ris = ris; 1070823e7c37SJames Morse 1071823e7c37SJames Morse err = smp_call_function_any(&msc->accessibility, 1072823e7c37SJames Morse __ris_msmon_read, arg, 1073823e7c37SJames Morse true); 1074823e7c37SJames Morse if (!err && arg->err) 1075823e7c37SJames Morse err = arg->err; 1076823e7c37SJames Morse 1077823e7c37SJames Morse /* 1078823e7c37SJames Morse * Save one error to be returned to the caller, but 1079823e7c37SJames Morse * keep reading counters so that get reprogrammed. On 1080823e7c37SJames Morse * platforms with NRDY this lets us wait once. 1081823e7c37SJames Morse */ 1082823e7c37SJames Morse if (err) 1083823e7c37SJames Morse any_err = err; 1084823e7c37SJames Morse } 1085823e7c37SJames Morse } 1086823e7c37SJames Morse 1087823e7c37SJames Morse return any_err; 1088823e7c37SJames Morse } 1089823e7c37SJames Morse 1090823e7c37SJames Morse int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, 1091823e7c37SJames Morse enum mpam_device_features type, u64 *val) 1092823e7c37SJames Morse { 1093823e7c37SJames Morse int err; 1094823e7c37SJames Morse struct mon_read arg; 1095823e7c37SJames Morse u64 wait_jiffies = 0; 1096823e7c37SJames Morse struct mpam_props *cprops = &comp->class->props; 1097823e7c37SJames Morse 1098823e7c37SJames Morse might_sleep(); 1099823e7c37SJames Morse 1100823e7c37SJames Morse if (!mpam_is_enabled()) 1101823e7c37SJames Morse return -EIO; 1102823e7c37SJames Morse 1103823e7c37SJames Morse if (!mpam_has_feature(type, cprops)) 1104823e7c37SJames Morse return -EOPNOTSUPP; 1105823e7c37SJames Morse 1106823e7c37SJames Morse arg = (struct mon_read) { 1107823e7c37SJames Morse .ctx = ctx, 1108823e7c37SJames Morse .type = type, 1109823e7c37SJames Morse .val = val, 1110823e7c37SJames Morse }; 1111823e7c37SJames Morse *val = 0; 1112823e7c37SJames Morse 1113823e7c37SJames Morse err = _msmon_read(comp, &arg); 1114823e7c37SJames Morse if (err == -EBUSY && comp->class->nrdy_usec) 1115823e7c37SJames Morse wait_jiffies = usecs_to_jiffies(comp->class->nrdy_usec); 1116823e7c37SJames Morse 1117823e7c37SJames Morse while (wait_jiffies) 1118823e7c37SJames Morse wait_jiffies = schedule_timeout_uninterruptible(wait_jiffies); 1119823e7c37SJames Morse 1120823e7c37SJames Morse if (err == -EBUSY) { 1121823e7c37SJames Morse arg = (struct mon_read) { 1122823e7c37SJames Morse .ctx = ctx, 1123823e7c37SJames Morse .type = type, 1124823e7c37SJames Morse .val = val, 1125823e7c37SJames Morse }; 1126823e7c37SJames Morse *val = 0; 1127823e7c37SJames Morse 1128823e7c37SJames Morse err = _msmon_read(comp, &arg); 1129823e7c37SJames Morse } 1130823e7c37SJames Morse 1131823e7c37SJames Morse return err; 1132823e7c37SJames Morse } 1133823e7c37SJames Morse 1134f188a36cSJames Morse static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) 1135f188a36cSJames Morse { 1136f188a36cSJames Morse u32 num_words, msb; 1137f188a36cSJames Morse u32 bm = ~0; 1138f188a36cSJames Morse int i; 1139f188a36cSJames Morse 1140f188a36cSJames Morse lockdep_assert_held(&msc->part_sel_lock); 1141f188a36cSJames Morse 1142f188a36cSJames Morse if (wd == 0) 1143f188a36cSJames Morse return; 1144f188a36cSJames Morse 1145f188a36cSJames Morse /* 1146f188a36cSJames Morse * Write all ~0 to all but the last 32bit-word, which may 1147f188a36cSJames Morse * have fewer bits... 1148f188a36cSJames Morse */ 1149f188a36cSJames Morse num_words = DIV_ROUND_UP(wd, 32); 1150f188a36cSJames Morse for (i = 0; i < num_words - 1; i++, reg += sizeof(bm)) 1151f188a36cSJames Morse __mpam_write_reg(msc, reg, bm); 1152f188a36cSJames Morse 1153f188a36cSJames Morse /* 1154f188a36cSJames Morse * ....and then the last (maybe) partial 32bit word. When wd is a 1155f188a36cSJames Morse * multiple of 32, msb should be 31 to write a full 32bit word. 1156f188a36cSJames Morse */ 1157f188a36cSJames Morse msb = (wd - 1) % 32; 1158f188a36cSJames Morse bm = GENMASK(msb, 0); 1159f188a36cSJames Morse __mpam_write_reg(msc, reg, bm); 1160f188a36cSJames Morse } 1161f188a36cSJames Morse 116209b89d2aSJames Morse /* Called via IPI. Call while holding an SRCU reference */ 116309b89d2aSJames Morse static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, 116409b89d2aSJames Morse struct mpam_config *cfg) 1165f188a36cSJames Morse { 1166880df85dSJames Morse u32 pri_val = 0; 1167880df85dSJames Morse u16 cmax = MPAMCFG_CMAX_CMAX; 1168f188a36cSJames Morse struct mpam_msc *msc = ris->vmsc->msc; 1169f188a36cSJames Morse struct mpam_props *rprops = &ris->props; 1170880df85dSJames Morse u16 dspri = GENMASK(rprops->dspri_wd, 0); 1171880df85dSJames Morse u16 intpri = GENMASK(rprops->intpri_wd, 0); 1172f188a36cSJames Morse 1173f188a36cSJames Morse mutex_lock(&msc->part_sel_lock); 1174f188a36cSJames Morse __mpam_part_sel(ris->ris_idx, partid, msc); 1175f188a36cSJames Morse 1176880df85dSJames Morse if (mpam_has_feature(mpam_feat_partid_nrw, rprops)) { 1177880df85dSJames Morse /* Update the intpartid mapping */ 1178880df85dSJames Morse mpam_write_partsel_reg(msc, INTPARTID, 1179880df85dSJames Morse MPAMCFG_INTPARTID_INTERNAL | partid); 1180880df85dSJames Morse 1181880df85dSJames Morse /* 1182880df85dSJames Morse * Then switch to the 'internal' partid to update the 1183880df85dSJames Morse * configuration. 1184880df85dSJames Morse */ 1185880df85dSJames Morse __mpam_intpart_sel(ris->ris_idx, partid, msc); 1186880df85dSJames Morse } 1187880df85dSJames Morse 118809b89d2aSJames Morse if (mpam_has_feature(mpam_feat_cpor_part, rprops) && 118909b89d2aSJames Morse mpam_has_feature(mpam_feat_cpor_part, cfg)) { 119009b89d2aSJames Morse if (cfg->reset_cpbm) 1191f188a36cSJames Morse mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); 119209b89d2aSJames Morse else 119309b89d2aSJames Morse mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); 119409b89d2aSJames Morse } 1195f188a36cSJames Morse 119609b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_part, rprops) && 119709b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_part, cfg)) { 119809b89d2aSJames Morse if (cfg->reset_mbw_pbm) 1199f188a36cSJames Morse mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); 120009b89d2aSJames Morse else 120109b89d2aSJames Morse mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); 120209b89d2aSJames Morse } 1203f188a36cSJames Morse 120409b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_min, rprops) && 120509b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_min, cfg)) 1206f188a36cSJames Morse mpam_write_partsel_reg(msc, MBW_MIN, 0); 1207f188a36cSJames Morse 120809b89d2aSJames Morse if (mpam_has_feature(mpam_feat_mbw_max, rprops) && 120909b89d2aSJames Morse mpam_has_feature(mpam_feat_mbw_max, cfg)) { 121009b89d2aSJames Morse if (cfg->reset_mbw_max) 1211f188a36cSJames Morse mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX); 121209b89d2aSJames Morse else 121309b89d2aSJames Morse mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max); 121409b89d2aSJames Morse } 1215f188a36cSJames Morse 1216880df85dSJames Morse if (mpam_has_feature(mpam_feat_mbw_prop, rprops) && 1217880df85dSJames Morse mpam_has_feature(mpam_feat_mbw_prop, cfg)) 1218880df85dSJames Morse mpam_write_partsel_reg(msc, MBW_PROP, 0); 1219880df85dSJames Morse 1220880df85dSJames Morse if (mpam_has_feature(mpam_feat_cmax_cmax, rprops)) 1221880df85dSJames Morse mpam_write_partsel_reg(msc, CMAX, cmax); 1222880df85dSJames Morse 1223880df85dSJames Morse if (mpam_has_feature(mpam_feat_cmax_cmin, rprops)) 1224880df85dSJames Morse mpam_write_partsel_reg(msc, CMIN, 0); 1225880df85dSJames Morse 1226880df85dSJames Morse if (mpam_has_feature(mpam_feat_cmax_cassoc, rprops)) 1227880df85dSJames Morse mpam_write_partsel_reg(msc, CASSOC, MPAMCFG_CASSOC_CASSOC); 1228880df85dSJames Morse 1229880df85dSJames Morse if (mpam_has_feature(mpam_feat_intpri_part, rprops) || 1230880df85dSJames Morse mpam_has_feature(mpam_feat_dspri_part, rprops)) { 1231880df85dSJames Morse /* aces high? */ 1232880df85dSJames Morse if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) 1233880df85dSJames Morse intpri = 0; 1234880df85dSJames Morse if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) 1235880df85dSJames Morse dspri = 0; 1236880df85dSJames Morse 1237880df85dSJames Morse if (mpam_has_feature(mpam_feat_intpri_part, rprops)) 1238880df85dSJames Morse pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri); 1239880df85dSJames Morse if (mpam_has_feature(mpam_feat_dspri_part, rprops)) 1240880df85dSJames Morse pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri); 1241880df85dSJames Morse 1242880df85dSJames Morse mpam_write_partsel_reg(msc, PRI, pri_val); 1243880df85dSJames Morse } 1244880df85dSJames Morse 1245f188a36cSJames Morse mutex_unlock(&msc->part_sel_lock); 1246f188a36cSJames Morse } 1247f188a36cSJames Morse 1248*41e8a149SJames Morse /* Call with msc cfg_lock held */ 1249*41e8a149SJames Morse static int mpam_restore_mbwu_state(void *_ris) 1250*41e8a149SJames Morse { 1251*41e8a149SJames Morse int i; 1252*41e8a149SJames Morse struct mon_read mwbu_arg; 1253*41e8a149SJames Morse struct mpam_msc_ris *ris = _ris; 1254*41e8a149SJames Morse 1255*41e8a149SJames Morse for (i = 0; i < ris->props.num_mbwu_mon; i++) { 1256*41e8a149SJames Morse if (ris->mbwu_state[i].enabled) { 1257*41e8a149SJames Morse mwbu_arg.ris = ris; 1258*41e8a149SJames Morse mwbu_arg.ctx = &ris->mbwu_state[i].cfg; 1259*41e8a149SJames Morse mwbu_arg.type = mpam_feat_msmon_mbwu; 1260*41e8a149SJames Morse 1261*41e8a149SJames Morse __ris_msmon_read(&mwbu_arg); 1262*41e8a149SJames Morse } 1263*41e8a149SJames Morse } 1264*41e8a149SJames Morse 1265*41e8a149SJames Morse return 0; 1266*41e8a149SJames Morse } 1267*41e8a149SJames Morse 1268*41e8a149SJames Morse /* Call with MSC cfg_lock held */ 1269*41e8a149SJames Morse static int mpam_save_mbwu_state(void *arg) 1270*41e8a149SJames Morse { 1271*41e8a149SJames Morse int i; 1272*41e8a149SJames Morse u64 val; 1273*41e8a149SJames Morse struct mon_cfg *cfg; 1274*41e8a149SJames Morse u32 cur_flt, cur_ctl, mon_sel; 1275*41e8a149SJames Morse struct mpam_msc_ris *ris = arg; 1276*41e8a149SJames Morse struct msmon_mbwu_state *mbwu_state; 1277*41e8a149SJames Morse struct mpam_msc *msc = ris->vmsc->msc; 1278*41e8a149SJames Morse 1279*41e8a149SJames Morse for (i = 0; i < ris->props.num_mbwu_mon; i++) { 1280*41e8a149SJames Morse mbwu_state = &ris->mbwu_state[i]; 1281*41e8a149SJames Morse cfg = &mbwu_state->cfg; 1282*41e8a149SJames Morse 1283*41e8a149SJames Morse if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc))) 1284*41e8a149SJames Morse return -EIO; 1285*41e8a149SJames Morse 1286*41e8a149SJames Morse mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) | 1287*41e8a149SJames Morse FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); 1288*41e8a149SJames Morse mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); 1289*41e8a149SJames Morse 1290*41e8a149SJames Morse cur_flt = mpam_read_monsel_reg(msc, CFG_MBWU_FLT); 1291*41e8a149SJames Morse cur_ctl = mpam_read_monsel_reg(msc, CFG_MBWU_CTL); 1292*41e8a149SJames Morse mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0); 1293*41e8a149SJames Morse 1294*41e8a149SJames Morse val = mpam_read_monsel_reg(msc, MBWU); 1295*41e8a149SJames Morse mpam_write_monsel_reg(msc, MBWU, 0); 1296*41e8a149SJames Morse 1297*41e8a149SJames Morse cfg->mon = i; 1298*41e8a149SJames Morse cfg->pmg = FIELD_GET(MSMON_CFG_x_FLT_PMG, cur_flt); 1299*41e8a149SJames Morse cfg->match_pmg = FIELD_GET(MSMON_CFG_x_CTL_MATCH_PMG, cur_ctl); 1300*41e8a149SJames Morse cfg->partid = FIELD_GET(MSMON_CFG_x_FLT_PARTID, cur_flt); 1301*41e8a149SJames Morse mbwu_state->correction += val; 1302*41e8a149SJames Morse mbwu_state->enabled = FIELD_GET(MSMON_CFG_x_CTL_EN, cur_ctl); 1303*41e8a149SJames Morse mpam_mon_sel_unlock(msc); 1304*41e8a149SJames Morse } 1305*41e8a149SJames Morse 1306*41e8a149SJames Morse return 0; 1307*41e8a149SJames Morse } 1308*41e8a149SJames Morse 130909b89d2aSJames Morse static void mpam_init_reset_cfg(struct mpam_config *reset_cfg) 131009b89d2aSJames Morse { 131109b89d2aSJames Morse *reset_cfg = (struct mpam_config) { 131209b89d2aSJames Morse .reset_cpbm = true, 131309b89d2aSJames Morse .reset_mbw_pbm = true, 131409b89d2aSJames Morse .reset_mbw_max = true, 131509b89d2aSJames Morse }; 131609b89d2aSJames Morse bitmap_fill(reset_cfg->features, MPAM_FEATURE_LAST); 131709b89d2aSJames Morse } 131809b89d2aSJames Morse 1319475228d1SJames Morse /* 1320475228d1SJames Morse * Called via smp_call_on_cpu() to prevent migration, while still being 13213bd04fe7SJames Morse * pre-emptible. Caller must hold mpam_srcu. 1322475228d1SJames Morse */ 1323475228d1SJames Morse static int mpam_reset_ris(void *arg) 1324f188a36cSJames Morse { 1325f188a36cSJames Morse u16 partid, partid_max; 132609b89d2aSJames Morse struct mpam_config reset_cfg; 1327475228d1SJames Morse struct mpam_msc_ris *ris = arg; 1328f188a36cSJames Morse 1329f188a36cSJames Morse if (ris->in_reset_state) 1330475228d1SJames Morse return 0; 1331f188a36cSJames Morse 133209b89d2aSJames Morse mpam_init_reset_cfg(&reset_cfg); 133309b89d2aSJames Morse 1334f188a36cSJames Morse spin_lock(&partid_max_lock); 1335f188a36cSJames Morse partid_max = mpam_partid_max; 1336f188a36cSJames Morse spin_unlock(&partid_max_lock); 1337f188a36cSJames Morse for (partid = 0; partid <= partid_max; partid++) 133809b89d2aSJames Morse mpam_reprogram_ris_partid(ris, partid, &reset_cfg); 1339475228d1SJames Morse 1340475228d1SJames Morse return 0; 1341475228d1SJames Morse } 1342475228d1SJames Morse 1343475228d1SJames Morse /* 1344475228d1SJames Morse * Get the preferred CPU for this MSC. If it is accessible from this CPU, 1345475228d1SJames Morse * this CPU is preferred. This can be preempted/migrated, it will only result 1346475228d1SJames Morse * in more work. 1347475228d1SJames Morse */ 1348475228d1SJames Morse static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc) 1349475228d1SJames Morse { 1350475228d1SJames Morse int cpu = raw_smp_processor_id(); 1351475228d1SJames Morse 1352475228d1SJames Morse if (cpumask_test_cpu(cpu, &msc->accessibility)) 1353475228d1SJames Morse return cpu; 1354475228d1SJames Morse 1355475228d1SJames Morse return cpumask_first_and(&msc->accessibility, cpu_online_mask); 1356475228d1SJames Morse } 1357475228d1SJames Morse 1358475228d1SJames Morse static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *arg) 1359475228d1SJames Morse { 1360475228d1SJames Morse lockdep_assert_irqs_enabled(); 1361475228d1SJames Morse lockdep_assert_cpus_held(); 1362475228d1SJames Morse WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu))); 1363475228d1SJames Morse 1364475228d1SJames Morse return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true); 1365f188a36cSJames Morse } 1366f188a36cSJames Morse 136709b89d2aSJames Morse struct mpam_write_config_arg { 1368f188a36cSJames Morse struct mpam_msc_ris *ris; 136909b89d2aSJames Morse struct mpam_component *comp; 137009b89d2aSJames Morse u16 partid; 137109b89d2aSJames Morse }; 1372f188a36cSJames Morse 137309b89d2aSJames Morse static int __write_config(void *arg) 137409b89d2aSJames Morse { 137509b89d2aSJames Morse struct mpam_write_config_arg *c = arg; 137609b89d2aSJames Morse 137709b89d2aSJames Morse mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]); 137809b89d2aSJames Morse 137909b89d2aSJames Morse return 0; 138009b89d2aSJames Morse } 138109b89d2aSJames Morse 138209b89d2aSJames Morse static void mpam_reprogram_msc(struct mpam_msc *msc) 138309b89d2aSJames Morse { 138409b89d2aSJames Morse u16 partid; 138509b89d2aSJames Morse bool reset; 138609b89d2aSJames Morse struct mpam_config *cfg; 138709b89d2aSJames Morse struct mpam_msc_ris *ris; 138809b89d2aSJames Morse struct mpam_write_config_arg arg; 1389f188a36cSJames Morse 1390f188a36cSJames Morse /* 139109b89d2aSJames Morse * No lock for mpam_partid_max as partid_max_published has been 139209b89d2aSJames Morse * set by mpam_enabled(), so the values can no longer change. 1393f188a36cSJames Morse */ 139409b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 139509b89d2aSJames Morse 139609b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 139709b89d2aSJames Morse list_for_each_entry_srcu(ris, &msc->ris, msc_list, 139809b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 139909b89d2aSJames Morse if (!mpam_is_enabled() && !ris->in_reset_state) { 140009b89d2aSJames Morse mpam_touch_msc(msc, &mpam_reset_ris, ris); 140109b89d2aSJames Morse ris->in_reset_state = true; 140209b89d2aSJames Morse continue; 1403f188a36cSJames Morse } 140409b89d2aSJames Morse 140509b89d2aSJames Morse arg.comp = ris->vmsc->comp; 140609b89d2aSJames Morse arg.ris = ris; 140709b89d2aSJames Morse reset = true; 140809b89d2aSJames Morse for (partid = 0; partid <= mpam_partid_max; partid++) { 140909b89d2aSJames Morse cfg = &ris->vmsc->comp->cfg[partid]; 141009b89d2aSJames Morse if (!bitmap_empty(cfg->features, MPAM_FEATURE_LAST)) 141109b89d2aSJames Morse reset = false; 141209b89d2aSJames Morse 141309b89d2aSJames Morse arg.partid = partid; 141409b89d2aSJames Morse mpam_touch_msc(msc, __write_config, &arg); 141509b89d2aSJames Morse } 141609b89d2aSJames Morse ris->in_reset_state = reset; 1417*41e8a149SJames Morse 1418*41e8a149SJames Morse if (mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props)) 1419*41e8a149SJames Morse mpam_touch_msc(msc, &mpam_restore_mbwu_state, ris); 142009b89d2aSJames Morse } 142109b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 1422f188a36cSJames Morse } 1423f188a36cSJames Morse 142449aa621cSJames Morse static void _enable_percpu_irq(void *_irq) 142549aa621cSJames Morse { 142649aa621cSJames Morse int *irq = _irq; 142749aa621cSJames Morse 142849aa621cSJames Morse enable_percpu_irq(*irq, IRQ_TYPE_NONE); 142949aa621cSJames Morse } 143049aa621cSJames Morse 14318f8d0ac1SJames Morse static int mpam_cpu_online(unsigned int cpu) 14328f8d0ac1SJames Morse { 1433f188a36cSJames Morse struct mpam_msc *msc; 1434f188a36cSJames Morse 1435f188a36cSJames Morse guard(srcu)(&mpam_srcu); 1436f188a36cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 1437f188a36cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1438f188a36cSJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 1439f188a36cSJames Morse continue; 1440f188a36cSJames Morse 144149aa621cSJames Morse if (msc->reenable_error_ppi) 144249aa621cSJames Morse _enable_percpu_irq(&msc->reenable_error_ppi); 144349aa621cSJames Morse 1444f188a36cSJames Morse if (atomic_fetch_inc(&msc->online_refs) == 0) 144509b89d2aSJames Morse mpam_reprogram_msc(msc); 1446f188a36cSJames Morse } 1447f188a36cSJames Morse 14488f8d0ac1SJames Morse return 0; 14498f8d0ac1SJames Morse } 14508f8d0ac1SJames Morse 14518f8d0ac1SJames Morse /* Before mpam is enabled, try to probe new MSC */ 14528f8d0ac1SJames Morse static int mpam_discovery_cpu_online(unsigned int cpu) 14538f8d0ac1SJames Morse { 14548f8d0ac1SJames Morse int err = 0; 14558f8d0ac1SJames Morse struct mpam_msc *msc; 14568f8d0ac1SJames Morse bool new_device_probed = false; 14578f8d0ac1SJames Morse 14583796f75aSJames Morse if (mpam_is_enabled()) 14593796f75aSJames Morse return 0; 14603796f75aSJames Morse 14618f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 14628f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 14638f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 14648f8d0ac1SJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 14658f8d0ac1SJames Morse continue; 14668f8d0ac1SJames Morse 14678f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 14688f8d0ac1SJames Morse if (!msc->probed) 14698f8d0ac1SJames Morse err = mpam_msc_hw_probe(msc); 14708f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 14718f8d0ac1SJames Morse 14728f8d0ac1SJames Morse if (err) 14738f8d0ac1SJames Morse break; 14748f8d0ac1SJames Morse new_device_probed = true; 14758f8d0ac1SJames Morse } 14768f8d0ac1SJames Morse 14778f8d0ac1SJames Morse if (new_device_probed && !err) 14788f8d0ac1SJames Morse schedule_work(&mpam_enable_work); 14798f8d0ac1SJames Morse if (err) { 14808f8d0ac1SJames Morse mpam_disable_reason = "error during probing"; 14818f8d0ac1SJames Morse schedule_work(&mpam_broken_work); 14828f8d0ac1SJames Morse } 14838f8d0ac1SJames Morse 14848f8d0ac1SJames Morse return err; 14858f8d0ac1SJames Morse } 14868f8d0ac1SJames Morse 14878f8d0ac1SJames Morse static int mpam_cpu_offline(unsigned int cpu) 14888f8d0ac1SJames Morse { 1489f188a36cSJames Morse struct mpam_msc *msc; 1490f188a36cSJames Morse 1491f188a36cSJames Morse guard(srcu)(&mpam_srcu); 1492f188a36cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 1493f188a36cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 1494f188a36cSJames Morse if (!cpumask_test_cpu(cpu, &msc->accessibility)) 1495f188a36cSJames Morse continue; 1496f188a36cSJames Morse 149749aa621cSJames Morse if (msc->reenable_error_ppi) 149849aa621cSJames Morse disable_percpu_irq(msc->reenable_error_ppi); 149949aa621cSJames Morse 150009b89d2aSJames Morse if (atomic_dec_and_test(&msc->online_refs)) { 150109b89d2aSJames Morse struct mpam_msc_ris *ris; 150209b89d2aSJames Morse 150309b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 150409b89d2aSJames Morse list_for_each_entry_srcu(ris, &msc->ris, msc_list, 150509b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 150609b89d2aSJames Morse mpam_touch_msc(msc, &mpam_reset_ris, ris); 150709b89d2aSJames Morse 150809b89d2aSJames Morse /* 150909b89d2aSJames Morse * The reset state for non-zero partid may be 151009b89d2aSJames Morse * lost while the CPUs are offline. 151109b89d2aSJames Morse */ 151209b89d2aSJames Morse ris->in_reset_state = false; 1513*41e8a149SJames Morse 1514*41e8a149SJames Morse if (mpam_is_enabled()) 1515*41e8a149SJames Morse mpam_touch_msc(msc, &mpam_save_mbwu_state, ris); 151609b89d2aSJames Morse } 151709b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 151809b89d2aSJames Morse } 1519f188a36cSJames Morse } 1520f188a36cSJames Morse 15218f8d0ac1SJames Morse return 0; 15228f8d0ac1SJames Morse } 15238f8d0ac1SJames Morse 15248f8d0ac1SJames Morse static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online), 15258f8d0ac1SJames Morse int (*offline)(unsigned int offline), 15268f8d0ac1SJames Morse char *name) 15278f8d0ac1SJames Morse { 15288f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 15298f8d0ac1SJames Morse if (mpam_cpuhp_state) { 15308f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 15318f8d0ac1SJames Morse mpam_cpuhp_state = 0; 15328f8d0ac1SJames Morse } 15338f8d0ac1SJames Morse 15348f8d0ac1SJames Morse mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online, 15358f8d0ac1SJames Morse offline); 15368f8d0ac1SJames Morse if (mpam_cpuhp_state <= 0) { 15378f8d0ac1SJames Morse pr_err("Failed to register cpuhp callbacks"); 15388f8d0ac1SJames Morse mpam_cpuhp_state = 0; 15398f8d0ac1SJames Morse } 15408f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 15418f8d0ac1SJames Morse } 15428f8d0ac1SJames Morse 154349aa621cSJames Morse static int __setup_ppi(struct mpam_msc *msc) 154449aa621cSJames Morse { 154549aa621cSJames Morse int cpu; 154649aa621cSJames Morse 154749aa621cSJames Morse msc->error_dev_id = alloc_percpu(struct mpam_msc *); 154849aa621cSJames Morse if (!msc->error_dev_id) 154949aa621cSJames Morse return -ENOMEM; 155049aa621cSJames Morse 155149aa621cSJames Morse for_each_cpu(cpu, &msc->accessibility) 155249aa621cSJames Morse *per_cpu_ptr(msc->error_dev_id, cpu) = msc; 155349aa621cSJames Morse 155449aa621cSJames Morse return 0; 155549aa621cSJames Morse } 155649aa621cSJames Morse 155749aa621cSJames Morse static int mpam_msc_setup_error_irq(struct mpam_msc *msc) 155849aa621cSJames Morse { 155949aa621cSJames Morse int irq; 156049aa621cSJames Morse 156149aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 156249aa621cSJames Morse if (irq <= 0) 156349aa621cSJames Morse return 0; 156449aa621cSJames Morse 156549aa621cSJames Morse /* Allocate and initialise the percpu device pointer for PPI */ 156649aa621cSJames Morse if (irq_is_percpu(irq)) 156749aa621cSJames Morse return __setup_ppi(msc); 156849aa621cSJames Morse 156949aa621cSJames Morse /* sanity check: shared interrupts can be routed anywhere? */ 157049aa621cSJames Morse if (!cpumask_equal(&msc->accessibility, cpu_possible_mask)) { 157149aa621cSJames Morse pr_err_once("msc:%u is a private resource with a shared error interrupt", 157249aa621cSJames Morse msc->id); 157349aa621cSJames Morse return -EINVAL; 157449aa621cSJames Morse } 157549aa621cSJames Morse 157649aa621cSJames Morse return 0; 157749aa621cSJames Morse } 157849aa621cSJames Morse 157901fb4b82SJames Morse /* 1580f04046f2SJames Morse * An MSC can control traffic from a set of CPUs, but may only be accessible 1581f04046f2SJames Morse * from a (hopefully wider) set of CPUs. The common reason for this is power 1582f04046f2SJames Morse * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 1583f04046f2SJames Morse * corresponding cache may also be powered off. By making accesses from 1584f04046f2SJames Morse * one of those CPUs, we ensure we don't access a cache that's powered off. 1585f04046f2SJames Morse */ 1586f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc) 1587f04046f2SJames Morse { 1588f04046f2SJames Morse u32 affinity_id; 1589f04046f2SJames Morse int err; 1590f04046f2SJames Morse 1591f04046f2SJames Morse err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 1592f04046f2SJames Morse &affinity_id); 1593f04046f2SJames Morse if (err) 1594f04046f2SJames Morse cpumask_copy(&msc->accessibility, cpu_possible_mask); 1595f04046f2SJames Morse else 1596f04046f2SJames Morse acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 1597f04046f2SJames Morse } 1598f04046f2SJames Morse 159901fb4b82SJames Morse /* 160001fb4b82SJames Morse * There are two ways of reaching a struct mpam_msc_ris. Via the 160101fb4b82SJames Morse * class->component->vmsc->ris, or via the msc. 160201fb4b82SJames Morse * When destroying the msc, the other side needs unlinking and cleaning up too. 160301fb4b82SJames Morse */ 1604f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc) 1605f04046f2SJames Morse { 1606f04046f2SJames Morse struct platform_device *pdev = msc->pdev; 160701fb4b82SJames Morse struct mpam_msc_ris *ris, *tmp; 1608f04046f2SJames Morse 1609f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 1610f04046f2SJames Morse 161101fb4b82SJames Morse list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) 161201fb4b82SJames Morse mpam_ris_destroy(ris); 161301fb4b82SJames Morse 1614f04046f2SJames Morse list_del_rcu(&msc->all_msc_list); 1615f04046f2SJames Morse platform_set_drvdata(pdev, NULL); 161601fb4b82SJames Morse 161701fb4b82SJames Morse add_to_garbage(msc); 1618f04046f2SJames Morse } 1619f04046f2SJames Morse 1620f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev) 1621f04046f2SJames Morse { 1622f04046f2SJames Morse struct mpam_msc *msc = platform_get_drvdata(pdev); 1623f04046f2SJames Morse 1624f04046f2SJames Morse mutex_lock(&mpam_list_lock); 1625f04046f2SJames Morse mpam_msc_destroy(msc); 1626f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 1627f04046f2SJames Morse 162801fb4b82SJames Morse mpam_free_garbage(); 1629f04046f2SJames Morse } 1630f04046f2SJames Morse 1631f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 1632f04046f2SJames Morse { 1633f04046f2SJames Morse int err; 1634f04046f2SJames Morse u32 tmp; 1635f04046f2SJames Morse struct mpam_msc *msc; 1636f04046f2SJames Morse struct resource *msc_res; 1637f04046f2SJames Morse struct device *dev = &pdev->dev; 1638f04046f2SJames Morse 1639f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 1640f04046f2SJames Morse 1641f04046f2SJames Morse msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 1642f04046f2SJames Morse if (!msc) 1643f04046f2SJames Morse return ERR_PTR(-ENOMEM); 164401fb4b82SJames Morse init_garbage(&msc->garbage); 164501fb4b82SJames Morse msc->garbage.pdev = pdev; 1646f04046f2SJames Morse 1647f04046f2SJames Morse err = devm_mutex_init(dev, &msc->probe_lock); 1648f04046f2SJames Morse if (err) 1649f04046f2SJames Morse return ERR_PTR(err); 1650f04046f2SJames Morse 1651f04046f2SJames Morse err = devm_mutex_init(dev, &msc->part_sel_lock); 1652f04046f2SJames Morse if (err) 1653f04046f2SJames Morse return ERR_PTR(err); 1654f04046f2SJames Morse 165549aa621cSJames Morse err = devm_mutex_init(dev, &msc->error_irq_lock); 165649aa621cSJames Morse if (err) 165749aa621cSJames Morse return ERR_PTR(err); 165809b89d2aSJames Morse 165909b89d2aSJames Morse err = devm_mutex_init(dev, &msc->cfg_lock); 166009b89d2aSJames Morse if (err) 166109b89d2aSJames Morse return ERR_PTR(err); 166209b89d2aSJames Morse 1663d02beb06SJames Morse mpam_mon_sel_lock_init(msc); 1664f04046f2SJames Morse msc->id = pdev->id; 1665f04046f2SJames Morse msc->pdev = pdev; 1666f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->all_msc_list); 1667f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->ris); 1668f04046f2SJames Morse 1669f04046f2SJames Morse update_msc_accessibility(msc); 1670f04046f2SJames Morse if (cpumask_empty(&msc->accessibility)) { 1671f04046f2SJames Morse dev_err_once(dev, "MSC is not accessible from any CPU!"); 1672f04046f2SJames Morse return ERR_PTR(-EINVAL); 1673f04046f2SJames Morse } 1674f04046f2SJames Morse 167549aa621cSJames Morse err = mpam_msc_setup_error_irq(msc); 167649aa621cSJames Morse if (err) 167749aa621cSJames Morse return ERR_PTR(err); 167849aa621cSJames Morse 1679f04046f2SJames Morse if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 1680f04046f2SJames Morse msc->iface = MPAM_IFACE_MMIO; 1681f04046f2SJames Morse else 1682f04046f2SJames Morse msc->iface = MPAM_IFACE_PCC; 1683f04046f2SJames Morse 1684f04046f2SJames Morse if (msc->iface == MPAM_IFACE_MMIO) { 1685f04046f2SJames Morse void __iomem *io; 1686f04046f2SJames Morse 1687f04046f2SJames Morse io = devm_platform_get_and_ioremap_resource(pdev, 0, 1688f04046f2SJames Morse &msc_res); 1689f04046f2SJames Morse if (IS_ERR(io)) { 1690f04046f2SJames Morse dev_err_once(dev, "Failed to map MSC base address\n"); 1691f04046f2SJames Morse return ERR_CAST(io); 1692f04046f2SJames Morse } 1693f04046f2SJames Morse msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 1694f04046f2SJames Morse msc->mapped_hwpage = io; 1695f04046f2SJames Morse } else { 1696f04046f2SJames Morse return ERR_PTR(-EINVAL); 1697f04046f2SJames Morse } 1698f04046f2SJames Morse 1699f04046f2SJames Morse list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 1700f04046f2SJames Morse platform_set_drvdata(pdev, msc); 1701f04046f2SJames Morse 1702f04046f2SJames Morse return msc; 1703f04046f2SJames Morse } 1704f04046f2SJames Morse 1705f04046f2SJames Morse static int fw_num_msc; 1706f04046f2SJames Morse 1707f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev) 1708f04046f2SJames Morse { 1709f04046f2SJames Morse int err; 1710f04046f2SJames Morse struct mpam_msc *msc = NULL; 1711f04046f2SJames Morse void *plat_data = pdev->dev.platform_data; 1712f04046f2SJames Morse 1713f04046f2SJames Morse mutex_lock(&mpam_list_lock); 1714f04046f2SJames Morse msc = do_mpam_msc_drv_probe(pdev); 1715f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 1716f04046f2SJames Morse 1717f04046f2SJames Morse if (IS_ERR(msc)) 1718f04046f2SJames Morse return PTR_ERR(msc); 1719f04046f2SJames Morse 1720f04046f2SJames Morse /* Create RIS entries described by firmware */ 1721f04046f2SJames Morse err = acpi_mpam_parse_resources(msc, plat_data); 1722f04046f2SJames Morse if (err) { 1723f04046f2SJames Morse mpam_msc_drv_remove(pdev); 1724f04046f2SJames Morse return err; 1725f04046f2SJames Morse } 1726f04046f2SJames Morse 1727f04046f2SJames Morse if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 17288f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL, 17298f8d0ac1SJames Morse "mpam:drv_probe"); 1730f04046f2SJames Morse 1731f04046f2SJames Morse return 0; 1732f04046f2SJames Morse } 1733f04046f2SJames Morse 1734f04046f2SJames Morse static struct platform_driver mpam_msc_driver = { 1735f04046f2SJames Morse .driver = { 1736f04046f2SJames Morse .name = "mpam_msc", 1737f04046f2SJames Morse }, 1738f04046f2SJames Morse .probe = mpam_msc_drv_probe, 1739f04046f2SJames Morse .remove = mpam_msc_drv_remove, 1740f04046f2SJames Morse }; 1741f04046f2SJames Morse 1742c10ca83aSJames Morse /* Any of these features mean the BWA_WD field is valid. */ 1743c10ca83aSJames Morse static bool mpam_has_bwa_wd_feature(struct mpam_props *props) 1744c10ca83aSJames Morse { 1745c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_min, props)) 1746c10ca83aSJames Morse return true; 1747c10ca83aSJames Morse if (mpam_has_feature(mpam_feat_mbw_max, props)) 1748c10ca83aSJames Morse return true; 1749880df85dSJames Morse if (mpam_has_feature(mpam_feat_mbw_prop, props)) 1750880df85dSJames Morse return true; 1751880df85dSJames Morse return false; 1752880df85dSJames Morse } 1753880df85dSJames Morse 1754880df85dSJames Morse /* Any of these features mean the CMAX_WD field is valid. */ 1755880df85dSJames Morse static bool mpam_has_cmax_wd_feature(struct mpam_props *props) 1756880df85dSJames Morse { 1757880df85dSJames Morse if (mpam_has_feature(mpam_feat_cmax_cmax, props)) 1758880df85dSJames Morse return true; 1759880df85dSJames Morse if (mpam_has_feature(mpam_feat_cmax_cmin, props)) 1760880df85dSJames Morse return true; 1761c10ca83aSJames Morse return false; 1762c10ca83aSJames Morse } 1763c10ca83aSJames Morse 1764c10ca83aSJames Morse #define MISMATCHED_HELPER(parent, child, helper, field, alias) \ 1765c10ca83aSJames Morse helper(parent) && \ 1766c10ca83aSJames Morse ((helper(child) && (parent)->field != (child)->field) || \ 1767c10ca83aSJames Morse (!helper(child) && !(alias))) 1768c10ca83aSJames Morse 1769c10ca83aSJames Morse #define MISMATCHED_FEAT(parent, child, feat, field, alias) \ 1770c10ca83aSJames Morse mpam_has_feature((feat), (parent)) && \ 1771c10ca83aSJames Morse ((mpam_has_feature((feat), (child)) && (parent)->field != (child)->field) || \ 1772c10ca83aSJames Morse (!mpam_has_feature((feat), (child)) && !(alias))) 1773c10ca83aSJames Morse 1774c10ca83aSJames Morse #define CAN_MERGE_FEAT(parent, child, feat, alias) \ 1775c10ca83aSJames Morse (alias) && !mpam_has_feature((feat), (parent)) && \ 1776c10ca83aSJames Morse mpam_has_feature((feat), (child)) 1777c10ca83aSJames Morse 1778c10ca83aSJames Morse /* 1779c10ca83aSJames Morse * Combine two props fields. 1780c10ca83aSJames Morse * If this is for controls that alias the same resource, it is safe to just 1781c10ca83aSJames Morse * copy the values over. If two aliasing controls implement the same scheme 1782c10ca83aSJames Morse * a safe value must be picked. 1783c10ca83aSJames Morse * For non-aliasing controls, these control different resources, and the 1784c10ca83aSJames Morse * resulting safe value must be compatible with both. When merging values in 1785c10ca83aSJames Morse * the tree, all the aliasing resources must be handled first. 1786c10ca83aSJames Morse * On mismatch, parent is modified. 1787c10ca83aSJames Morse */ 1788c10ca83aSJames Morse static void __props_mismatch(struct mpam_props *parent, 1789c10ca83aSJames Morse struct mpam_props *child, bool alias) 1790c10ca83aSJames Morse { 1791c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) { 1792c10ca83aSJames Morse parent->cpbm_wd = child->cpbm_wd; 1793c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part, 1794c10ca83aSJames Morse cpbm_wd, alias)) { 1795c10ca83aSJames Morse pr_debug("cleared cpor_part\n"); 1796c10ca83aSJames Morse mpam_clear_feature(mpam_feat_cpor_part, parent); 1797c10ca83aSJames Morse parent->cpbm_wd = 0; 1798c10ca83aSJames Morse } 1799c10ca83aSJames Morse 1800c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) { 1801c10ca83aSJames Morse parent->mbw_pbm_bits = child->mbw_pbm_bits; 1802c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part, 1803c10ca83aSJames Morse mbw_pbm_bits, alias)) { 1804c10ca83aSJames Morse pr_debug("cleared mbw_part\n"); 1805c10ca83aSJames Morse mpam_clear_feature(mpam_feat_mbw_part, parent); 1806c10ca83aSJames Morse parent->mbw_pbm_bits = 0; 1807c10ca83aSJames Morse } 1808c10ca83aSJames Morse 1809c10ca83aSJames Morse /* bwa_wd is a count of bits, fewer bits means less precision */ 1810c10ca83aSJames Morse if (alias && !mpam_has_bwa_wd_feature(parent) && 1811c10ca83aSJames Morse mpam_has_bwa_wd_feature(child)) { 1812c10ca83aSJames Morse parent->bwa_wd = child->bwa_wd; 1813c10ca83aSJames Morse } else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature, 1814c10ca83aSJames Morse bwa_wd, alias)) { 1815c10ca83aSJames Morse pr_debug("took the min bwa_wd\n"); 1816c10ca83aSJames Morse parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd); 1817c10ca83aSJames Morse } 1818c10ca83aSJames Morse 1819880df85dSJames Morse if (alias && !mpam_has_cmax_wd_feature(parent) && mpam_has_cmax_wd_feature(child)) { 1820880df85dSJames Morse parent->cmax_wd = child->cmax_wd; 1821880df85dSJames Morse } else if (MISMATCHED_HELPER(parent, child, mpam_has_cmax_wd_feature, 1822880df85dSJames Morse cmax_wd, alias)) { 1823880df85dSJames Morse pr_debug("%s took the min cmax_wd\n", __func__); 1824880df85dSJames Morse parent->cmax_wd = min(parent->cmax_wd, child->cmax_wd); 1825880df85dSJames Morse } 1826880df85dSJames Morse 1827880df85dSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_cmax_cassoc, alias)) { 1828880df85dSJames Morse parent->cassoc_wd = child->cassoc_wd; 1829880df85dSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cmax_cassoc, 1830880df85dSJames Morse cassoc_wd, alias)) { 1831880df85dSJames Morse pr_debug("%s cleared cassoc_wd\n", __func__); 1832880df85dSJames Morse mpam_clear_feature(mpam_feat_cmax_cassoc, parent); 1833880df85dSJames Morse parent->cassoc_wd = 0; 1834880df85dSJames Morse } 1835880df85dSJames Morse 1836c10ca83aSJames Morse /* For num properties, take the minimum */ 1837c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { 1838c10ca83aSJames Morse parent->num_csu_mon = child->num_csu_mon; 1839c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu, 1840c10ca83aSJames Morse num_csu_mon, alias)) { 1841c10ca83aSJames Morse pr_debug("took the min num_csu_mon\n"); 1842c10ca83aSJames Morse parent->num_csu_mon = min(parent->num_csu_mon, 1843c10ca83aSJames Morse child->num_csu_mon); 1844c10ca83aSJames Morse } 1845c10ca83aSJames Morse 1846c10ca83aSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) { 1847c10ca83aSJames Morse parent->num_mbwu_mon = child->num_mbwu_mon; 1848c10ca83aSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu, 1849c10ca83aSJames Morse num_mbwu_mon, alias)) { 1850c10ca83aSJames Morse pr_debug("took the min num_mbwu_mon\n"); 1851c10ca83aSJames Morse parent->num_mbwu_mon = min(parent->num_mbwu_mon, 1852c10ca83aSJames Morse child->num_mbwu_mon); 1853c10ca83aSJames Morse } 1854c10ca83aSJames Morse 1855880df85dSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_intpri_part, alias)) { 1856880df85dSJames Morse parent->intpri_wd = child->intpri_wd; 1857880df85dSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_intpri_part, 1858880df85dSJames Morse intpri_wd, alias)) { 1859880df85dSJames Morse pr_debug("%s took the min intpri_wd\n", __func__); 1860880df85dSJames Morse parent->intpri_wd = min(parent->intpri_wd, child->intpri_wd); 1861880df85dSJames Morse } 1862880df85dSJames Morse 1863880df85dSJames Morse if (CAN_MERGE_FEAT(parent, child, mpam_feat_dspri_part, alias)) { 1864880df85dSJames Morse parent->dspri_wd = child->dspri_wd; 1865880df85dSJames Morse } else if (MISMATCHED_FEAT(parent, child, mpam_feat_dspri_part, 1866880df85dSJames Morse dspri_wd, alias)) { 1867880df85dSJames Morse pr_debug("%s took the min dspri_wd\n", __func__); 1868880df85dSJames Morse parent->dspri_wd = min(parent->dspri_wd, child->dspri_wd); 1869880df85dSJames Morse } 1870880df85dSJames Morse 1871880df85dSJames Morse /* TODO: alias support for these two */ 1872880df85dSJames Morse /* {int,ds}pri may not have differing 0-low behaviour */ 1873880df85dSJames Morse if (mpam_has_feature(mpam_feat_intpri_part, parent) && 1874880df85dSJames Morse (!mpam_has_feature(mpam_feat_intpri_part, child) || 1875880df85dSJames Morse mpam_has_feature(mpam_feat_intpri_part_0_low, parent) != 1876880df85dSJames Morse mpam_has_feature(mpam_feat_intpri_part_0_low, child))) { 1877880df85dSJames Morse pr_debug("%s cleared intpri_part\n", __func__); 1878880df85dSJames Morse mpam_clear_feature(mpam_feat_intpri_part, parent); 1879880df85dSJames Morse mpam_clear_feature(mpam_feat_intpri_part_0_low, parent); 1880880df85dSJames Morse } 1881880df85dSJames Morse if (mpam_has_feature(mpam_feat_dspri_part, parent) && 1882880df85dSJames Morse (!mpam_has_feature(mpam_feat_dspri_part, child) || 1883880df85dSJames Morse mpam_has_feature(mpam_feat_dspri_part_0_low, parent) != 1884880df85dSJames Morse mpam_has_feature(mpam_feat_dspri_part_0_low, child))) { 1885880df85dSJames Morse pr_debug("%s cleared dspri_part\n", __func__); 1886880df85dSJames Morse mpam_clear_feature(mpam_feat_dspri_part, parent); 1887880df85dSJames Morse mpam_clear_feature(mpam_feat_dspri_part_0_low, parent); 1888880df85dSJames Morse } 1889880df85dSJames Morse 1890c10ca83aSJames Morse if (alias) { 1891c10ca83aSJames Morse /* Merge features for aliased resources */ 1892c10ca83aSJames Morse bitmap_or(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1893c10ca83aSJames Morse } else { 1894c10ca83aSJames Morse /* Clear missing features for non aliasing */ 1895c10ca83aSJames Morse bitmap_and(parent->features, parent->features, child->features, MPAM_FEATURE_LAST); 1896c10ca83aSJames Morse } 1897c10ca83aSJames Morse } 1898c10ca83aSJames Morse 1899c10ca83aSJames Morse /* 1900c10ca83aSJames Morse * If a vmsc doesn't match class feature/configuration, do the right thing(tm). 1901c10ca83aSJames Morse * For 'num' properties we can just take the minimum. 1902c10ca83aSJames Morse * For properties where the mismatched unused bits would make a difference, we 1903c10ca83aSJames Morse * nobble the class feature, as we can't configure all the resources. 1904c10ca83aSJames Morse * e.g. The L3 cache is composed of two resources with 13 and 17 portion 1905c10ca83aSJames Morse * bitmaps respectively. 1906c10ca83aSJames Morse */ 1907c10ca83aSJames Morse static void 1908c10ca83aSJames Morse __class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc) 1909c10ca83aSJames Morse { 1910c10ca83aSJames Morse struct mpam_props *cprops = &class->props; 1911c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1912c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1913c10ca83aSJames Morse 1914c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify class */ 1915c10ca83aSJames Morse 1916c10ca83aSJames Morse dev_dbg(dev, "Merging features for class:0x%lx &= vmsc:0x%lx\n", 1917c10ca83aSJames Morse (long)cprops->features, (long)vprops->features); 1918c10ca83aSJames Morse 1919c10ca83aSJames Morse /* Take the safe value for any common features */ 1920c10ca83aSJames Morse __props_mismatch(cprops, vprops, false); 1921c10ca83aSJames Morse } 1922c10ca83aSJames Morse 1923c10ca83aSJames Morse static void 1924c10ca83aSJames Morse __vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris) 1925c10ca83aSJames Morse { 1926c10ca83aSJames Morse struct mpam_props *rprops = &ris->props; 1927c10ca83aSJames Morse struct mpam_props *vprops = &vmsc->props; 1928c10ca83aSJames Morse struct device *dev = &vmsc->msc->pdev->dev; 1929c10ca83aSJames Morse 1930c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */ 1931c10ca83aSJames Morse 1932c10ca83aSJames Morse dev_dbg(dev, "Merging features for vmsc:0x%lx |= ris:0x%lx\n", 1933c10ca83aSJames Morse (long)vprops->features, (long)rprops->features); 1934c10ca83aSJames Morse 1935c10ca83aSJames Morse /* 1936c10ca83aSJames Morse * Merge mismatched features - Copy any features that aren't common, 1937c10ca83aSJames Morse * but take the safe value for any common features. 1938c10ca83aSJames Morse */ 1939c10ca83aSJames Morse __props_mismatch(vprops, rprops, true); 1940c10ca83aSJames Morse } 1941c10ca83aSJames Morse 1942c10ca83aSJames Morse /* 1943c10ca83aSJames Morse * Copy the first component's first vMSC's properties and features to the 1944c10ca83aSJames Morse * class. __class_props_mismatch() will remove conflicts. 1945c10ca83aSJames Morse * It is not possible to have a class with no components, or a component with 1946c10ca83aSJames Morse * no resources. The vMSC properties have already been built. 1947c10ca83aSJames Morse */ 1948c10ca83aSJames Morse static void mpam_enable_init_class_features(struct mpam_class *class) 1949c10ca83aSJames Morse { 1950c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1951c10ca83aSJames Morse struct mpam_component *comp; 1952c10ca83aSJames Morse 1953c10ca83aSJames Morse comp = list_first_entry(&class->components, 1954c10ca83aSJames Morse struct mpam_component, class_list); 1955c10ca83aSJames Morse vmsc = list_first_entry(&comp->vmsc, 1956c10ca83aSJames Morse struct mpam_vmsc, comp_list); 1957c10ca83aSJames Morse 1958c10ca83aSJames Morse class->props = vmsc->props; 1959c10ca83aSJames Morse } 1960c10ca83aSJames Morse 1961c10ca83aSJames Morse static void mpam_enable_merge_vmsc_features(struct mpam_component *comp) 1962c10ca83aSJames Morse { 1963c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1964c10ca83aSJames Morse struct mpam_msc_ris *ris; 1965c10ca83aSJames Morse struct mpam_class *class = comp->class; 1966c10ca83aSJames Morse 1967c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 1968c10ca83aSJames Morse list_for_each_entry(ris, &vmsc->ris, vmsc_list) { 1969c10ca83aSJames Morse __vmsc_props_mismatch(vmsc, ris); 1970c10ca83aSJames Morse class->nrdy_usec = max(class->nrdy_usec, 1971c10ca83aSJames Morse vmsc->msc->nrdy_usec); 1972c10ca83aSJames Morse } 1973c10ca83aSJames Morse } 1974c10ca83aSJames Morse } 1975c10ca83aSJames Morse 1976c10ca83aSJames Morse static void mpam_enable_merge_class_features(struct mpam_component *comp) 1977c10ca83aSJames Morse { 1978c10ca83aSJames Morse struct mpam_vmsc *vmsc; 1979c10ca83aSJames Morse struct mpam_class *class = comp->class; 1980c10ca83aSJames Morse 1981c10ca83aSJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) 1982c10ca83aSJames Morse __class_props_mismatch(class, vmsc); 1983c10ca83aSJames Morse } 1984c10ca83aSJames Morse 1985c10ca83aSJames Morse /* 1986c10ca83aSJames Morse * Merge all the common resource features into class. 1987c10ca83aSJames Morse * vmsc features are bitwise-or'd together by mpam_enable_merge_vmsc_features() 1988c10ca83aSJames Morse * as the first step so that mpam_enable_init_class_features() can initialise 1989c10ca83aSJames Morse * the class with a representative set of features. 1990c10ca83aSJames Morse * Next the mpam_enable_merge_class_features() bitwise-and's all the vmsc 1991c10ca83aSJames Morse * features to form the class features. 1992c10ca83aSJames Morse * Other features are the min/max as appropriate. 1993c10ca83aSJames Morse * 1994c10ca83aSJames Morse * To avoid walking the whole tree twice, the class->nrdy_usec property is 1995c10ca83aSJames Morse * updated when working with the vmsc as it is a max(), and doesn't need 1996c10ca83aSJames Morse * initialising first. 1997c10ca83aSJames Morse */ 1998c10ca83aSJames Morse static void mpam_enable_merge_features(struct list_head *all_classes_list) 1999c10ca83aSJames Morse { 2000c10ca83aSJames Morse struct mpam_class *class; 2001c10ca83aSJames Morse struct mpam_component *comp; 2002c10ca83aSJames Morse 2003c10ca83aSJames Morse lockdep_assert_held(&mpam_list_lock); 2004c10ca83aSJames Morse 2005c10ca83aSJames Morse list_for_each_entry(class, all_classes_list, classes_list) { 2006c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 2007c10ca83aSJames Morse mpam_enable_merge_vmsc_features(comp); 2008c10ca83aSJames Morse 2009c10ca83aSJames Morse mpam_enable_init_class_features(class); 2010c10ca83aSJames Morse 2011c10ca83aSJames Morse list_for_each_entry(comp, &class->components, class_list) 2012c10ca83aSJames Morse mpam_enable_merge_class_features(comp); 2013c10ca83aSJames Morse } 2014c10ca83aSJames Morse } 2015c10ca83aSJames Morse 201649aa621cSJames Morse static char *mpam_errcode_names[16] = { 201749aa621cSJames Morse [MPAM_ERRCODE_NONE] = "No error", 201849aa621cSJames Morse [MPAM_ERRCODE_PARTID_SEL_RANGE] = "PARTID_SEL_Range", 201949aa621cSJames Morse [MPAM_ERRCODE_REQ_PARTID_RANGE] = "Req_PARTID_Range", 202049aa621cSJames Morse [MPAM_ERRCODE_MSMONCFG_ID_RANGE] = "MSMONCFG_ID_RANGE", 202149aa621cSJames Morse [MPAM_ERRCODE_REQ_PMG_RANGE] = "Req_PMG_Range", 202249aa621cSJames Morse [MPAM_ERRCODE_MONITOR_RANGE] = "Monitor_Range", 202349aa621cSJames Morse [MPAM_ERRCODE_INTPARTID_RANGE] = "intPARTID_Range", 202449aa621cSJames Morse [MPAM_ERRCODE_UNEXPECTED_INTERNAL] = "Unexpected_INTERNAL", 202549aa621cSJames Morse [MPAM_ERRCODE_UNDEFINED_RIS_PART_SEL] = "Undefined_RIS_PART_SEL", 202649aa621cSJames Morse [MPAM_ERRCODE_RIS_NO_CONTROL] = "RIS_No_Control", 202749aa621cSJames Morse [MPAM_ERRCODE_UNDEFINED_RIS_MON_SEL] = "Undefined_RIS_MON_SEL", 202849aa621cSJames Morse [MPAM_ERRCODE_RIS_NO_MONITOR] = "RIS_No_Monitor", 202949aa621cSJames Morse [12 ... 15] = "Reserved" 203049aa621cSJames Morse }; 203149aa621cSJames Morse 203249aa621cSJames Morse static int mpam_enable_msc_ecr(void *_msc) 203349aa621cSJames Morse { 203449aa621cSJames Morse struct mpam_msc *msc = _msc; 203549aa621cSJames Morse 203649aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ECR, MPAMF_ECR_INTEN); 203749aa621cSJames Morse 203849aa621cSJames Morse return 0; 203949aa621cSJames Morse } 204049aa621cSJames Morse 204149aa621cSJames Morse /* This can run in mpam_disable(), and the interrupt handler on the same CPU */ 204249aa621cSJames Morse static int mpam_disable_msc_ecr(void *_msc) 204349aa621cSJames Morse { 204449aa621cSJames Morse struct mpam_msc *msc = _msc; 204549aa621cSJames Morse 204649aa621cSJames Morse __mpam_write_reg(msc, MPAMF_ECR, 0); 204749aa621cSJames Morse 204849aa621cSJames Morse return 0; 204949aa621cSJames Morse } 205049aa621cSJames Morse 205149aa621cSJames Morse static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) 205249aa621cSJames Morse { 205349aa621cSJames Morse u64 reg; 205449aa621cSJames Morse u16 partid; 205549aa621cSJames Morse u8 errcode, pmg, ris; 205649aa621cSJames Morse 205749aa621cSJames Morse if (WARN_ON_ONCE(!msc) || 205849aa621cSJames Morse WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), 205949aa621cSJames Morse &msc->accessibility))) 206049aa621cSJames Morse return IRQ_NONE; 206149aa621cSJames Morse 206249aa621cSJames Morse reg = mpam_msc_read_esr(msc); 206349aa621cSJames Morse 206449aa621cSJames Morse errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg); 206549aa621cSJames Morse if (!errcode) 206649aa621cSJames Morse return IRQ_NONE; 206749aa621cSJames Morse 206849aa621cSJames Morse /* Clear level triggered irq */ 206949aa621cSJames Morse mpam_msc_clear_esr(msc); 207049aa621cSJames Morse 207149aa621cSJames Morse partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg); 207249aa621cSJames Morse pmg = FIELD_GET(MPAMF_ESR_PMG, reg); 207349aa621cSJames Morse ris = FIELD_GET(MPAMF_ESR_RIS, reg); 207449aa621cSJames Morse 207549aa621cSJames Morse pr_err_ratelimited("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n", 207649aa621cSJames Morse msc->id, mpam_errcode_names[errcode], partid, pmg, 207749aa621cSJames Morse ris); 207849aa621cSJames Morse 207949aa621cSJames Morse /* Disable this interrupt. */ 208049aa621cSJames Morse mpam_disable_msc_ecr(msc); 208149aa621cSJames Morse 20823796f75aSJames Morse /* Are we racing with the thread disabling MPAM? */ 20833796f75aSJames Morse if (!mpam_is_enabled()) 20843796f75aSJames Morse return IRQ_HANDLED; 20853796f75aSJames Morse 208649aa621cSJames Morse /* 208749aa621cSJames Morse * Schedule the teardown work. Don't use a threaded IRQ as we can't 208849aa621cSJames Morse * unregister the interrupt from the threaded part of the handler. 208949aa621cSJames Morse */ 209049aa621cSJames Morse mpam_disable_reason = "hardware error interrupt"; 209149aa621cSJames Morse schedule_work(&mpam_broken_work); 209249aa621cSJames Morse 209349aa621cSJames Morse return IRQ_HANDLED; 209449aa621cSJames Morse } 209549aa621cSJames Morse 209649aa621cSJames Morse static irqreturn_t mpam_ppi_handler(int irq, void *dev_id) 209749aa621cSJames Morse { 209849aa621cSJames Morse struct mpam_msc *msc = *(struct mpam_msc **)dev_id; 209949aa621cSJames Morse 210049aa621cSJames Morse return __mpam_irq_handler(irq, msc); 210149aa621cSJames Morse } 210249aa621cSJames Morse 210349aa621cSJames Morse static irqreturn_t mpam_spi_handler(int irq, void *dev_id) 210449aa621cSJames Morse { 210549aa621cSJames Morse struct mpam_msc *msc = dev_id; 210649aa621cSJames Morse 210749aa621cSJames Morse return __mpam_irq_handler(irq, msc); 210849aa621cSJames Morse } 210949aa621cSJames Morse 211049aa621cSJames Morse static int mpam_register_irqs(void) 211149aa621cSJames Morse { 211249aa621cSJames Morse int err, irq; 211349aa621cSJames Morse struct mpam_msc *msc; 211449aa621cSJames Morse 211549aa621cSJames Morse lockdep_assert_cpus_held(); 211649aa621cSJames Morse 211749aa621cSJames Morse guard(srcu)(&mpam_srcu); 211849aa621cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 211949aa621cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 212049aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 212149aa621cSJames Morse if (irq <= 0) 212249aa621cSJames Morse continue; 212349aa621cSJames Morse 212449aa621cSJames Morse /* The MPAM spec says the interrupt can be SPI, PPI or LPI */ 212549aa621cSJames Morse /* We anticipate sharing the interrupt with other MSCs */ 212649aa621cSJames Morse if (irq_is_percpu(irq)) { 212749aa621cSJames Morse err = request_percpu_irq(irq, &mpam_ppi_handler, 212849aa621cSJames Morse "mpam:msc:error", 212949aa621cSJames Morse msc->error_dev_id); 213049aa621cSJames Morse if (err) 213149aa621cSJames Morse return err; 213249aa621cSJames Morse 213349aa621cSJames Morse msc->reenable_error_ppi = irq; 213449aa621cSJames Morse smp_call_function_many(&msc->accessibility, 213549aa621cSJames Morse &_enable_percpu_irq, &irq, 213649aa621cSJames Morse true); 213749aa621cSJames Morse } else { 213849aa621cSJames Morse err = devm_request_irq(&msc->pdev->dev, irq, 213949aa621cSJames Morse &mpam_spi_handler, IRQF_SHARED, 214049aa621cSJames Morse "mpam:msc:error", msc); 214149aa621cSJames Morse if (err) 214249aa621cSJames Morse return err; 214349aa621cSJames Morse } 214449aa621cSJames Morse 214549aa621cSJames Morse mutex_lock(&msc->error_irq_lock); 214649aa621cSJames Morse msc->error_irq_req = true; 214749aa621cSJames Morse mpam_touch_msc(msc, mpam_enable_msc_ecr, msc); 214849aa621cSJames Morse msc->error_irq_hw_enabled = true; 214949aa621cSJames Morse mutex_unlock(&msc->error_irq_lock); 215049aa621cSJames Morse } 215149aa621cSJames Morse 215249aa621cSJames Morse return 0; 215349aa621cSJames Morse } 215449aa621cSJames Morse 215549aa621cSJames Morse static void mpam_unregister_irqs(void) 215649aa621cSJames Morse { 215749aa621cSJames Morse int irq; 215849aa621cSJames Morse struct mpam_msc *msc; 215949aa621cSJames Morse 216049aa621cSJames Morse guard(cpus_read_lock)(); 216149aa621cSJames Morse guard(srcu)(&mpam_srcu); 216249aa621cSJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 216349aa621cSJames Morse srcu_read_lock_held(&mpam_srcu)) { 216449aa621cSJames Morse irq = platform_get_irq_byname_optional(msc->pdev, "error"); 216549aa621cSJames Morse if (irq <= 0) 216649aa621cSJames Morse continue; 216749aa621cSJames Morse 216849aa621cSJames Morse mutex_lock(&msc->error_irq_lock); 216949aa621cSJames Morse if (msc->error_irq_hw_enabled) { 217049aa621cSJames Morse mpam_touch_msc(msc, mpam_disable_msc_ecr, msc); 217149aa621cSJames Morse msc->error_irq_hw_enabled = false; 217249aa621cSJames Morse } 217349aa621cSJames Morse 217449aa621cSJames Morse if (msc->error_irq_req) { 217549aa621cSJames Morse if (irq_is_percpu(irq)) { 217649aa621cSJames Morse msc->reenable_error_ppi = 0; 217749aa621cSJames Morse free_percpu_irq(irq, msc->error_dev_id); 217849aa621cSJames Morse } else { 217949aa621cSJames Morse devm_free_irq(&msc->pdev->dev, irq, msc); 218049aa621cSJames Morse } 218149aa621cSJames Morse msc->error_irq_req = false; 218249aa621cSJames Morse } 218349aa621cSJames Morse mutex_unlock(&msc->error_irq_lock); 218449aa621cSJames Morse } 218549aa621cSJames Morse } 218649aa621cSJames Morse 218709b89d2aSJames Morse static void __destroy_component_cfg(struct mpam_component *comp) 218809b89d2aSJames Morse { 2189*41e8a149SJames Morse struct mpam_msc *msc; 2190*41e8a149SJames Morse struct mpam_vmsc *vmsc; 2191*41e8a149SJames Morse struct mpam_msc_ris *ris; 2192*41e8a149SJames Morse 2193*41e8a149SJames Morse lockdep_assert_held(&mpam_list_lock); 2194*41e8a149SJames Morse 219509b89d2aSJames Morse add_to_garbage(comp->cfg); 2196*41e8a149SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 2197*41e8a149SJames Morse msc = vmsc->msc; 2198*41e8a149SJames Morse 2199*41e8a149SJames Morse if (mpam_mon_sel_lock(msc)) { 2200*41e8a149SJames Morse list_for_each_entry(ris, &vmsc->ris, vmsc_list) 2201*41e8a149SJames Morse add_to_garbage(ris->mbwu_state); 2202*41e8a149SJames Morse mpam_mon_sel_unlock(msc); 2203*41e8a149SJames Morse } 2204*41e8a149SJames Morse } 220509b89d2aSJames Morse } 220609b89d2aSJames Morse 220709b89d2aSJames Morse static void mpam_reset_component_cfg(struct mpam_component *comp) 220809b89d2aSJames Morse { 220909b89d2aSJames Morse int i; 221009b89d2aSJames Morse struct mpam_props *cprops = &comp->class->props; 221109b89d2aSJames Morse 221209b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 221309b89d2aSJames Morse 221409b89d2aSJames Morse if (!comp->cfg) 221509b89d2aSJames Morse return; 221609b89d2aSJames Morse 221709b89d2aSJames Morse for (i = 0; i <= mpam_partid_max; i++) { 221809b89d2aSJames Morse comp->cfg[i] = (struct mpam_config) {}; 221909b89d2aSJames Morse if (cprops->cpbm_wd) 222009b89d2aSJames Morse comp->cfg[i].cpbm = GENMASK(cprops->cpbm_wd - 1, 0); 222109b89d2aSJames Morse if (cprops->mbw_pbm_bits) 222209b89d2aSJames Morse comp->cfg[i].mbw_pbm = GENMASK(cprops->mbw_pbm_bits - 1, 0); 222309b89d2aSJames Morse if (cprops->bwa_wd) 222409b89d2aSJames Morse comp->cfg[i].mbw_max = GENMASK(15, 16 - cprops->bwa_wd); 222509b89d2aSJames Morse } 222609b89d2aSJames Morse } 222709b89d2aSJames Morse 222809b89d2aSJames Morse static int __allocate_component_cfg(struct mpam_component *comp) 222909b89d2aSJames Morse { 2230*41e8a149SJames Morse struct mpam_vmsc *vmsc; 2231*41e8a149SJames Morse 223209b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 223309b89d2aSJames Morse 223409b89d2aSJames Morse if (comp->cfg) 223509b89d2aSJames Morse return 0; 223609b89d2aSJames Morse 223709b89d2aSJames Morse comp->cfg = kcalloc(mpam_partid_max + 1, sizeof(*comp->cfg), GFP_KERNEL); 223809b89d2aSJames Morse if (!comp->cfg) 223909b89d2aSJames Morse return -ENOMEM; 224009b89d2aSJames Morse 224109b89d2aSJames Morse /* 224209b89d2aSJames Morse * The array is free()d in one go, so only cfg[0]'s structure needs 224309b89d2aSJames Morse * to be initialised. 224409b89d2aSJames Morse */ 224509b89d2aSJames Morse init_garbage(&comp->cfg[0].garbage); 224609b89d2aSJames Morse 224709b89d2aSJames Morse mpam_reset_component_cfg(comp); 224809b89d2aSJames Morse 2249*41e8a149SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 2250*41e8a149SJames Morse struct mpam_msc *msc; 2251*41e8a149SJames Morse struct mpam_msc_ris *ris; 2252*41e8a149SJames Morse struct msmon_mbwu_state *mbwu_state; 2253*41e8a149SJames Morse 2254*41e8a149SJames Morse if (!vmsc->props.num_mbwu_mon) 2255*41e8a149SJames Morse continue; 2256*41e8a149SJames Morse 2257*41e8a149SJames Morse msc = vmsc->msc; 2258*41e8a149SJames Morse list_for_each_entry(ris, &vmsc->ris, vmsc_list) { 2259*41e8a149SJames Morse if (!ris->props.num_mbwu_mon) 2260*41e8a149SJames Morse continue; 2261*41e8a149SJames Morse 2262*41e8a149SJames Morse mbwu_state = kcalloc(ris->props.num_mbwu_mon, 2263*41e8a149SJames Morse sizeof(*ris->mbwu_state), 2264*41e8a149SJames Morse GFP_KERNEL); 2265*41e8a149SJames Morse if (!mbwu_state) { 2266*41e8a149SJames Morse __destroy_component_cfg(comp); 2267*41e8a149SJames Morse return -ENOMEM; 2268*41e8a149SJames Morse } 2269*41e8a149SJames Morse 2270*41e8a149SJames Morse init_garbage(&mbwu_state[0].garbage); 2271*41e8a149SJames Morse 2272*41e8a149SJames Morse if (mpam_mon_sel_lock(msc)) { 2273*41e8a149SJames Morse ris->mbwu_state = mbwu_state; 2274*41e8a149SJames Morse mpam_mon_sel_unlock(msc); 2275*41e8a149SJames Morse } 2276*41e8a149SJames Morse } 2277*41e8a149SJames Morse } 2278*41e8a149SJames Morse 227909b89d2aSJames Morse return 0; 228009b89d2aSJames Morse } 228109b89d2aSJames Morse 228209b89d2aSJames Morse static int mpam_allocate_config(void) 228309b89d2aSJames Morse { 228409b89d2aSJames Morse struct mpam_class *class; 228509b89d2aSJames Morse struct mpam_component *comp; 228609b89d2aSJames Morse 228709b89d2aSJames Morse lockdep_assert_held(&mpam_list_lock); 228809b89d2aSJames Morse 228909b89d2aSJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 229009b89d2aSJames Morse list_for_each_entry(comp, &class->components, class_list) { 229109b89d2aSJames Morse int err = __allocate_component_cfg(comp); 229209b89d2aSJames Morse if (err) 229309b89d2aSJames Morse return err; 229409b89d2aSJames Morse } 229509b89d2aSJames Morse } 229609b89d2aSJames Morse 229709b89d2aSJames Morse return 0; 229809b89d2aSJames Morse } 229909b89d2aSJames Morse 23008f8d0ac1SJames Morse static void mpam_enable_once(void) 23018f8d0ac1SJames Morse { 230249aa621cSJames Morse int err; 230349aa621cSJames Morse 2304bd221f9fSJames Morse /* 2305bd221f9fSJames Morse * Once the cpuhp callbacks have been changed, mpam_partid_max can no 2306bd221f9fSJames Morse * longer change. 2307bd221f9fSJames Morse */ 2308bd221f9fSJames Morse spin_lock(&partid_max_lock); 2309bd221f9fSJames Morse partid_max_published = true; 2310bd221f9fSJames Morse spin_unlock(&partid_max_lock); 2311bd221f9fSJames Morse 231249aa621cSJames Morse /* 231349aa621cSJames Morse * If all the MSC have been probed, enabling the IRQs happens next. 231449aa621cSJames Morse * That involves cross-calling to a CPU that can reach the MSC, and 231549aa621cSJames Morse * the locks must be taken in this order: 231649aa621cSJames Morse */ 231749aa621cSJames Morse cpus_read_lock(); 2318c10ca83aSJames Morse mutex_lock(&mpam_list_lock); 231909b89d2aSJames Morse do { 2320c10ca83aSJames Morse mpam_enable_merge_features(&mpam_classes); 232149aa621cSJames Morse 232249aa621cSJames Morse err = mpam_register_irqs(); 232309b89d2aSJames Morse if (err) { 232409b89d2aSJames Morse pr_warn("Failed to register irqs: %d\n", err); 232509b89d2aSJames Morse break; 232609b89d2aSJames Morse } 232749aa621cSJames Morse 232809b89d2aSJames Morse err = mpam_allocate_config(); 232909b89d2aSJames Morse if (err) { 233009b89d2aSJames Morse pr_err("Failed to allocate configuration arrays.\n"); 233109b89d2aSJames Morse break; 233209b89d2aSJames Morse } 233309b89d2aSJames Morse } while (0); 2334c10ca83aSJames Morse mutex_unlock(&mpam_list_lock); 233549aa621cSJames Morse cpus_read_unlock(); 233649aa621cSJames Morse 233749aa621cSJames Morse if (err) { 233849aa621cSJames Morse mpam_disable_reason = "Failed to enable."; 233949aa621cSJames Morse schedule_work(&mpam_broken_work); 234049aa621cSJames Morse return; 234149aa621cSJames Morse } 2342c10ca83aSJames Morse 23433796f75aSJames Morse static_branch_enable(&mpam_enabled); 23448f8d0ac1SJames Morse mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline, 23458f8d0ac1SJames Morse "mpam:online"); 23468f8d0ac1SJames Morse 2347bd221f9fSJames Morse /* Use printk() to avoid the pr_fmt adding the function name. */ 2348bd221f9fSJames Morse printk(KERN_INFO "MPAM enabled with %u PARTIDs and %u PMGs\n", 2349bd221f9fSJames Morse mpam_partid_max + 1, mpam_pmg_max + 1); 23508f8d0ac1SJames Morse } 23518f8d0ac1SJames Morse 23523bd04fe7SJames Morse static void mpam_reset_component_locked(struct mpam_component *comp) 23533bd04fe7SJames Morse { 23543bd04fe7SJames Morse struct mpam_vmsc *vmsc; 23553bd04fe7SJames Morse 23563bd04fe7SJames Morse lockdep_assert_cpus_held(); 235709b89d2aSJames Morse mpam_assert_partid_sizes_fixed(); 235809b89d2aSJames Morse 235909b89d2aSJames Morse mpam_reset_component_cfg(comp); 23603bd04fe7SJames Morse 23613bd04fe7SJames Morse guard(srcu)(&mpam_srcu); 23623bd04fe7SJames Morse list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list, 23633bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) { 23643bd04fe7SJames Morse struct mpam_msc *msc = vmsc->msc; 23653bd04fe7SJames Morse struct mpam_msc_ris *ris; 23663bd04fe7SJames Morse 23673bd04fe7SJames Morse list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list, 23683bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) { 23693bd04fe7SJames Morse if (!ris->in_reset_state) 23703bd04fe7SJames Morse mpam_touch_msc(msc, mpam_reset_ris, ris); 23713bd04fe7SJames Morse ris->in_reset_state = true; 23723bd04fe7SJames Morse } 23733bd04fe7SJames Morse } 23743bd04fe7SJames Morse } 23753bd04fe7SJames Morse 23763bd04fe7SJames Morse static void mpam_reset_class_locked(struct mpam_class *class) 23773bd04fe7SJames Morse { 23783bd04fe7SJames Morse struct mpam_component *comp; 23793bd04fe7SJames Morse 23803bd04fe7SJames Morse lockdep_assert_cpus_held(); 23813bd04fe7SJames Morse 23823bd04fe7SJames Morse guard(srcu)(&mpam_srcu); 23833bd04fe7SJames Morse list_for_each_entry_srcu(comp, &class->components, class_list, 23843bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) 23853bd04fe7SJames Morse mpam_reset_component_locked(comp); 23863bd04fe7SJames Morse } 23873bd04fe7SJames Morse 23883bd04fe7SJames Morse static void mpam_reset_class(struct mpam_class *class) 23893bd04fe7SJames Morse { 23903bd04fe7SJames Morse cpus_read_lock(); 23913bd04fe7SJames Morse mpam_reset_class_locked(class); 23923bd04fe7SJames Morse cpus_read_unlock(); 23933bd04fe7SJames Morse } 23943bd04fe7SJames Morse 23953bd04fe7SJames Morse /* 23963bd04fe7SJames Morse * Called in response to an error IRQ. 23973bd04fe7SJames Morse * All of MPAMs errors indicate a software bug, restore any modified 23983bd04fe7SJames Morse * controls to their reset values. 23993bd04fe7SJames Morse */ 24008f8d0ac1SJames Morse void mpam_disable(struct work_struct *ignored) 24018f8d0ac1SJames Morse { 24023bd04fe7SJames Morse int idx; 24033bd04fe7SJames Morse struct mpam_class *class; 24048f8d0ac1SJames Morse struct mpam_msc *msc, *tmp; 24058f8d0ac1SJames Morse 24068f8d0ac1SJames Morse mutex_lock(&mpam_cpuhp_state_lock); 24078f8d0ac1SJames Morse if (mpam_cpuhp_state) { 24088f8d0ac1SJames Morse cpuhp_remove_state(mpam_cpuhp_state); 24098f8d0ac1SJames Morse mpam_cpuhp_state = 0; 24108f8d0ac1SJames Morse } 24118f8d0ac1SJames Morse mutex_unlock(&mpam_cpuhp_state_lock); 24128f8d0ac1SJames Morse 24133796f75aSJames Morse static_branch_disable(&mpam_enabled); 24143796f75aSJames Morse 241549aa621cSJames Morse mpam_unregister_irqs(); 241649aa621cSJames Morse 24173bd04fe7SJames Morse idx = srcu_read_lock(&mpam_srcu); 24183bd04fe7SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 24193bd04fe7SJames Morse srcu_read_lock_held(&mpam_srcu)) 24203bd04fe7SJames Morse mpam_reset_class(class); 24213bd04fe7SJames Morse srcu_read_unlock(&mpam_srcu, idx); 24223bd04fe7SJames Morse 24238f8d0ac1SJames Morse mutex_lock(&mpam_list_lock); 24248f8d0ac1SJames Morse list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list) 24258f8d0ac1SJames Morse mpam_msc_destroy(msc); 24268f8d0ac1SJames Morse mutex_unlock(&mpam_list_lock); 24278f8d0ac1SJames Morse mpam_free_garbage(); 24288f8d0ac1SJames Morse 24298f8d0ac1SJames Morse pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason); 24308f8d0ac1SJames Morse } 24318f8d0ac1SJames Morse 24328f8d0ac1SJames Morse /* 24338f8d0ac1SJames Morse * Enable mpam once all devices have been probed. 24348f8d0ac1SJames Morse * Scheduled by mpam_discovery_cpu_online() once all devices have been created. 24358f8d0ac1SJames Morse * Also scheduled when new devices are probed when new CPUs come online. 24368f8d0ac1SJames Morse */ 24378f8d0ac1SJames Morse void mpam_enable(struct work_struct *work) 24388f8d0ac1SJames Morse { 24398f8d0ac1SJames Morse static atomic_t once; 24408f8d0ac1SJames Morse struct mpam_msc *msc; 24418f8d0ac1SJames Morse bool all_devices_probed = true; 24428f8d0ac1SJames Morse 24438f8d0ac1SJames Morse /* Have we probed all the hw devices? */ 24448f8d0ac1SJames Morse guard(srcu)(&mpam_srcu); 24458f8d0ac1SJames Morse list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 24468f8d0ac1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 24478f8d0ac1SJames Morse mutex_lock(&msc->probe_lock); 24488f8d0ac1SJames Morse if (!msc->probed) 24498f8d0ac1SJames Morse all_devices_probed = false; 24508f8d0ac1SJames Morse mutex_unlock(&msc->probe_lock); 24518f8d0ac1SJames Morse 24528f8d0ac1SJames Morse if (!all_devices_probed) 24538f8d0ac1SJames Morse break; 24548f8d0ac1SJames Morse } 24558f8d0ac1SJames Morse 24568f8d0ac1SJames Morse if (all_devices_probed && !atomic_fetch_inc(&once)) 24578f8d0ac1SJames Morse mpam_enable_once(); 24588f8d0ac1SJames Morse } 24598f8d0ac1SJames Morse 246009b89d2aSJames Morse #define maybe_update_config(cfg, feature, newcfg, member, changes) do { \ 246109b89d2aSJames Morse if (mpam_has_feature(feature, newcfg) && \ 246209b89d2aSJames Morse (newcfg)->member != (cfg)->member) { \ 246309b89d2aSJames Morse (cfg)->member = (newcfg)->member; \ 246409b89d2aSJames Morse mpam_set_feature(feature, cfg); \ 246509b89d2aSJames Morse \ 246609b89d2aSJames Morse (changes) = true; \ 246709b89d2aSJames Morse } \ 246809b89d2aSJames Morse } while (0) 246909b89d2aSJames Morse 247009b89d2aSJames Morse static bool mpam_update_config(struct mpam_config *cfg, 247109b89d2aSJames Morse const struct mpam_config *newcfg) 247209b89d2aSJames Morse { 247309b89d2aSJames Morse bool has_changes = false; 247409b89d2aSJames Morse 247509b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, has_changes); 247609b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, has_changes); 247709b89d2aSJames Morse maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, has_changes); 247809b89d2aSJames Morse 247909b89d2aSJames Morse return has_changes; 248009b89d2aSJames Morse } 248109b89d2aSJames Morse 248209b89d2aSJames Morse int mpam_apply_config(struct mpam_component *comp, u16 partid, 248309b89d2aSJames Morse struct mpam_config *cfg) 248409b89d2aSJames Morse { 248509b89d2aSJames Morse struct mpam_write_config_arg arg; 248609b89d2aSJames Morse struct mpam_msc_ris *ris; 248709b89d2aSJames Morse struct mpam_vmsc *vmsc; 248809b89d2aSJames Morse struct mpam_msc *msc; 248909b89d2aSJames Morse 249009b89d2aSJames Morse lockdep_assert_cpus_held(); 249109b89d2aSJames Morse 249209b89d2aSJames Morse /* Don't pass in the current config! */ 249309b89d2aSJames Morse WARN_ON_ONCE(&comp->cfg[partid] == cfg); 249409b89d2aSJames Morse 249509b89d2aSJames Morse if (!mpam_update_config(&comp->cfg[partid], cfg)) 249609b89d2aSJames Morse return 0; 249709b89d2aSJames Morse 249809b89d2aSJames Morse arg.comp = comp; 249909b89d2aSJames Morse arg.partid = partid; 250009b89d2aSJames Morse 250109b89d2aSJames Morse guard(srcu)(&mpam_srcu); 250209b89d2aSJames Morse list_for_each_entry_srcu(vmsc, &comp->vmsc, comp_list, 250309b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 250409b89d2aSJames Morse msc = vmsc->msc; 250509b89d2aSJames Morse 250609b89d2aSJames Morse mutex_lock(&msc->cfg_lock); 250709b89d2aSJames Morse list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list, 250809b89d2aSJames Morse srcu_read_lock_held(&mpam_srcu)) { 250909b89d2aSJames Morse arg.ris = ris; 251009b89d2aSJames Morse mpam_touch_msc(msc, __write_config, &arg); 251109b89d2aSJames Morse } 251209b89d2aSJames Morse mutex_unlock(&msc->cfg_lock); 251309b89d2aSJames Morse } 251409b89d2aSJames Morse 251509b89d2aSJames Morse return 0; 251609b89d2aSJames Morse } 251709b89d2aSJames Morse 2518f04046f2SJames Morse static int __init mpam_msc_driver_init(void) 2519f04046f2SJames Morse { 2520f04046f2SJames Morse if (!system_supports_mpam()) 2521f04046f2SJames Morse return -EOPNOTSUPP; 2522f04046f2SJames Morse 2523f04046f2SJames Morse init_srcu_struct(&mpam_srcu); 2524f04046f2SJames Morse 2525f04046f2SJames Morse fw_num_msc = acpi_mpam_count_msc(); 2526f04046f2SJames Morse if (fw_num_msc <= 0) { 2527f04046f2SJames Morse pr_err("No MSC devices found in firmware\n"); 2528f04046f2SJames Morse return -EINVAL; 2529f04046f2SJames Morse } 2530f04046f2SJames Morse 2531f04046f2SJames Morse return platform_driver_register(&mpam_msc_driver); 2532f04046f2SJames Morse } 2533bd221f9fSJames Morse 2534bd221f9fSJames Morse /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ 2535f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init); 2536