[PATCH RFC 03/15] arm_mpam: Add device tree support for MSC probing
Yin Li <[email protected]>
| Newsgroups | dev.linux.lists.driver-core,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260811-mpam-resctrl-dt-knp-support-v1-3-ea6397bead59@oss.qualcomm.com> |
From: James Morse <[email protected]> The MPAM driver currently discovers MSCs only via ACPI. Add a device tree path so MSCs can be probed on DT-based platforms: parse MSC nodes from the device tree, compute cache-id and affinity from the cache nodes and create the RIS entries. Signed-off-by: James Morse <[email protected]> [ Yin Li: fix context conflicts; drop the existing ACPI-only stub of mpam_get_cpumask_from_cache_id() which conflicted with the new implementation that supports both ACPI and DT; fix update_msc_accessibility() return type conflict: drop the existing void implementation and apply the int version from this patch ] Signed-off-by: Yin Li <[email protected]> --- drivers/resctrl/mpam_devices.c | 256 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 238 insertions(+), 18 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index dd422c56fbb1..6f2854fe08ca 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -20,6 +20,9 @@ #include <linux/list.h> #include <linux/lockdep.h> #include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/srcu.h> @@ -161,6 +164,163 @@ static void mpam_free_garbage(void) } } +/* Called recursively to walk the list of caches from a particular CPU */ +static void __mpam_get_cpumask_from_cache_id(int cpu, struct device_node *cache_node, + unsigned long cache_id, + u32 cache_level, + cpumask_t *affinity) +{ + int err; + u32 iter_level; + unsigned long iter_cache_id; + struct device_node *iter_node __free(device_node) = of_find_next_cache_node(cache_node); + + if (!iter_node) + return; + + err = of_property_read_u32(iter_node, "cache-level", &iter_level); + if (err) + return; + + /* + * get_cpu_cacheinfo_id() isn't ready until sometime + * during device_initcall(). Use cache_of_calculate_id(). + */ + iter_cache_id = cache_of_calculate_id(iter_node); + if (iter_cache_id == ~0UL) + return; + + if (iter_level == cache_level && iter_cache_id == cache_id) + cpumask_set_cpu(cpu, affinity); + + if (iter_level < cache_level) + __mpam_get_cpumask_from_cache_id(cpu, iter_node, cache_id, + cache_level, affinity); +} + +/* + * The cacheinfo structures are only populated when CPUs are online. + * This helper walks the device tree to include offline CPUs too. + */ +int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, + cpumask_t *affinity) +{ + int cpu; + + if (!acpi_disabled) + return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); + + for_each_possible_cpu(cpu) { + struct device_node *cpu_node __free(device_node) = of_get_cpu_node(cpu, NULL); + if (!cpu_node) { + pr_err("Failed to find cpu%d device node\n", cpu); + return -ENOENT; + } + + __mpam_get_cpumask_from_cache_id(cpu, cpu_node, cache_id, + cache_level, affinity); + } + + return 0; +} + +static int get_cpumask_from_cache(struct device_node *cache, + cpumask_t *affinity) +{ + int err; + u32 cache_level; + unsigned long cache_id; + + err = of_property_read_u32(cache, "cache-level", &cache_level); + if (err) { + pr_err("Failed to read cache-level from cache node\n"); + return -ENOENT; + } + + cache_id = cache_of_calculate_id(cache); + if (cache_id == ~0UL) { + pr_err("Failed to calculate cache-id from cache node\n"); + return -ENOENT; + } + + return mpam_get_cpumask_from_cache_id(cache_id, cache_level, affinity); +} + +static int mpam_dt_count_msc(void) +{ + int count = 0; + struct device_node *np; + + for_each_compatible_node(np, NULL, "arm,mpam-msc") { + if (of_device_is_available(np)) + count++; + } + + return count; +} + +static int mpam_dt_parse_resource(struct mpam_msc *msc, struct device_node *np, + u32 ris_idx) +{ + int err = 0; + u32 level = 0; + unsigned long cache_id; + struct device *dev = &msc->pdev->dev; + struct device_node *cache __free(device_node) = NULL; + struct device_node *parent __free(device_node) = of_get_parent(np); + + if (of_device_is_compatible(np, "arm,mpam-cache")) { + cache = of_parse_phandle(np, "arm,mpam-device", 0); + if (!cache) { + dev_err_once(dev, "Failed to read phandle\n"); + return -EINVAL; + } + } else if (of_device_is_compatible(parent, "cache")) { + cache = parent; + } else { + /* For now, only caches are supported */ + cache = NULL; + return err; + } + + err = of_property_read_u32(cache, "cache-level", &level); + if (err) { + dev_err_once(dev, "Failed to read cache-level\n"); + return err; + } + + cache_id = cache_of_calculate_id(cache); + if (cache_id == ~0) { + dev_err_once(dev, "Failed to calculate cache-id\n"); + return -ENOENT; + } + + return mpam_ris_create(msc, ris_idx, MPAM_CLASS_CACHE, level, cache_id); +} + +static int mpam_dt_parse_resources(struct mpam_msc *msc, void *ignored) +{ + u64 ris_idx = 0; + int err, num_ris = 0; + struct device_node *np; + + np = msc->pdev->dev.of_node; + for_each_available_child_of_node_scoped(np, iter) { + err = of_property_read_reg(iter, 0, &ris_idx, NULL); + if (!err) { + num_ris++; + err = mpam_dt_parse_resource(msc, iter, ris_idx); + if (err) + return err; + } + } + + if (!num_ris) + err = mpam_dt_parse_resource(msc, np, 0); + + return err; +} + /* * Once mpam is enabled, new requestors cannot further reduce the available * partid. Assert that the size is fixed, and new requestors will be turned @@ -481,16 +641,6 @@ mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) return mpam_vmsc_alloc(comp, msc); } -/* - * The cacheinfo structures are only populated when CPUs are online. - * This helper walks the acpi tables to include offline CPUs too. - */ -int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, - cpumask_t *affinity) -{ - return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); -} - /* * cpumask_of_node() only knows about online CPUs. This can't tell us whether * a class is represented on all possible CPUs. @@ -1985,17 +2135,42 @@ static int mpam_msc_setup_error_irq(struct mpam_msc *msc) * corresponding cache may also be powered off. By making accesses from * one of those CPUs, we ensure we don't access a cache that's powered off. */ -static void update_msc_accessibility(struct mpam_msc *msc) +static int update_msc_accessibility(struct mpam_msc *msc) { + struct device *dev = &msc->pdev->dev; + struct device_node *parent; u32 affinity_id; int err; - err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", - &affinity_id); - if (err) + if (!acpi_disabled) { + err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", + &affinity_id); + if (err) + cpumask_copy(&msc->accessibility, cpu_possible_mask); + else + acpi_pptt_get_cpus_from_container(affinity_id, + &msc->accessibility); + + return 0; + } + + /* Where an MSC can be accessed from depends on the path to of_node. */ + parent = of_get_parent(msc->pdev->dev.of_node); + if (parent == of_root) { cpumask_copy(&msc->accessibility, cpu_possible_mask); - else - acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); + err = 0; + } else { + if (of_device_is_compatible(parent, "cache")) { + err = get_cpumask_from_cache(parent, + &msc->accessibility); + } else { + err = -EINVAL; + dev_err_once(dev, "Cannot determine accessibility of MSC.\n"); + } + } + of_node_put(parent); + + return err; } /* @@ -2123,7 +2298,10 @@ static int mpam_msc_drv_probe(struct platform_device *pdev) return PTR_ERR(msc); /* Create RIS entries described by firmware */ - err = acpi_mpam_parse_resources(msc, plat_data); + if (!acpi_disabled) + err = acpi_mpam_parse_resources(msc, plat_data); + else + err = mpam_dt_parse_resources(msc, plat_data); if (err) { mpam_msc_drv_remove(pdev); return err; @@ -2136,15 +2314,51 @@ static int mpam_msc_drv_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id mpam_of_match[] = { + { .compatible = "arm,mpam-msc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpam_of_match); + static struct platform_driver mpam_msc_driver = { .driver = { .name = "mpam_msc", .suppress_bind_attrs = true, + .of_match_table = of_match_ptr(mpam_of_match), }, .probe = mpam_msc_drv_probe, .remove = mpam_msc_drv_remove, }; +/* + * MSCs that are declared by the firmware as being part of a cache may not + * be created automatically as platform devices, since there is no + * dedicated cache driver. + * + * Deal with theo MSCs here. + */ +static void mpam_dt_create_foundling_msc(void) +{ + struct platform_device *pdev; + struct device_node *cache; + + for_each_compatible_node(cache, NULL, "cache") { + struct device_node *cache_device; + + if (of_node_check_flag(cache, OF_POPULATED)) + continue; + + cache_device = of_find_matching_node_and_match(cache, mpam_of_match, NULL); + if (!cache_device) + continue; + of_node_put(cache_device); + + pdev = of_platform_device_create(cache, "cache", NULL); + if (!pdev) + pr_err_once("Failed to create MSC devices under caches\n"); + } +} + /* Any of these features mean the BWA_WD field is valid. */ static bool mpam_has_bwa_wd_feature(struct mpam_props *props) { @@ -2963,12 +3177,18 @@ static int __init mpam_msc_driver_init(void) init_srcu_struct(&mpam_srcu); - fw_num_msc = acpi_mpam_count_msc(); + if (!acpi_disabled) + fw_num_msc = acpi_mpam_count_msc(); + else + fw_num_msc = mpam_dt_count_msc(); if (fw_num_msc <= 0) { pr_err("No MSC devices found in firmware\n"); return -EINVAL; } + if (acpi_disabled) + mpam_dt_create_foundling_msc(); + return platform_driver_register(&mpam_msc_driver); } -- 2.34.1