[PATCH RFC v3 10/11] ACPI: RISC-V: Parse RISC-V Quality of Service Controller (RQSC) table

Drew Fustini fustini at kernel.org
Tue Apr 14 18:54:04 PDT 2026


Add parser for the ACPI RQSC table which describes the capacity and
bandwidth QoS controllers in a system. For each table entry, allocate a
cbqri_controller struct and populate it with the controller type, MMIO
base address, RCID/MCID counts, and resource identifiers (cache ID for
capacity controllers, proximity domain for bandwidth controllers).

Cache controller cpumasks are resolved via
acpi_pptt_get_cpumask_from_cache_id(). Bandwidth controller cpumasks
are derived from the proximity domain's NUMA node. Controllers with
invalid addresses or failed cpumask lookups are skipped with a warning.

The populated controller list is consumed by qos_resctrl_setup() in
arch/riscv/kernel/qos/ to probe the hardware and register resctrl
domains.

Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0
Link: https://github.com/riscv-non-isa/riscv-rqsc/blob/main/src/
Signed-off-by: Drew Fustini <fustini at kernel.org>
---
 MAINTAINERS                   |   1 +
 arch/riscv/include/asm/acpi.h |  10 ++++
 drivers/acpi/riscv/Makefile   |   1 +
 drivers/acpi/riscv/rqsc.c     | 136 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 148 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index af9698a16439..d5ec7d29bf11 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22786,6 +22786,7 @@ S:	Supported
 F:	arch/riscv/include/asm/qos.h
 F:	arch/riscv/include/asm/resctrl.h
 F:	arch/riscv/kernel/qos/
+F:	drivers/acpi/riscv/rqsc.c
 F:	include/linux/riscv_qos.h
 
 RISC-V RPMI AND MPXY DRIVERS
diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
index 6e13695120bc..62296a2a519b 100644
--- a/arch/riscv/include/asm/acpi.h
+++ b/arch/riscv/include/asm/acpi.h
@@ -71,6 +71,16 @@ int acpi_get_riscv_isa(struct acpi_table_header *table,
 
 void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
 			     u32 *cboz_size, u32 *cbop_size);
+
+#ifdef CONFIG_RISCV_ISA_SSQOSID
+int __init acpi_parse_rqsc(struct acpi_table_header *table);
+#else
+static inline int acpi_parse_rqsc(struct acpi_table_header *table)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_RISCV_ISA_SSQOSID */
+
 #else
 static inline void acpi_init_rintc_map(void) { }
 static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)
diff --git a/drivers/acpi/riscv/Makefile b/drivers/acpi/riscv/Makefile
index 1284a076fa88..d7ae8729987a 100644
--- a/drivers/acpi/riscv/Makefile
+++ b/drivers/acpi/riscv/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y					+= rhct.o init.o irq.o
+obj-$(CONFIG_RISCV_ISA_SSQOSID)		+= rqsc.o
 obj-$(CONFIG_ACPI_PROCESSOR_IDLE)	+= cpuidle.o
 obj-$(CONFIG_ACPI_CPPC_LIB)		+= cppc.o
 obj-$(CONFIG_ACPI_RIMT)			+= rimt.o
