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> 7f04046f2SJames Morse #include <linux/arm_mpam.h> 8f04046f2SJames Morse #include <linux/cacheinfo.h> 9f04046f2SJames Morse #include <linux/cpumask.h> 10f04046f2SJames Morse #include <linux/device.h> 11f04046f2SJames Morse #include <linux/errno.h> 12f04046f2SJames Morse #include <linux/gfp.h> 13f04046f2SJames Morse #include <linux/list.h> 14f04046f2SJames Morse #include <linux/lockdep.h> 15f04046f2SJames Morse #include <linux/mutex.h> 16f04046f2SJames Morse #include <linux/platform_device.h> 17f04046f2SJames Morse #include <linux/printk.h> 18f04046f2SJames Morse #include <linux/srcu.h> 19f04046f2SJames Morse #include <linux/types.h> 20f04046f2SJames Morse 21f04046f2SJames Morse #include "mpam_internal.h" 22f04046f2SJames Morse 23f04046f2SJames Morse /* 24f04046f2SJames Morse * mpam_list_lock protects the SRCU lists when writing. Once the 25f04046f2SJames Morse * mpam_enabled key is enabled these lists are read-only, 26f04046f2SJames Morse * unless the error interrupt disables the driver. 27f04046f2SJames Morse */ 28f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock); 29f04046f2SJames Morse static LIST_HEAD(mpam_all_msc); 30f04046f2SJames Morse 31f04046f2SJames Morse struct srcu_struct mpam_srcu; 32f04046f2SJames Morse 33f04046f2SJames Morse /* 34f04046f2SJames Morse * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 35f04046f2SJames Morse * can be enabled. 36f04046f2SJames Morse */ 37f04046f2SJames Morse static atomic_t mpam_num_msc; 38f04046f2SJames Morse 39f04046f2SJames Morse /* 40*01fb4b82SJames Morse * An MSC is a physical container for controls and monitors, each identified by 41*01fb4b82SJames Morse * their RIS index. These share a base-address, interrupts and some MMIO 42*01fb4b82SJames Morse * registers. A vMSC is a virtual container for RIS in an MSC that control or 43*01fb4b82SJames Morse * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but 44*01fb4b82SJames Morse * not all RIS in an MSC share a vMSC. 45*01fb4b82SJames Morse * 46*01fb4b82SJames Morse * Components are a group of vMSC that control or monitor the same thing but 47*01fb4b82SJames Morse * are from different MSC, so have different base-address, interrupts etc. 48*01fb4b82SJames Morse * Classes are the set components of the same type. 49*01fb4b82SJames Morse * 50*01fb4b82SJames Morse * The features of a vMSC is the union of the RIS it contains. 51*01fb4b82SJames Morse * The features of a Class and Component are the common subset of the vMSC 52*01fb4b82SJames Morse * they contain. 53*01fb4b82SJames Morse * 54*01fb4b82SJames Morse * e.g. The system cache may have bandwidth controls on multiple interfaces, 55*01fb4b82SJames Morse * for regulating traffic from devices independently of traffic from CPUs. 56*01fb4b82SJames Morse * If these are two RIS in one MSC, they will be treated as controlling 57*01fb4b82SJames Morse * different things, and will not share a vMSC/component/class. 58*01fb4b82SJames Morse * 59*01fb4b82SJames Morse * e.g. The L2 may have one MSC and two RIS, one for cache-controls another 60*01fb4b82SJames Morse * for bandwidth. These two RIS are members of the same vMSC. 61*01fb4b82SJames Morse * 62*01fb4b82SJames Morse * e.g. The set of RIS that make up the L2 are grouped as a component. These 63*01fb4b82SJames Morse * are sometimes termed slices. They should be configured the same, as if there 64*01fb4b82SJames Morse * were only one. 65*01fb4b82SJames Morse * 66*01fb4b82SJames Morse * e.g. The SoC probably has more than one L2, each attached to a distinct set 67*01fb4b82SJames Morse * of CPUs. All the L2 components are grouped as a class. 68*01fb4b82SJames Morse * 69*01fb4b82SJames Morse * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, 70*01fb4b82SJames Morse * then linked via struct mpam_ris to a vmsc, component and class. 71*01fb4b82SJames Morse * The same MSC may exist under different class->component->vmsc paths, but the 72*01fb4b82SJames Morse * RIS index will be unique. 73*01fb4b82SJames Morse */ 74*01fb4b82SJames Morse LIST_HEAD(mpam_classes); 75*01fb4b82SJames Morse 76*01fb4b82SJames Morse /* List of all objects that can be free()d after synchronise_srcu() */ 77*01fb4b82SJames Morse static LLIST_HEAD(mpam_garbage); 78*01fb4b82SJames Morse 79*01fb4b82SJames Morse static inline void init_garbage(struct mpam_garbage *garbage) 80*01fb4b82SJames Morse { 81*01fb4b82SJames Morse init_llist_node(&garbage->llist); 82*01fb4b82SJames Morse } 83*01fb4b82SJames Morse 84*01fb4b82SJames Morse #define add_to_garbage(x) \ 85*01fb4b82SJames Morse do { \ 86*01fb4b82SJames Morse __typeof__(x) _x = (x); \ 87*01fb4b82SJames Morse _x->garbage.to_free = _x; \ 88*01fb4b82SJames Morse llist_add(&_x->garbage.llist, &mpam_garbage); \ 89*01fb4b82SJames Morse } while (0) 90*01fb4b82SJames Morse 91*01fb4b82SJames Morse static void mpam_free_garbage(void) 92*01fb4b82SJames Morse { 93*01fb4b82SJames Morse struct mpam_garbage *iter, *tmp; 94*01fb4b82SJames Morse struct llist_node *to_free = llist_del_all(&mpam_garbage); 95*01fb4b82SJames Morse 96*01fb4b82SJames Morse if (!to_free) 97*01fb4b82SJames Morse return; 98*01fb4b82SJames Morse 99*01fb4b82SJames Morse synchronize_srcu(&mpam_srcu); 100*01fb4b82SJames Morse 101*01fb4b82SJames Morse llist_for_each_entry_safe(iter, tmp, to_free, llist) { 102*01fb4b82SJames Morse if (iter->pdev) 103*01fb4b82SJames Morse devm_kfree(&iter->pdev->dev, iter->to_free); 104*01fb4b82SJames Morse else 105*01fb4b82SJames Morse kfree(iter->to_free); 106*01fb4b82SJames Morse } 107*01fb4b82SJames Morse } 108*01fb4b82SJames Morse 109*01fb4b82SJames Morse static struct mpam_class * 110*01fb4b82SJames Morse mpam_class_alloc(u8 level_idx, enum mpam_class_types type) 111*01fb4b82SJames Morse { 112*01fb4b82SJames Morse struct mpam_class *class; 113*01fb4b82SJames Morse 114*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 115*01fb4b82SJames Morse 116*01fb4b82SJames Morse class = kzalloc(sizeof(*class), GFP_KERNEL); 117*01fb4b82SJames Morse if (!class) 118*01fb4b82SJames Morse return ERR_PTR(-ENOMEM); 119*01fb4b82SJames Morse init_garbage(&class->garbage); 120*01fb4b82SJames Morse 121*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->components); 122*01fb4b82SJames Morse /* Affinity is updated when ris are added */ 123*01fb4b82SJames Morse class->level = level_idx; 124*01fb4b82SJames Morse class->type = type; 125*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&class->classes_list); 126*01fb4b82SJames Morse 127*01fb4b82SJames Morse list_add_rcu(&class->classes_list, &mpam_classes); 128*01fb4b82SJames Morse 129*01fb4b82SJames Morse return class; 130*01fb4b82SJames Morse } 131*01fb4b82SJames Morse 132*01fb4b82SJames Morse static void mpam_class_destroy(struct mpam_class *class) 133*01fb4b82SJames Morse { 134*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 135*01fb4b82SJames Morse 136*01fb4b82SJames Morse list_del_rcu(&class->classes_list); 137*01fb4b82SJames Morse add_to_garbage(class); 138*01fb4b82SJames Morse } 139*01fb4b82SJames Morse 140*01fb4b82SJames Morse static struct mpam_class * 141*01fb4b82SJames Morse mpam_class_find(u8 level_idx, enum mpam_class_types type) 142*01fb4b82SJames Morse { 143*01fb4b82SJames Morse struct mpam_class *class; 144*01fb4b82SJames Morse 145*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 146*01fb4b82SJames Morse 147*01fb4b82SJames Morse list_for_each_entry(class, &mpam_classes, classes_list) { 148*01fb4b82SJames Morse if (class->type == type && class->level == level_idx) 149*01fb4b82SJames Morse return class; 150*01fb4b82SJames Morse } 151*01fb4b82SJames Morse 152*01fb4b82SJames Morse return mpam_class_alloc(level_idx, type); 153*01fb4b82SJames Morse } 154*01fb4b82SJames Morse 155*01fb4b82SJames Morse static struct mpam_component * 156*01fb4b82SJames Morse mpam_component_alloc(struct mpam_class *class, int id) 157*01fb4b82SJames Morse { 158*01fb4b82SJames Morse struct mpam_component *comp; 159*01fb4b82SJames Morse 160*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 161*01fb4b82SJames Morse 162*01fb4b82SJames Morse comp = kzalloc(sizeof(*comp), GFP_KERNEL); 163*01fb4b82SJames Morse if (!comp) 164*01fb4b82SJames Morse return ERR_PTR(-ENOMEM); 165*01fb4b82SJames Morse init_garbage(&comp->garbage); 166*01fb4b82SJames Morse 167*01fb4b82SJames Morse comp->comp_id = id; 168*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->vmsc); 169*01fb4b82SJames Morse /* Affinity is updated when RIS are added */ 170*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&comp->class_list); 171*01fb4b82SJames Morse comp->class = class; 172*01fb4b82SJames Morse 173*01fb4b82SJames Morse list_add_rcu(&comp->class_list, &class->components); 174*01fb4b82SJames Morse 175*01fb4b82SJames Morse return comp; 176*01fb4b82SJames Morse } 177*01fb4b82SJames Morse 178*01fb4b82SJames Morse static void mpam_component_destroy(struct mpam_component *comp) 179*01fb4b82SJames Morse { 180*01fb4b82SJames Morse struct mpam_class *class = comp->class; 181*01fb4b82SJames Morse 182*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 183*01fb4b82SJames Morse 184*01fb4b82SJames Morse list_del_rcu(&comp->class_list); 185*01fb4b82SJames Morse add_to_garbage(comp); 186*01fb4b82SJames Morse 187*01fb4b82SJames Morse if (list_empty(&class->components)) 188*01fb4b82SJames Morse mpam_class_destroy(class); 189*01fb4b82SJames Morse } 190*01fb4b82SJames Morse 191*01fb4b82SJames Morse static struct mpam_component * 192*01fb4b82SJames Morse mpam_component_find(struct mpam_class *class, int id) 193*01fb4b82SJames Morse { 194*01fb4b82SJames Morse struct mpam_component *comp; 195*01fb4b82SJames Morse 196*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 197*01fb4b82SJames Morse 198*01fb4b82SJames Morse list_for_each_entry(comp, &class->components, class_list) { 199*01fb4b82SJames Morse if (comp->comp_id == id) 200*01fb4b82SJames Morse return comp; 201*01fb4b82SJames Morse } 202*01fb4b82SJames Morse 203*01fb4b82SJames Morse return mpam_component_alloc(class, id); 204*01fb4b82SJames Morse } 205*01fb4b82SJames Morse 206*01fb4b82SJames Morse static struct mpam_vmsc * 207*01fb4b82SJames Morse mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) 208*01fb4b82SJames Morse { 209*01fb4b82SJames Morse struct mpam_vmsc *vmsc; 210*01fb4b82SJames Morse 211*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 212*01fb4b82SJames Morse 213*01fb4b82SJames Morse vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL); 214*01fb4b82SJames Morse if (!vmsc) 215*01fb4b82SJames Morse return ERR_PTR(-ENOMEM); 216*01fb4b82SJames Morse init_garbage(&vmsc->garbage); 217*01fb4b82SJames Morse 218*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->ris); 219*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&vmsc->comp_list); 220*01fb4b82SJames Morse vmsc->comp = comp; 221*01fb4b82SJames Morse vmsc->msc = msc; 222*01fb4b82SJames Morse 223*01fb4b82SJames Morse list_add_rcu(&vmsc->comp_list, &comp->vmsc); 224*01fb4b82SJames Morse 225*01fb4b82SJames Morse return vmsc; 226*01fb4b82SJames Morse } 227*01fb4b82SJames Morse 228*01fb4b82SJames Morse static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) 229*01fb4b82SJames Morse { 230*01fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 231*01fb4b82SJames Morse 232*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 233*01fb4b82SJames Morse 234*01fb4b82SJames Morse list_del_rcu(&vmsc->comp_list); 235*01fb4b82SJames Morse add_to_garbage(vmsc); 236*01fb4b82SJames Morse 237*01fb4b82SJames Morse if (list_empty(&comp->vmsc)) 238*01fb4b82SJames Morse mpam_component_destroy(comp); 239*01fb4b82SJames Morse } 240*01fb4b82SJames Morse 241*01fb4b82SJames Morse static struct mpam_vmsc * 242*01fb4b82SJames Morse mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) 243*01fb4b82SJames Morse { 244*01fb4b82SJames Morse struct mpam_vmsc *vmsc; 245*01fb4b82SJames Morse 246*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 247*01fb4b82SJames Morse 248*01fb4b82SJames Morse list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 249*01fb4b82SJames Morse if (vmsc->msc->id == msc->id) 250*01fb4b82SJames Morse return vmsc; 251*01fb4b82SJames Morse } 252*01fb4b82SJames Morse 253*01fb4b82SJames Morse return mpam_vmsc_alloc(comp, msc); 254*01fb4b82SJames Morse } 255*01fb4b82SJames Morse 256*01fb4b82SJames Morse /* 257*01fb4b82SJames Morse * The cacheinfo structures are only populated when CPUs are online. 258*01fb4b82SJames Morse * This helper walks the acpi tables to include offline CPUs too. 259*01fb4b82SJames Morse */ 260*01fb4b82SJames Morse int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 261*01fb4b82SJames Morse cpumask_t *affinity) 262*01fb4b82SJames Morse { 263*01fb4b82SJames Morse return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); 264*01fb4b82SJames Morse } 265*01fb4b82SJames Morse 266*01fb4b82SJames Morse /* 267*01fb4b82SJames Morse * cpumask_of_node() only knows about online CPUs. This can't tell us whether 268*01fb4b82SJames Morse * a class is represented on all possible CPUs. 269*01fb4b82SJames Morse */ 270*01fb4b82SJames Morse static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) 271*01fb4b82SJames Morse { 272*01fb4b82SJames Morse int cpu; 273*01fb4b82SJames Morse 274*01fb4b82SJames Morse for_each_possible_cpu(cpu) { 275*01fb4b82SJames Morse if (node_id == cpu_to_node(cpu)) 276*01fb4b82SJames Morse cpumask_set_cpu(cpu, affinity); 277*01fb4b82SJames Morse } 278*01fb4b82SJames Morse } 279*01fb4b82SJames Morse 280*01fb4b82SJames Morse static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, 281*01fb4b82SJames Morse enum mpam_class_types type, 282*01fb4b82SJames Morse struct mpam_class *class, 283*01fb4b82SJames Morse struct mpam_component *comp) 284*01fb4b82SJames Morse { 285*01fb4b82SJames Morse int err; 286*01fb4b82SJames Morse 287*01fb4b82SJames Morse switch (type) { 288*01fb4b82SJames Morse case MPAM_CLASS_CACHE: 289*01fb4b82SJames Morse err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, 290*01fb4b82SJames Morse affinity); 291*01fb4b82SJames Morse if (err) { 292*01fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, 293*01fb4b82SJames Morse "Failed to determine CPU affinity\n"); 294*01fb4b82SJames Morse return err; 295*01fb4b82SJames Morse } 296*01fb4b82SJames Morse 297*01fb4b82SJames Morse if (cpumask_empty(affinity)) 298*01fb4b82SJames Morse dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n"); 299*01fb4b82SJames Morse 300*01fb4b82SJames Morse break; 301*01fb4b82SJames Morse case MPAM_CLASS_MEMORY: 302*01fb4b82SJames Morse get_cpumask_from_node_id(comp->comp_id, affinity); 303*01fb4b82SJames Morse /* affinity may be empty for CPU-less memory nodes */ 304*01fb4b82SJames Morse break; 305*01fb4b82SJames Morse case MPAM_CLASS_UNKNOWN: 306*01fb4b82SJames Morse return 0; 307*01fb4b82SJames Morse } 308*01fb4b82SJames Morse 309*01fb4b82SJames Morse cpumask_and(affinity, affinity, &msc->accessibility); 310*01fb4b82SJames Morse 311*01fb4b82SJames Morse return 0; 312*01fb4b82SJames Morse } 313*01fb4b82SJames Morse 314*01fb4b82SJames Morse static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, 315*01fb4b82SJames Morse enum mpam_class_types type, u8 class_id, 316*01fb4b82SJames Morse int component_id) 317*01fb4b82SJames Morse { 318*01fb4b82SJames Morse int err; 319*01fb4b82SJames Morse struct mpam_vmsc *vmsc; 320*01fb4b82SJames Morse struct mpam_msc_ris *ris; 321*01fb4b82SJames Morse struct mpam_class *class; 322*01fb4b82SJames Morse struct mpam_component *comp; 323*01fb4b82SJames Morse struct platform_device *pdev = msc->pdev; 324*01fb4b82SJames Morse 325*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 326*01fb4b82SJames Morse 327*01fb4b82SJames Morse if (ris_idx > MPAM_MSC_MAX_NUM_RIS) 328*01fb4b82SJames Morse return -EINVAL; 329*01fb4b82SJames Morse 330*01fb4b82SJames Morse if (test_and_set_bit(ris_idx, &msc->ris_idxs)) 331*01fb4b82SJames Morse return -EBUSY; 332*01fb4b82SJames Morse 333*01fb4b82SJames Morse ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL); 334*01fb4b82SJames Morse if (!ris) 335*01fb4b82SJames Morse return -ENOMEM; 336*01fb4b82SJames Morse init_garbage(&ris->garbage); 337*01fb4b82SJames Morse ris->garbage.pdev = pdev; 338*01fb4b82SJames Morse 339*01fb4b82SJames Morse class = mpam_class_find(class_id, type); 340*01fb4b82SJames Morse if (IS_ERR(class)) 341*01fb4b82SJames Morse return PTR_ERR(class); 342*01fb4b82SJames Morse 343*01fb4b82SJames Morse comp = mpam_component_find(class, component_id); 344*01fb4b82SJames Morse if (IS_ERR(comp)) { 345*01fb4b82SJames Morse if (list_empty(&class->components)) 346*01fb4b82SJames Morse mpam_class_destroy(class); 347*01fb4b82SJames Morse return PTR_ERR(comp); 348*01fb4b82SJames Morse } 349*01fb4b82SJames Morse 350*01fb4b82SJames Morse vmsc = mpam_vmsc_find(comp, msc); 351*01fb4b82SJames Morse if (IS_ERR(vmsc)) { 352*01fb4b82SJames Morse if (list_empty(&comp->vmsc)) 353*01fb4b82SJames Morse mpam_component_destroy(comp); 354*01fb4b82SJames Morse return PTR_ERR(vmsc); 355*01fb4b82SJames Morse } 356*01fb4b82SJames Morse 357*01fb4b82SJames Morse err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); 358*01fb4b82SJames Morse if (err) { 359*01fb4b82SJames Morse if (list_empty(&vmsc->ris)) 360*01fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 361*01fb4b82SJames Morse return err; 362*01fb4b82SJames Morse } 363*01fb4b82SJames Morse 364*01fb4b82SJames Morse ris->ris_idx = ris_idx; 365*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->msc_list); 366*01fb4b82SJames Morse INIT_LIST_HEAD_RCU(&ris->vmsc_list); 367*01fb4b82SJames Morse ris->vmsc = vmsc; 368*01fb4b82SJames Morse 369*01fb4b82SJames Morse cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); 370*01fb4b82SJames Morse cpumask_or(&class->affinity, &class->affinity, &ris->affinity); 371*01fb4b82SJames Morse list_add_rcu(&ris->vmsc_list, &vmsc->ris); 372*01fb4b82SJames Morse list_add_rcu(&ris->msc_list, &msc->ris); 373*01fb4b82SJames Morse 374*01fb4b82SJames Morse return 0; 375*01fb4b82SJames Morse } 376*01fb4b82SJames Morse 377*01fb4b82SJames Morse static void mpam_ris_destroy(struct mpam_msc_ris *ris) 378*01fb4b82SJames Morse { 379*01fb4b82SJames Morse struct mpam_vmsc *vmsc = ris->vmsc; 380*01fb4b82SJames Morse struct mpam_msc *msc = vmsc->msc; 381*01fb4b82SJames Morse struct mpam_component *comp = vmsc->comp; 382*01fb4b82SJames Morse struct mpam_class *class = comp->class; 383*01fb4b82SJames Morse 384*01fb4b82SJames Morse lockdep_assert_held(&mpam_list_lock); 385*01fb4b82SJames Morse 386*01fb4b82SJames Morse /* 387*01fb4b82SJames Morse * It is assumed affinities don't overlap. If they do the class becomes 388*01fb4b82SJames Morse * unusable immediately. 389*01fb4b82SJames Morse */ 390*01fb4b82SJames Morse cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); 391*01fb4b82SJames Morse cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); 392*01fb4b82SJames Morse clear_bit(ris->ris_idx, &msc->ris_idxs); 393*01fb4b82SJames Morse list_del_rcu(&ris->msc_list); 394*01fb4b82SJames Morse list_del_rcu(&ris->vmsc_list); 395*01fb4b82SJames Morse add_to_garbage(ris); 396*01fb4b82SJames Morse 397*01fb4b82SJames Morse if (list_empty(&vmsc->ris)) 398*01fb4b82SJames Morse mpam_vmsc_destroy(vmsc); 399*01fb4b82SJames Morse } 400*01fb4b82SJames Morse 401*01fb4b82SJames Morse int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 402*01fb4b82SJames Morse enum mpam_class_types type, u8 class_id, int component_id) 403*01fb4b82SJames Morse { 404*01fb4b82SJames Morse int err; 405*01fb4b82SJames Morse 406*01fb4b82SJames Morse mutex_lock(&mpam_list_lock); 407*01fb4b82SJames Morse err = mpam_ris_create_locked(msc, ris_idx, type, class_id, 408*01fb4b82SJames Morse component_id); 409*01fb4b82SJames Morse mutex_unlock(&mpam_list_lock); 410*01fb4b82SJames Morse if (err) 411*01fb4b82SJames Morse mpam_free_garbage(); 412*01fb4b82SJames Morse 413*01fb4b82SJames Morse return err; 414*01fb4b82SJames Morse } 415*01fb4b82SJames Morse 416*01fb4b82SJames Morse /* 417f04046f2SJames Morse * An MSC can control traffic from a set of CPUs, but may only be accessible 418f04046f2SJames Morse * from a (hopefully wider) set of CPUs. The common reason for this is power 419f04046f2SJames Morse * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 420f04046f2SJames Morse * corresponding cache may also be powered off. By making accesses from 421f04046f2SJames Morse * one of those CPUs, we ensure we don't access a cache that's powered off. 422f04046f2SJames Morse */ 423f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc) 424f04046f2SJames Morse { 425f04046f2SJames Morse u32 affinity_id; 426f04046f2SJames Morse int err; 427f04046f2SJames Morse 428f04046f2SJames Morse err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 429f04046f2SJames Morse &affinity_id); 430f04046f2SJames Morse if (err) 431f04046f2SJames Morse cpumask_copy(&msc->accessibility, cpu_possible_mask); 432f04046f2SJames Morse else 433f04046f2SJames Morse acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 434f04046f2SJames Morse } 435f04046f2SJames Morse 436*01fb4b82SJames Morse /* 437*01fb4b82SJames Morse * There are two ways of reaching a struct mpam_msc_ris. Via the 438*01fb4b82SJames Morse * class->component->vmsc->ris, or via the msc. 439*01fb4b82SJames Morse * When destroying the msc, the other side needs unlinking and cleaning up too. 440*01fb4b82SJames Morse */ 441f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc) 442f04046f2SJames Morse { 443f04046f2SJames Morse struct platform_device *pdev = msc->pdev; 444*01fb4b82SJames Morse struct mpam_msc_ris *ris, *tmp; 445f04046f2SJames Morse 446f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 447f04046f2SJames Morse 448*01fb4b82SJames Morse list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) 449*01fb4b82SJames Morse mpam_ris_destroy(ris); 450*01fb4b82SJames Morse 451f04046f2SJames Morse list_del_rcu(&msc->all_msc_list); 452f04046f2SJames Morse platform_set_drvdata(pdev, NULL); 453*01fb4b82SJames Morse 454*01fb4b82SJames Morse add_to_garbage(msc); 455f04046f2SJames Morse } 456f04046f2SJames Morse 457f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev) 458f04046f2SJames Morse { 459f04046f2SJames Morse struct mpam_msc *msc = platform_get_drvdata(pdev); 460f04046f2SJames Morse 461f04046f2SJames Morse mutex_lock(&mpam_list_lock); 462f04046f2SJames Morse mpam_msc_destroy(msc); 463f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 464f04046f2SJames Morse 465*01fb4b82SJames Morse mpam_free_garbage(); 466f04046f2SJames Morse } 467f04046f2SJames Morse 468f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 469f04046f2SJames Morse { 470f04046f2SJames Morse int err; 471f04046f2SJames Morse u32 tmp; 472f04046f2SJames Morse struct mpam_msc *msc; 473f04046f2SJames Morse struct resource *msc_res; 474f04046f2SJames Morse struct device *dev = &pdev->dev; 475f04046f2SJames Morse 476f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 477f04046f2SJames Morse 478f04046f2SJames Morse msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 479f04046f2SJames Morse if (!msc) 480f04046f2SJames Morse return ERR_PTR(-ENOMEM); 481*01fb4b82SJames Morse init_garbage(&msc->garbage); 482*01fb4b82SJames Morse msc->garbage.pdev = pdev; 483f04046f2SJames Morse 484f04046f2SJames Morse err = devm_mutex_init(dev, &msc->probe_lock); 485f04046f2SJames Morse if (err) 486f04046f2SJames Morse return ERR_PTR(err); 487f04046f2SJames Morse 488f04046f2SJames Morse err = devm_mutex_init(dev, &msc->part_sel_lock); 489f04046f2SJames Morse if (err) 490f04046f2SJames Morse return ERR_PTR(err); 491f04046f2SJames Morse 492f04046f2SJames Morse msc->id = pdev->id; 493f04046f2SJames Morse msc->pdev = pdev; 494f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->all_msc_list); 495f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->ris); 496f04046f2SJames Morse 497f04046f2SJames Morse update_msc_accessibility(msc); 498f04046f2SJames Morse if (cpumask_empty(&msc->accessibility)) { 499f04046f2SJames Morse dev_err_once(dev, "MSC is not accessible from any CPU!"); 500f04046f2SJames Morse return ERR_PTR(-EINVAL); 501f04046f2SJames Morse } 502f04046f2SJames Morse 503f04046f2SJames Morse if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 504f04046f2SJames Morse msc->iface = MPAM_IFACE_MMIO; 505f04046f2SJames Morse else 506f04046f2SJames Morse msc->iface = MPAM_IFACE_PCC; 507f04046f2SJames Morse 508f04046f2SJames Morse if (msc->iface == MPAM_IFACE_MMIO) { 509f04046f2SJames Morse void __iomem *io; 510f04046f2SJames Morse 511f04046f2SJames Morse io = devm_platform_get_and_ioremap_resource(pdev, 0, 512f04046f2SJames Morse &msc_res); 513f04046f2SJames Morse if (IS_ERR(io)) { 514f04046f2SJames Morse dev_err_once(dev, "Failed to map MSC base address\n"); 515f04046f2SJames Morse return ERR_CAST(io); 516f04046f2SJames Morse } 517f04046f2SJames Morse msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 518f04046f2SJames Morse msc->mapped_hwpage = io; 519f04046f2SJames Morse } else { 520f04046f2SJames Morse return ERR_PTR(-EINVAL); 521f04046f2SJames Morse } 522f04046f2SJames Morse 523f04046f2SJames Morse list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 524f04046f2SJames Morse platform_set_drvdata(pdev, msc); 525f04046f2SJames Morse 526f04046f2SJames Morse return msc; 527f04046f2SJames Morse } 528f04046f2SJames Morse 529f04046f2SJames Morse static int fw_num_msc; 530f04046f2SJames Morse 531f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev) 532f04046f2SJames Morse { 533f04046f2SJames Morse int err; 534f04046f2SJames Morse struct mpam_msc *msc = NULL; 535f04046f2SJames Morse void *plat_data = pdev->dev.platform_data; 536f04046f2SJames Morse 537f04046f2SJames Morse mutex_lock(&mpam_list_lock); 538f04046f2SJames Morse msc = do_mpam_msc_drv_probe(pdev); 539f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 540f04046f2SJames Morse 541f04046f2SJames Morse if (IS_ERR(msc)) 542f04046f2SJames Morse return PTR_ERR(msc); 543f04046f2SJames Morse 544f04046f2SJames Morse /* Create RIS entries described by firmware */ 545f04046f2SJames Morse err = acpi_mpam_parse_resources(msc, plat_data); 546f04046f2SJames Morse if (err) { 547f04046f2SJames Morse mpam_msc_drv_remove(pdev); 548f04046f2SJames Morse return err; 549f04046f2SJames Morse } 550f04046f2SJames Morse 551f04046f2SJames Morse if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 552f04046f2SJames Morse pr_info("Discovered all MSCs\n"); 553f04046f2SJames Morse 554f04046f2SJames Morse return 0; 555f04046f2SJames Morse } 556f04046f2SJames Morse 557f04046f2SJames Morse static struct platform_driver mpam_msc_driver = { 558f04046f2SJames Morse .driver = { 559f04046f2SJames Morse .name = "mpam_msc", 560f04046f2SJames Morse }, 561f04046f2SJames Morse .probe = mpam_msc_drv_probe, 562f04046f2SJames Morse .remove = mpam_msc_drv_remove, 563f04046f2SJames Morse }; 564f04046f2SJames Morse 565f04046f2SJames Morse static int __init mpam_msc_driver_init(void) 566f04046f2SJames Morse { 567f04046f2SJames Morse if (!system_supports_mpam()) 568f04046f2SJames Morse return -EOPNOTSUPP; 569f04046f2SJames Morse 570f04046f2SJames Morse init_srcu_struct(&mpam_srcu); 571f04046f2SJames Morse 572f04046f2SJames Morse fw_num_msc = acpi_mpam_count_msc(); 573f04046f2SJames Morse if (fw_num_msc <= 0) { 574f04046f2SJames Morse pr_err("No MSC devices found in firmware\n"); 575f04046f2SJames Morse return -EINVAL; 576f04046f2SJames Morse } 577f04046f2SJames Morse 578f04046f2SJames Morse return platform_driver_register(&mpam_msc_driver); 579f04046f2SJames Morse } 580f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init); 581