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

Drew Fustini <[email protected]>
Newsgroups dev.linux.lists.acpica-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-acpi,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 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 <[email protected]>
+ */
+
+#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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.