diff --git a/drivers/acpi/riscv/rqsc.c b/drivers/acpi/riscv/rqsc.c
new file mode 100644
index 000000000000..f647051be0bf
--- /dev/null
+++ b/drivers/acpi/riscv/rqsc.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Tenstorrent
+ *	Author: Drew Fustini <fustini at kernel.org>
+ */
+
+#define pr_fmt(fmt) "ACPI: RQSC: " fmt
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/riscv_qos.h>
+
+#define CBQRI_CTRL_SIZE 0x1000
+
+int __init acpi_parse_rqsc(struct acpi_table_header *table)
+{
+	struct acpi_table_rqsc *rqsc;
+	struct acpi_table_rqsc_fields *end;
+	struct acpi_table_rqsc_fields *node;
+	int err;
+	int num_controllers = 0;
+
+	rqsc = (struct acpi_table_rqsc *)table;
+
+	end = ACPI_ADD_PTR(struct acpi_table_rqsc_fields, rqsc, rqsc->header.length);
+
+	for (node = ACPI_ADD_PTR(struct acpi_table_rqsc_fields, rqsc,
+				 sizeof(struct acpi_table_rqsc));
+	     node < end;
+	     node = ACPI_ADD_PTR(struct acpi_table_rqsc_fields, node, node->length)
+	) {
+		struct cbqri_controller *ctrl;
+
+		if (node->length < sizeof(*node)) {
+			pr_err("malformed RQSC entry: length %u < %zu, aborting\n",
+			       node->length, sizeof(*node));
+			err = -EINVAL;
+			goto err_free_controllers;
+		}
+
+		ctrl = kzalloc_obj(*ctrl, GFP_KERNEL);
+		if (!ctrl) {
+			err = -ENOMEM;
+			goto err_free_controllers;
+		}
+
+		ctrl->type = node->type;
+		/* reg[1] is the MMIO base address per the RQSC table layout */
+		ctrl->addr = node->reg[1];
+		ctrl->size = CBQRI_CTRL_SIZE;
+		ctrl->rcid_count = node->rcid;
+		ctrl->mcid_count = node->mcid;
+
+		if (!ctrl->addr) {
+			pr_warn("skipping controller with invalid addr=0x0\n");
+			kfree(ctrl);
+			continue;
+		}
+
+		if (node->nres == 0) {
+			pr_warn("controller at %pa has no resource descriptors, skipping\n",
+				&ctrl->addr);
+			kfree(ctrl);
+			continue;
+		}
+
+		if (node->length < sizeof(*node) + sizeof(node->res[0])) {
+			pr_warn("controller at %pa: node too short for resource descriptor, skipping\n",
+				&ctrl->addr);
+			kfree(ctrl);
+			continue;
+		}
+
+		if (node->nres > 1)
+			pr_warn("controller at %pa has %u resource descriptors, using first\n",
+				&ctrl->addr, node->nres);
+
+		pr_debug("Found controller with type %u addr %pa size %pa rcid %u mcid %u\n",
+			 ctrl->type, &ctrl->addr, &ctrl->size,
+			 ctrl->rcid_count, ctrl->mcid_count);
+		if (ctrl->type == CBQRI_CONTROLLER_TYPE_CAPACITY) {
+			ctrl->cache.cache_id = (u32)node->res[0].id1;
+			ctrl->cache.cache_level =
+				find_acpi_cache_level_from_id(ctrl->cache.cache_id);
+
+			if (acpi_pptt_get_cache_size_from_id(ctrl->cache.cache_id,
+							     &ctrl->cache.cache_size)) {
+				pr_warn("failed to determine size for cache id 0x%x\n",
+					ctrl->cache.cache_id);
+				ctrl->cache.cache_size = 0;
+			}
+
+			pr_debug("Cache controller has ID 0x%x level %u size %u\n",
+				 ctrl->cache.cache_id, ctrl->cache.cache_level,
+				 ctrl->cache.cache_size);
+
+			/*
+			 * For CBQRI, any cpu (technically a hart in RISC-V terms)
+			 * can access the memory-mapped registers of any CBQRI
+			 * controller in the system.
+			 */
+			err = acpi_pptt_get_cpumask_from_cache_id(ctrl->cache.cache_id,
+								  &ctrl->cache.cpu_mask);
+			if (err) {
+				pr_warn("Failed to get cpumask for cache id 0x%x (%d), skipping\n",
+					ctrl->cache.cache_id, err);
+				kfree(ctrl);
+				continue;
+			}
+
+		} else if (ctrl->type == CBQRI_CONTROLLER_TYPE_BANDWIDTH) {
+			ctrl->mem.prox_dom = (u32)node->res[0].id1;
+			cpumask_copy(&ctrl->mem.cpu_mask,
+				     cpumask_of_node(pxm_to_node(ctrl->mem.prox_dom)));
+			pr_debug("Memory controller with proximity domain %u\n",
+				 ctrl->mem.prox_dom);
+		}
+
+		/* List shared with RISC-V QoS resctrl implementation */
+		list_add_tail(&ctrl->list, &cbqri_controllers);
+		num_controllers++;
+	}
+
+	pr_info("found %d CBQRI controllers\n", num_controllers);
+	return 0;
+
+err_free_controllers:
+	while (!list_empty(&cbqri_controllers)) {
+		struct cbqri_controller *ctrl;
+
+		ctrl = list_first_entry(&cbqri_controllers, struct cbqri_controller, list);
+		list_del(&ctrl->list);
+		kfree(ctrl);
+	}
+	return err;
+}

-- 
2.43.0




More information about the linux-riscv mailing list