xref: /linux/drivers/resctrl/mpam_devices.c (revision f04046f2577a5c76167333ca99d3903ee5331ba0)
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