[PATCH v6 8/8] riscv_cbqri: Add CBQRI capacity allocation platform driver
Drew Fustini <[email protected]> Wed, 29 Jul 2026 18:11:35 -0700
| Newsgroups | dev.linux.lists.linux-rt-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add a device-tree platform driver, bound to the generic riscv,cbqri-capacity-controller compatible, that registers a CBQRI capacity controller as the resctrl cache-allocation resource for the cache it governs. The driver follows the node's riscv,cbqri-cache phandle to that cache, reads its level, and matches it against cacheinfo to get the resctrl domain id. It then hands the controller to riscv_cbqri_register_cc_dt() with the riscv,cbqri-rcid count from the node. Nothing is vendor-specific, and the DT "reg" is the CBQRI register block itself, so any SoC that describes a CBQRI capacity controller in device tree can reuse the driver unchanged. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Drew Fustini <[email protected]> --- MAINTAINERS | 1 + drivers/resctrl/Kconfig | 15 +++++ drivers/resctrl/Makefile | 1 + drivers/resctrl/cbqri_capacity.c | 127 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7acdab42e9c5..5c4be59b2670 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23367,6 +23367,7 @@ F: Documentation/devicetree/bindings/riscv/riscv,cbqri.yaml F: arch/riscv/include/asm/qos.h F: arch/riscv/include/asm/resctrl.h F: arch/riscv/kernel/qos.c +F: drivers/resctrl/cbqri_capacity.c F: drivers/resctrl/cbqri_devices.c F: drivers/resctrl/cbqri_internal.h F: drivers/resctrl/cbqri_resctrl.c diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index 617b813b9808..689a91873d7b 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -41,6 +41,21 @@ menuconfig RISCV_CBQRI allocation through the resctrl filesystem at /sys/fs/resctrl when RESCTRL_FS is also enabled. +if RISCV_CBQRI + +config RISCV_CBQRI_CAPACITY + bool "RISC-V CBQRI cache capacity-allocation controller" + depends on OF + help + Enable driver for a RISC-V CBQRI capacity controller that + governs a CPU cache, matching the "riscv,cbqri-capacity-controller" + compatible. The controller's cache phandle gives the cache level and + id, which the driver registers as a resctrl cache-allocation resource. + + Say N unless your device tree describes a CBQRI capacity controller. + +endif + config RISCV_CBQRI_RESCTRL_FS bool default y if RISCV_CBQRI && RESCTRL_FS diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile index a7631712dba9..c8339113ef1f 100644 --- a/drivers/resctrl/Makefile +++ b/drivers/resctrl/Makefile @@ -7,3 +7,4 @@ ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) += -DDEBUG obj-$(CONFIG_RISCV_CBQRI) += cbqri.o cbqri-y += cbqri_devices.o cbqri-$(CONFIG_RISCV_CBQRI_RESCTRL_FS) += cbqri_resctrl.o +cbqri-$(CONFIG_RISCV_CBQRI_CAPACITY) += cbqri_capacity.o diff --git a/drivers/resctrl/cbqri_capacity.c b/drivers/resctrl/cbqri_capacity.c new file mode 100644 index 000000000000..0670a676bdb1 --- /dev/null +++ b/drivers/resctrl/cbqri_capacity.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Platform driver for a RISC-V CBQRI capacity controller that backs a CPU + * cache. The controller is described in device tree by the generic + * "riscv,cbqri-capacity-controller" compatible together with a phandle to the + * cache node it governs. The driver hands it to the CBQRI core, which probes + * the capabilities register and exposes a controller that supports allocation + * as the resctrl cache allocation resource for that cache. + */ + +#define pr_fmt(fmt) "cbqri-capacity: " fmt + +#include <linux/cacheinfo.h> +#include <linux/cpu.h> +#include <linux/device-id/of.h> +#include <linux/ioport.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/printk.h> +#include <linux/riscv_cbqri.h> +#include <linux/types.h> + +static int cbqri_capacity_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cbqri_controller_info info = {}; + struct device_node *cache_np; + struct resource *res; + u32 rcid_count, cache_level; + int cache_id, cpu, ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + ret = of_property_read_u32(dev->of_node, "riscv,cbqri-rcid", &rcid_count); + if (ret) { + dev_err(dev, "missing riscv,cbqri-rcid\n"); + return ret; + } + + cache_np = of_parse_phandle(dev->of_node, "riscv,cbqri-cache", 0); + if (!cache_np) { + dev_err(dev, "missing riscv,cbqri-cache phandle\n"); + return -EINVAL; + } + + ret = of_property_read_u32(cache_np, "cache-level", &cache_level); + if (ret) { + dev_err(dev, "%pOF: missing cache-level\n", cache_np); + goto out_put; + } + + /* + * Associate the controller with its cache instance via cacheinfo. + * The matching cache provides the cache id used as the resctrl + * domain id. + * + * Taking the first leaf at the level is sufficient, because fw_token + * is keyed on the DT cache node, so sibling leaves of a split cache + * share it, and riscv,cbqri-cache must reference a unified shared + * cache (cache-unified), not a split private L1. + */ + cache_id = -1; + cpus_read_lock(); + for_each_online_cpu(cpu) { + struct cacheinfo *ci = get_cpu_cacheinfo_level(cpu, cache_level); + + if (ci && ci->fw_token == cache_np) { + cache_id = ci->id; + break; + } + } + cpus_read_unlock(); + + if (cache_id < 0) { + dev_err(dev, "%pOF: no online hart reports an L%u cache for this node\n", + cache_np, cache_level); + ret = -ENODEV; + goto out_put; + } + + info.type = CBQRI_CONTROLLER_TYPE_CAPACITY; + info.addr = res->start; + info.size = resource_size(res); + info.rcid_count = rcid_count; + info.cache_id = cache_id; + + ret = riscv_cbqri_register_cc_dt(&info, cache_level); + if (ret) { + dev_err(dev, "failed to register capacity controller: %d\n", ret); + goto out_put; + } + + dev_info(dev, "registered L%u capacity controller at %pa (cache_id=%d, rcid=%u)\n", + cache_level, &info.addr, cache_id, rcid_count); + +out_put: + of_node_put(cache_np); + return ret; +} + +static const struct of_device_id cbqri_capacity_of_match[] = { + { .compatible = "riscv,cbqri-capacity-controller" }, + {} +}; +MODULE_DEVICE_TABLE(of, cbqri_capacity_of_match); + +static struct platform_driver cbqri_capacity_driver = { + .probe = cbqri_capacity_probe, + .driver = { + .name = "cbqri-capacity", + .of_match_table = cbqri_capacity_of_match, + /* + * The controller is registered permanently into the + * CBQRI core for the life of the system. Block unbind + * so userspace cannot leave a dangling controller. + */ + .suppress_bind_attrs = true, + }, +}; + +/* + * Register at device_initcall so probe runs before the CBQRI core's + * late_initcall which walks the cbqri_controllers list. + */ +builtin_platform_driver(cbqri_capacity_driver); -- 2.43.0