1*f04046f2SJames Morse // SPDX-License-Identifier: GPL-2.0 2*f04046f2SJames Morse // Copyright (C) 2025 Arm Ltd. 3*f04046f2SJames Morse 4*f04046f2SJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 5*f04046f2SJames Morse 6*f04046f2SJames Morse #include <linux/acpi.h> 7*f04046f2SJames Morse #include <linux/arm_mpam.h> 8*f04046f2SJames Morse #include <linux/cacheinfo.h> 9*f04046f2SJames Morse #include <linux/cpumask.h> 10*f04046f2SJames Morse #include <linux/device.h> 11*f04046f2SJames Morse #include <linux/errno.h> 12*f04046f2SJames Morse #include <linux/gfp.h> 13*f04046f2SJames Morse #include <linux/list.h> 14*f04046f2SJames Morse #include <linux/lockdep.h> 15*f04046f2SJames Morse #include <linux/mutex.h> 16*f04046f2SJames Morse #include <linux/platform_device.h> 17*f04046f2SJames Morse #include <linux/printk.h> 18*f04046f2SJames Morse #include <linux/srcu.h> 19*f04046f2SJames Morse #include <linux/types.h> 20*f04046f2SJames Morse 21*f04046f2SJames Morse #include "mpam_internal.h" 22*f04046f2SJames Morse 23*f04046f2SJames Morse /* 24*f04046f2SJames Morse * mpam_list_lock protects the SRCU lists when writing. Once the 25*f04046f2SJames Morse * mpam_enabled key is enabled these lists are read-only, 26*f04046f2SJames Morse * unless the error interrupt disables the driver. 27*f04046f2SJames Morse */ 28*f04046f2SJames Morse static DEFINE_MUTEX(mpam_list_lock); 29*f04046f2SJames Morse static LIST_HEAD(mpam_all_msc); 30*f04046f2SJames Morse 31*f04046f2SJames Morse struct srcu_struct mpam_srcu; 32*f04046f2SJames Morse 33*f04046f2SJames Morse /* 34*f04046f2SJames Morse * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 35*f04046f2SJames Morse * can be enabled. 36*f04046f2SJames Morse */ 37*f04046f2SJames Morse static atomic_t mpam_num_msc; 38*f04046f2SJames Morse 39*f04046f2SJames Morse /* 40*f04046f2SJames Morse * An MSC can control traffic from a set of CPUs, but may only be accessible 41*f04046f2SJames Morse * from a (hopefully wider) set of CPUs. The common reason for this is power 42*f04046f2SJames Morse * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 43*f04046f2SJames Morse * corresponding cache may also be powered off. By making accesses from 44*f04046f2SJames Morse * one of those CPUs, we ensure we don't access a cache that's powered off. 45*f04046f2SJames Morse */ 46*f04046f2SJames Morse static void update_msc_accessibility(struct mpam_msc *msc) 47*f04046f2SJames Morse { 48*f04046f2SJames Morse u32 affinity_id; 49*f04046f2SJames Morse int err; 50*f04046f2SJames Morse 51*f04046f2SJames Morse err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 52*f04046f2SJames Morse &affinity_id); 53*f04046f2SJames Morse if (err) 54*f04046f2SJames Morse cpumask_copy(&msc->accessibility, cpu_possible_mask); 55*f04046f2SJames Morse else 56*f04046f2SJames Morse acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 57*f04046f2SJames Morse } 58*f04046f2SJames Morse 59*f04046f2SJames Morse static void mpam_msc_destroy(struct mpam_msc *msc) 60*f04046f2SJames Morse { 61*f04046f2SJames Morse struct platform_device *pdev = msc->pdev; 62*f04046f2SJames Morse 63*f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 64*f04046f2SJames Morse 65*f04046f2SJames Morse list_del_rcu(&msc->all_msc_list); 66*f04046f2SJames Morse platform_set_drvdata(pdev, NULL); 67*f04046f2SJames Morse } 68*f04046f2SJames Morse 69*f04046f2SJames Morse static void mpam_msc_drv_remove(struct platform_device *pdev) 70*f04046f2SJames Morse { 71*f04046f2SJames Morse struct mpam_msc *msc = platform_get_drvdata(pdev); 72*f04046f2SJames Morse 73*f04046f2SJames Morse mutex_lock(&mpam_list_lock); 74*f04046f2SJames Morse mpam_msc_destroy(msc); 75*f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 76*f04046f2SJames Morse 77*f04046f2SJames Morse synchronize_srcu(&mpam_srcu); 78*f04046f2SJames Morse } 79*f04046f2SJames Morse 80*f04046f2SJames Morse static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 81*f04046f2SJames Morse { 82*f04046f2SJames Morse int err; 83*f04046f2SJames Morse u32 tmp; 84*f04046f2SJames Morse struct mpam_msc *msc; 85*f04046f2SJames Morse struct resource *msc_res; 86*f04046f2SJames Morse struct device *dev = &pdev->dev; 87*f04046f2SJames Morse 88*f04046f2SJames Morse lockdep_assert_held(&mpam_list_lock); 89*f04046f2SJames Morse 90*f04046f2SJames Morse msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 91*f04046f2SJames Morse if (!msc) 92*f04046f2SJames Morse return ERR_PTR(-ENOMEM); 93*f04046f2SJames Morse 94*f04046f2SJames Morse err = devm_mutex_init(dev, &msc->probe_lock); 95*f04046f2SJames Morse if (err) 96*f04046f2SJames Morse return ERR_PTR(err); 97*f04046f2SJames Morse 98*f04046f2SJames Morse err = devm_mutex_init(dev, &msc->part_sel_lock); 99*f04046f2SJames Morse if (err) 100*f04046f2SJames Morse return ERR_PTR(err); 101*f04046f2SJames Morse 102*f04046f2SJames Morse msc->id = pdev->id; 103*f04046f2SJames Morse msc->pdev = pdev; 104*f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->all_msc_list); 105*f04046f2SJames Morse INIT_LIST_HEAD_RCU(&msc->ris); 106*f04046f2SJames Morse 107*f04046f2SJames Morse update_msc_accessibility(msc); 108*f04046f2SJames Morse if (cpumask_empty(&msc->accessibility)) { 109*f04046f2SJames Morse dev_err_once(dev, "MSC is not accessible from any CPU!"); 110*f04046f2SJames Morse return ERR_PTR(-EINVAL); 111*f04046f2SJames Morse } 112*f04046f2SJames Morse 113*f04046f2SJames Morse if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 114*f04046f2SJames Morse msc->iface = MPAM_IFACE_MMIO; 115*f04046f2SJames Morse else 116*f04046f2SJames Morse msc->iface = MPAM_IFACE_PCC; 117*f04046f2SJames Morse 118*f04046f2SJames Morse if (msc->iface == MPAM_IFACE_MMIO) { 119*f04046f2SJames Morse void __iomem *io; 120*f04046f2SJames Morse 121*f04046f2SJames Morse io = devm_platform_get_and_ioremap_resource(pdev, 0, 122*f04046f2SJames Morse &msc_res); 123*f04046f2SJames Morse if (IS_ERR(io)) { 124*f04046f2SJames Morse dev_err_once(dev, "Failed to map MSC base address\n"); 125*f04046f2SJames Morse return ERR_CAST(io); 126*f04046f2SJames Morse } 127*f04046f2SJames Morse msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 128*f04046f2SJames Morse msc->mapped_hwpage = io; 129*f04046f2SJames Morse } else { 130*f04046f2SJames Morse return ERR_PTR(-EINVAL); 131*f04046f2SJames Morse } 132*f04046f2SJames Morse 133*f04046f2SJames Morse list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 134*f04046f2SJames Morse platform_set_drvdata(pdev, msc); 135*f04046f2SJames Morse 136*f04046f2SJames Morse return msc; 137*f04046f2SJames Morse } 138*f04046f2SJames Morse 139*f04046f2SJames Morse static int fw_num_msc; 140*f04046f2SJames Morse 141*f04046f2SJames Morse static int mpam_msc_drv_probe(struct platform_device *pdev) 142*f04046f2SJames Morse { 143*f04046f2SJames Morse int err; 144*f04046f2SJames Morse struct mpam_msc *msc = NULL; 145*f04046f2SJames Morse void *plat_data = pdev->dev.platform_data; 146*f04046f2SJames Morse 147*f04046f2SJames Morse mutex_lock(&mpam_list_lock); 148*f04046f2SJames Morse msc = do_mpam_msc_drv_probe(pdev); 149*f04046f2SJames Morse mutex_unlock(&mpam_list_lock); 150*f04046f2SJames Morse 151*f04046f2SJames Morse if (IS_ERR(msc)) 152*f04046f2SJames Morse return PTR_ERR(msc); 153*f04046f2SJames Morse 154*f04046f2SJames Morse /* Create RIS entries described by firmware */ 155*f04046f2SJames Morse err = acpi_mpam_parse_resources(msc, plat_data); 156*f04046f2SJames Morse if (err) { 157*f04046f2SJames Morse mpam_msc_drv_remove(pdev); 158*f04046f2SJames Morse return err; 159*f04046f2SJames Morse } 160*f04046f2SJames Morse 161*f04046f2SJames Morse if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 162*f04046f2SJames Morse pr_info("Discovered all MSCs\n"); 163*f04046f2SJames Morse 164*f04046f2SJames Morse return 0; 165*f04046f2SJames Morse } 166*f04046f2SJames Morse 167*f04046f2SJames Morse static struct platform_driver mpam_msc_driver = { 168*f04046f2SJames Morse .driver = { 169*f04046f2SJames Morse .name = "mpam_msc", 170*f04046f2SJames Morse }, 171*f04046f2SJames Morse .probe = mpam_msc_drv_probe, 172*f04046f2SJames Morse .remove = mpam_msc_drv_remove, 173*f04046f2SJames Morse }; 174*f04046f2SJames Morse 175*f04046f2SJames Morse static int __init mpam_msc_driver_init(void) 176*f04046f2SJames Morse { 177*f04046f2SJames Morse if (!system_supports_mpam()) 178*f04046f2SJames Morse return -EOPNOTSUPP; 179*f04046f2SJames Morse 180*f04046f2SJames Morse init_srcu_struct(&mpam_srcu); 181*f04046f2SJames Morse 182*f04046f2SJames Morse fw_num_msc = acpi_mpam_count_msc(); 183*f04046f2SJames Morse if (fw_num_msc <= 0) { 184*f04046f2SJames Morse pr_err("No MSC devices found in firmware\n"); 185*f04046f2SJames Morse return -EINVAL; 186*f04046f2SJames Morse } 187*f04046f2SJames Morse 188*f04046f2SJames Morse return platform_driver_register(&mpam_msc_driver); 189*f04046f2SJames Morse } 190*f04046f2SJames Morse subsys_initcall(mpam_msc_driver_init); 191