[PATCH v6 4/8] riscv_cbqri: Add capacity controller probe and allocation device ops
Drew Fustini <[email protected]> Wed, 29 Jul 2026 18:11:31 -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 support for the RISC-V CBQRI capacity controller. A platform driver passes a cbqri_controller_info descriptor together with the cache level to riscv_cbqri_register_cc_dt(), which probes the controller and adds it to the controller list. Assisted-by: Claude:claude-opus-4-8 Co-developed-by: Adrien Ricciardi <[email protected]> Signed-off-by: Adrien Ricciardi <[email protected]> Signed-off-by: Drew Fustini <[email protected]> --- MAINTAINERS | 3 + drivers/resctrl/Kconfig | 9 + drivers/resctrl/Makefile | 3 + drivers/resctrl/cbqri_devices.c | 568 +++++++++++++++++++++++++++++++++++++++ drivers/resctrl/cbqri_internal.h | 122 +++++++++ include/linux/riscv_cbqri.h | 45 ++++ 6 files changed, 750 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a0a4b41f02c5..064a6ae2823e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23365,6 +23365,9 @@ L: [email protected] S: Supported F: arch/riscv/include/asm/qos.h F: arch/riscv/kernel/qos.c +F: drivers/resctrl/cbqri_devices.c +F: drivers/resctrl/cbqri_internal.h +F: include/linux/riscv_cbqri.h RISC-V RPMI AND MPXY DRIVERS M: Rahul Pathak <[email protected]> diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index 672abea3b03c..1219fb29aa2e 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -29,3 +29,12 @@ config ARM64_MPAM_RESCTRL_FS default y if ARM64_MPAM_DRIVER && RESCTRL_FS select RESCTRL_RMID_DEPENDS_ON_CLOSID select RESCTRL_ASSIGN_FIXED + +menuconfig RISCV_CBQRI + bool "RISC-V CBQRI support" + depends on RISCV && RISCV_ISA_SSQOSID + help + Capacity and Bandwidth QoS Register Interface (CBQRI) support for + RISC-V cache QoS resources. CBQRI exposes cache capacity + allocation through the resctrl filesystem at /sys/fs/resctrl when + RESCTRL_FS is also enabled. diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile index 4f6d0e81f9b8..4d8a2c4b5627 100644 --- a/drivers/resctrl/Makefile +++ b/drivers/resctrl/Makefile @@ -3,3 +3,6 @@ mpam-y += mpam_devices.o mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS) += mpam_resctrl.o ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) += -DDEBUG + +obj-$(CONFIG_RISCV_CBQRI) += cbqri.o +cbqri-y += cbqri_devices.o diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c new file mode 100644 index 000000000000..e93261dfa4fa --- /dev/null +++ b/drivers/resctrl/cbqri_devices.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/riscv_cbqri.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <asm/csr.h> + +#include "cbqri_internal.h" + +LIST_HEAD(cbqri_controllers); + +/* + * Serializes cbqri_controllers mutations against a concurrent insert under + * asynchronous driver probing, and against the boot-time walk in the resctrl + * glue. Runtime cpuhp walks happen after registration has settled. + */ +DEFINE_MUTEX(cbqri_controllers_lock); + +/* + * CBQRI registers are 64-bit, but the spec only guarantees single-copy + * atomicity for naturally aligned 4-byte accesses. Read the two halves and + * reconstruct, so the driver does not rely on native 64-bit MMIO. + * + * The halves are not sampled atomically, but a tear is harmless. The fields + * that change while an operation runs, BUSY (bit 39) and STATUS (bits + * 38:32), are in the high half and read by the single high readl(). The low + * half (OP/AT/RCID) is software-written and constant for the operation. + */ +static u64 cbqri_readq(void __iomem *addr) +{ + u32 lo = readl(addr); + u32 hi = readl(addr + 4); + + return (u64)hi << 32 | lo; +} + +/* Set capacity block mask (cc_block_mask) */ +static void cbqri_set_cbm(struct cbqri_controller *ctrl, u64 cbm) +{ + /* + * cbqri_probe_cc() rejects ncblks > 32, so the mask fits the low word. + * Per CBQRI 3.5 the cc_block_mask bits above NCBLKS are read-only zero, + * so the upper word needs no write. + */ + writel(lower_32_bits(cbm), ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); +} + +/* + * Clear cc_cunits so a CONFIG_LIMIT on a CUNITS-capable controller imposes no + * capacity-unit limit. resctrl models only the block mask. On controllers + * with cc_capabilities.CUNITS == 0 the register is read-only zero, so there is + * nothing to do. + */ +static void cbqri_clear_cunits(struct cbqri_controller *ctrl) +{ + if (ctrl->cc.cunits) { + writel(0, ctrl->base + CBQRI_CC_CUNITS_OFF); + writel(0, ctrl->base + CBQRI_CC_CUNITS_OFF + 4); + } +} + +static int cbqri_wait_busy_flag(struct cbqri_controller *ctrl, int reg_offset, + u64 *regp) +{ + u64 reg; + int ret; + + /* + * Sleeping poll: caller holds ctrl->lock as a sleeping mutex, so + * 10us/1ms is safe under PREEMPT_RT. + */ + ret = read_poll_timeout(cbqri_readq, reg, + !FIELD_GET(CBQRI_CONTROL_REGISTERS_BUSY_MASK, reg), + 10, 1000, false, ctrl->base + reg_offset); + if (ret) + return ret; + if (regp) + *regp = reg; + return 0; +} + +/* + * Perform capacity allocation control operation on capacity controller. + * Caller must hold ctrl->lock. + */ +static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation, + int rcid, u32 at) +{ + int reg_offset = CBQRI_CC_ALLOC_CTL_OFF; + int status; + u64 reg; + + lockdep_assert_held(&ctrl->lock); + + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout before starting operation\n"); + return -EIO; + } + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, ®, operation); + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, ®, rcid); + + /* + * CBQRI Table 1: AT 0=Data, 1=Code. Program AT on controllers + * that report supports_alloc_at_code. On controllers that don't, + * AT is reserved-zero and the op acts on both halves. + */ + reg &= ~CBQRI_CONTROL_REGISTERS_AT_MASK; + if (ctrl->cc.supports_alloc_at_code) + reg |= FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, at); + + writel(lower_32_bits(reg), ctrl->base + reg_offset); + + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout during operation\n"); + return -EIO; + } + + status = FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg); + if (status != CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) { + pr_err_ratelimited("operation %d failed: status=%d\n", operation, status); + return -EIO; + } + + return 0; +} + +/* + * Apply a capacity block mask and verify via CONFIG_LIMIT + READ_LIMIT. + * + * AT-capable controllers with CDP off need a second CONFIG_LIMIT on the + * other AT half (the spec encodes AT only as 0=Data / 1=Code, there is + * no "both halves" value). CDP-on issues separate per-type writes from + * resctrl, so a single CONFIG_LIMIT per call is correct. + */ +int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid, + const struct cbqri_cc_config *cfg) +{ + bool need_at_mirror; + u64 saved_cbm = 0; + int err = 0; + u64 reg; + + mutex_lock(&ctrl->lock); + + need_at_mirror = ctrl->cc.supports_alloc_at_code && !cfg->cdp_enabled; + + /* + * Capture the cfg->at half CBM before any write so a partial + * AT-mirror failure can revert and keep the two halves consistent. + * Pre-clear cc_block_mask so a silent firmware no-op (status + * SUCCESS but staging not updated) shows as a zero readback + * rather than carrying stale data from a prior op. + */ + if (need_at_mirror) { + cbqri_set_cbm(ctrl, 0); + err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + closid, cfg->at); + if (err < 0) + goto out; + saved_cbm = cbqri_readq(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + } + + /* Set capacity block mask (cc_block_mask) */ + cbqri_set_cbm(ctrl, cfg->cbm); + cbqri_clear_cunits(ctrl); + + /* Capacity config limit operation for the AT half implied by cfg->at */ + err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, cfg->at); + if (err < 0) + goto out; + + /* + * CDP-off mirror: on AT-capable controllers, also program the + * other AT half with the same mask so the two halves stay in sync. + */ + if (need_at_mirror) { + u32 other = (cfg->at == CBQRI_CONTROL_REGISTERS_AT_CODE) ? + CBQRI_CONTROL_REGISTERS_AT_DATA : + CBQRI_CONTROL_REGISTERS_AT_CODE; + + cbqri_set_cbm(ctrl, cfg->cbm); + cbqri_clear_cunits(ctrl); + err = cbqri_cc_alloc_op(ctrl, + CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, other); + if (err < 0) { + int rerr; + + /* + * Best-effort revert of the cfg->at half so the two + * halves stay in sync. A schemata read sees only one + * half, so silent divergence would otherwise report + * the new value as if the write had succeeded. + */ + cbqri_set_cbm(ctrl, saved_cbm); + cbqri_clear_cunits(ctrl); + rerr = cbqri_cc_alloc_op(ctrl, + CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, cfg->at); + if (rerr < 0) + pr_err_ratelimited("AT-mirror revert failed (err=%d), AT halves diverged\n", + rerr); + goto out; + } + } + + /* Clear cc_block_mask before read limit to verify op works */ + cbqri_set_cbm(ctrl, 0); + + /* Perform a capacity read limit operation to verify blockmask */ + err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + closid, cfg->at); + if (err < 0) + goto out; + + /* + * Read capacity blockmask and narrow to u32 to match resctrl's CBM + * width. cbqri_probe_cc() rejects ncblks > 32 so the upper bits are + * reserved zero. + */ + reg = cbqri_readq(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + if (lower_32_bits(reg) != cfg->cbm) { + pr_err_ratelimited("CBM verify mismatch (reg=%llx != cbm=%llx)\n", + reg, cfg->cbm); + err = -EIO; + } + +out: + mutex_unlock(&ctrl->lock); + return err; +} + +/* + * Read the configured CBM for closid on the at half via READ_LIMIT. + * Pre-clears cc_block_mask before the op so a silent firmware no-op + * (status SUCCESS but staging not updated) is detectable in cbm_out. + */ +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid, + u32 at, u32 *cbm_out) +{ + int err; + + mutex_lock(&ctrl->lock); + cbqri_set_cbm(ctrl, 0); + err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, closid, at); + if (err == 0) { + /* + * resctrl exposes the CBM as a u32 and cbqri_probe_cc() rejects + * ncblks > 32, so the mask fits the low 32-bit word of the + * cc_block_mask register. + */ + *cbm_out = readl(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + } + mutex_unlock(&ctrl->lock); + return err; +} + +static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offset, + int operation, int *status, bool *access_type_supported) +{ + const u64 active_mask = CBQRI_CONTROL_REGISTERS_OP_MASK | + CBQRI_CONTROL_REGISTERS_AT_MASK | + CBQRI_CONTROL_REGISTERS_RCID_MASK; + u64 reg, saved_reg; + int at; + + /* + * Default the output to false so the status==0 (feature not + * implemented) path returns a deterministic value to the caller + * rather than leaving an uninitialized bool. + */ + *access_type_supported = false; + + /* Keep the initial register value to preserve the WPRI fields */ + reg = cbqri_readq(ctrl->base + reg_offset); + saved_reg = reg; + + /* Drain any in-flight firmware op before issuing our own write. */ + if (cbqri_wait_busy_flag(ctrl, reg_offset, &saved_reg) < 0) { + pr_err("BUSY timeout before probe operation\n"); + return -EIO; + } + + /* + * Execute the requested operation with all active fields + * (OP/AT/RCID) zeroed except OP itself. Every bit not in + * active_mask is WPRI and gets carried over from saved_reg. + */ + reg = (saved_reg & ~active_mask) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation); + writel(lower_32_bits(reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout during operation\n"); + return -EIO; + } + + /* Get the operation status */ + *status = FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg); + + /* + * Check for the AT support if the register is implemented + * (if not, the status value will remain 0) + */ + if (*status != 0) { + /* + * Re-issue operation with AT=CODE so the controller + * latches AT=CODE on supported hardware (or resets it to 0 + * on hardware that doesn't). OP must be a defined CBQRI op + * here. OP=0 is a no-op and would silently disable CDP. + */ + reg = (saved_reg & ~active_mask) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, + CBQRI_CONTROL_REGISTERS_AT_CODE); + writel(lower_32_bits(reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err("BUSY timeout setting AT field\n"); + return -EIO; + } + + /* + * If the AT field value has been reset to zero, + * then the AT support is not present + */ + at = FIELD_GET(CBQRI_CONTROL_REGISTERS_AT_MASK, reg); + if (at == CBQRI_CONTROL_REGISTERS_AT_CODE) + *access_type_supported = true; + } + + /* + * Restore the original register value. + * Clear OP to avoid re-triggering the probe op. + */ + saved_reg &= ~CBQRI_CONTROL_REGISTERS_OP_MASK; + writel(lower_32_bits(saved_reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, NULL) < 0) { + pr_err("BUSY timeout restoring register value\n"); + return -EIO; + } + + return 0; +} + +static int cbqri_probe_cc(struct cbqri_controller *ctrl) +{ + int err, status; + int ver_major, ver_minor; + u64 reg; + + reg = cbqri_readq(ctrl->base + CBQRI_CC_CAPABILITIES_OFF); + if (reg == 0) + return -ENODEV; + + ver_minor = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MINOR_MASK, reg); + ver_major = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK, reg); + ctrl->cc.ncblks = FIELD_GET(CBQRI_CC_CAPABILITIES_NCBLKS_MASK, reg); + + pr_debug("version=%d.%d ncblks=%d cache_level=%d\n", + ver_major, ver_minor, + ctrl->cc.ncblks, ctrl->cache.cache_level); + + /* + * NCBLKS == 0 would divide-by-zero in the schemata math while + * ctrl->lock is held. + */ + if (!ctrl->cc.ncblks) { + pr_warn("CC at %pa has 0 capacity blocks, skipping\n", + &ctrl->addr); + return -ENODEV; + } + + /* + * resctrl represents the capacity bitmask (CBM) as a u32, so a + * controller with more than 32 capacity blocks cannot be expressed + * through the schemata interface. Skip it rather than truncate. + */ + if (ctrl->cc.ncblks > 32) { + pr_warn("CC at %pa has ncblks=%u > 32 (resctrl CBM is u32), skipping\n", + &ctrl->addr, ctrl->cc.ncblks); + return -ENODEV; + } + + /* + * On a CUNITS-capable controller, CONFIG_LIMIT also consumes cc_cunits, + * whose reset value is unspecified. The driver clears it to 0 (no limit) + * on every CONFIG_LIMIT, so cc_cunits at 0x28 must be within the mapping. + */ + ctrl->cc.cunits = FIELD_GET(CBQRI_CC_CAPABILITIES_CUNITS_MASK, reg); + if (ctrl->cc.cunits && ctrl->size < CBQRI_CC_CUNITS_OFF + 8) { + pr_warn("CC at %pa supports CUNITS but maps only %pa, skipping\n", + &ctrl->addr, &ctrl->size); + return -ENODEV; + } + + /* Probe allocation features */ + err = cbqri_probe_feature(ctrl, CBQRI_CC_ALLOC_CTL_OFF, + CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + &status, &ctrl->cc.supports_alloc_at_code); + if (err) + return err; + + if (status == CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) + ctrl->alloc_capable = true; + + return 0; +} + +static int cbqri_probe_controller(struct cbqri_controller *ctrl) +{ + int err; + + pr_debug("controller info: type=%d addr=%pa size=%pa max-rcid=%u\n", + ctrl->type, &ctrl->addr, &ctrl->size, ctrl->rcid_count); + + if (!ctrl->addr) { + pr_warn("controller has invalid addr=0x0, skipping\n"); + return -EINVAL; + } + + if (ctrl->size < CBQRI_CTRL_MIN_REG_SPAN) { + pr_warn("controller at %pa: size %pa < minimum 0x%x, skipping\n", + &ctrl->addr, &ctrl->size, CBQRI_CTRL_MIN_REG_SPAN); + return -EINVAL; + } + + if (!request_mem_region(ctrl->addr, ctrl->size, "cbqri_controller")) { + pr_err("request_mem_region failed for %pa\n", &ctrl->addr); + return -EBUSY; + } + + ctrl->base = ioremap(ctrl->addr, ctrl->size); + if (!ctrl->base) { + pr_err("ioremap failed for %pa\n", &ctrl->addr); + err = -ENOMEM; + goto err_release; + } + + switch (ctrl->type) { + case CBQRI_CONTROLLER_TYPE_CAPACITY: + err = cbqri_probe_cc(ctrl); + break; + default: + pr_err("unknown controller type %d\n", ctrl->type); + err = -ENODEV; + break; + } + + if (err) + goto err_iounmap; + + return 0; + +err_iounmap: + iounmap(ctrl->base); + ctrl->base = NULL; +err_release: + release_mem_region(ctrl->addr, ctrl->size); + return err; +} + +void cbqri_controller_destroy(struct cbqri_controller *ctrl) +{ + /* + * cbqri_probe_controller() clears ctrl->base on its error paths and + * releases the mem region itself, so reach into both only when + * destroy is rolling back a successful probe. + */ + if (ctrl->base) { + iounmap(ctrl->base); + release_mem_region(ctrl->addr, ctrl->size); + } + kfree(ctrl); +} + +/** + * riscv_cbqri_register_cc_dt() - register a DT-described capacity controller + * @info: registration descriptor. info->cache_id is used as the + * resctrl domain id. info->type must be CAPACITY. + * @cache_level: cache level (2 or 3) the controller backs, mapped to the + * resctrl L2/L3 resource by the resctrl glue. + * + * The cache level and id (info->cache_id) are supplied directly by the + * caller, which resolves them from cacheinfo. Domain membership is resolved + * per CPU at hotplug, so no cpu mask is stored. There is no firmware table + * to resolve any of this from. + * + * Return: 0 on success, or a negative errno on failure. + */ +int riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level) +{ + struct cbqri_controller *ctrl; + int err; + + if (!info->addr) { + pr_warn("skipping controller with invalid addr=0x0\n"); + return -EINVAL; + } + + if (info->type != CBQRI_CONTROLLER_TYPE_CAPACITY) { + pr_warn("register_cc_dt called with non-capacity type %u\n", + info->type); + return -EINVAL; + } + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + mutex_init(&ctrl->lock); + + ctrl->addr = info->addr; + ctrl->size = info->size; + ctrl->type = info->type; + ctrl->rcid_count = info->rcid_count; + + /* + * SRMCFG encodes RCID in 12 bits. Reject an out-of-range count rather + * than silently truncating in every FIELD_PREP(SRMCFG_RCID_MASK, closid) + * on the schedule-in fast path. + */ + if (ctrl->rcid_count > FIELD_MAX(SRMCFG_RCID_MASK) + 1) { + pr_warn("CC at %pa has RCID count %u beyond the 12-bit SRMCFG field, skipping\n", + &ctrl->addr, ctrl->rcid_count); + cbqri_controller_destroy(ctrl); + return -EINVAL; + } + + ctrl->cache.cache_id = info->cache_id; + ctrl->cache.cache_level = cache_level; + + err = cbqri_probe_controller(ctrl); + if (err) { + cbqri_controller_destroy(ctrl); + return err; + } + + /* + * Allocation capability comes from the capabilities register probed + * above, not from device tree. rcid_count only bounds the RCID range, + * so a controller the hardware reports as alloc-capable but described + * with no RCID count cannot be driven. Reject that inconsistency. A + * monitoring-only controller (not alloc_capable) needs no RCID count. + */ + if (ctrl->alloc_capable && !ctrl->rcid_count) { + pr_warn("CC at %pa is alloc-capable but has no RCID count, skipping\n", + &ctrl->addr); + cbqri_controller_destroy(ctrl); + return -EINVAL; + } + + guard(mutex)(&cbqri_controllers_lock); + list_add_tail(&ctrl->list, &cbqri_controllers); + return 0; +} diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h new file mode 100644 index 000000000000..77429f39f874 --- /dev/null +++ b/drivers/resctrl/cbqri_internal.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _DRIVERS_RESCTRL_CBQRI_INTERNAL_H +#define _DRIVERS_RESCTRL_CBQRI_INTERNAL_H + +#include <linux/bitfield.h> +#include <linux/riscv_cbqri.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/types.h> + +/* Capacity Controller (CC) MMIO register offsets. */ +#define CBQRI_CC_CAPABILITIES_OFF 0 +#define CBQRI_CC_ALLOC_CTL_OFF 24 +#define CBQRI_CC_BLOCK_MASK_OFF 32 + +/* + * Per CBQRI 3.5 the block-mask width BMW is the smallest multiple of 64 bits + * that holds NCBLKS, so cc_cunits sits at 32 + BMW/8 bytes. This constant is + * valid only while NCBLKS <= 64. cbqri_probe_cc() rejects ncblks > 32 before + * it reads cc_capabilities.CUNITS, forcing BMW to 64 bits, one 8-byte block + * mask, and cc_cunits at 0x28. Raising that cap above 64 would require + * computing 32 + roundup(ncblks, 64) / 8 instead of a constant. + */ +#define CBQRI_CC_CUNITS_OFF 40 + +/* + * Highest base register offset (cc_block_mask at 0x20) plus its 8-byte width. + * cbqri_probe_controller() rejects smaller mappings. cc_cunits at 0x28 is + * optional and only required when cc_capabilities.CUNITS is set, which + * cbqri_probe_cc() checks against the mapping size separately. + */ +#define CBQRI_CTRL_MIN_REG_SPAN 0x28u + +#define CBQRI_CC_CAPABILITIES_VER_MINOR_MASK GENMASK_ULL(3, 0) +#define CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK GENMASK_ULL(7, 4) +#define CBQRI_CC_CAPABILITIES_NCBLKS_MASK GENMASK_ULL(23, 8) +#define CBQRI_CC_CAPABILITIES_CUNITS_MASK BIT_ULL(25) + +/* + * CC control registers are 64-bit, but the CBQRI spec only guarantees + * single-copy atomicity for naturally aligned 4-byte accesses. They are read + * as two 32-bit halves (cbqri_readq) reconstructed into a u64 for field + * extraction, and written via the low 32-bit half, so the driver does not + * depend on native 64-bit MMIO. Keep every field mask GENMASK_ULL so + * FIELD_MODIFY() or ~mask on the reconstructed u64 never zero-extends a + * 32-bit mask and clobbers STATUS/BUSY/WPRI in bits 63:32. + */ +#define CBQRI_CONTROL_REGISTERS_OP_MASK GENMASK_ULL(4, 0) +#define CBQRI_CONTROL_REGISTERS_AT_MASK GENMASK_ULL(7, 5) +/* AT field values (CBQRI Table 1): data vs code half for CDP */ +#define CBQRI_CONTROL_REGISTERS_AT_DATA 0 +#define CBQRI_CONTROL_REGISTERS_AT_CODE 1 +#define CBQRI_CONTROL_REGISTERS_RCID_MASK GENMASK_ULL(19, 8) +#define CBQRI_CONTROL_REGISTERS_STATUS_MASK GENMASK_ULL(38, 32) +#define CBQRI_CONTROL_REGISTERS_BUSY_MASK GENMASK_ULL(39, 39) + +#define CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT 1 +#define CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT 2 +#define CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS 1 + +/* Capacity Controller hardware capabilities */ +struct riscv_cbqri_capacity_caps { + u16 ncblks; + bool supports_alloc_at_code; + /* cc_capabilities.CUNITS: controller enforces a capacity-unit limit */ + bool cunits; +}; + +/** + * struct cbqri_cc_config - desired capacity allocation state for one rcid + * @cbm: capacity block mask + * @at: AT half the @cbm applies to (CBQRI_CONTROL_REGISTERS_AT_DATA + * or CBQRI_CONTROL_REGISTERS_AT_CODE) + * @cdp_enabled: when false and the controller supports AT, mirror @cbm + * into the other AT half so both stay in sync + */ +struct cbqri_cc_config { + u64 cbm; + u32 at; + bool cdp_enabled; +}; + +struct cbqri_controller { + void __iomem *base; + /* + * Serializes the write-then-poll-busy MMIO sequences on this + * controller. Each CBQRI op may busy-wait up to 1 ms on slow + * firmware, so use a sleeping mutex to keep preemption enabled. + * All resctrl-arch entry points run in process context. + */ + struct mutex lock; + + struct riscv_cbqri_capacity_caps cc; + + bool alloc_capable; + + phys_addr_t addr; + phys_addr_t size; + enum cbqri_controller_type type; + u32 rcid_count; + + struct list_head list; + + struct cache_controller { + u32 cache_level; + /* Cache id used as the resctrl domain id */ + u32 cache_id; + } cache; +}; + +extern struct list_head cbqri_controllers; +extern struct mutex cbqri_controllers_lock; + +void cbqri_controller_destroy(struct cbqri_controller *ctrl); + +int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid, + const struct cbqri_cc_config *cfg); + +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid, + u32 at, u32 *cbm_out); + +#endif /* _DRIVERS_RESCTRL_CBQRI_INTERNAL_H */ diff --git a/include/linux/riscv_cbqri.h b/include/linux/riscv_cbqri.h new file mode 100644 index 000000000000..e6b3febbed89 --- /dev/null +++ b/include/linux/riscv_cbqri.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Public registration API for the RISC-V Capacity and Bandwidth QoS + * Register Interface (CBQRI) core. Discovery layers (device tree + * platform drivers) call riscv_cbqri_register_cc_dt() to hand a capacity + * controller descriptor to the core, which owns all subsequent state. + */ +#ifndef _LINUX_RISCV_CBQRI_H +#define _LINUX_RISCV_CBQRI_H + +#include <linux/types.h> + +enum cbqri_controller_type { + CBQRI_CONTROLLER_TYPE_CAPACITY, +}; + +/** + * struct cbqri_controller_info - registration descriptor + * @addr: MMIO base address of the controller's register interface + * @size: size of the MMIO region + * @type: controller type (capacity) + * @rcid_count: number of supported RCIDs + * @cache_id: cache id used as the resctrl domain id + */ +struct cbqri_controller_info { + phys_addr_t addr; + phys_addr_t size; + enum cbqri_controller_type type; + u32 rcid_count; + u32 cache_id; +}; + +#if IS_ENABLED(CONFIG_RISCV_CBQRI) +int riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level); +#else +static inline int +riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level) +{ + return -ENODEV; +} +#endif + +#endif /* _LINUX_RISCV_CBQRI_H */ -- 2.43.0