mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface
@ 2026-09-22 12:41 Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 1/8] ACPI: CPPC: Prepare cpc_register_resource for Package-type entries Lifeng Zheng
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

This series implements support for the CPPC v4 Resource Priority Register
mechanism defined in ACPI 6.6, Section 8.4.6.1.2.7, and exposes it to
userspace via sysfs.

Resource Priority allows OSPM to control the relative priority among
processors for shared resources. OSPM can utilize these sysfs interfaces
to configure resource priorities, allowing resources to be preferentially
allocated to more important tasks.

The controlled resource types include processor boost, throttle, L2 cache,
L3 cache, and memory bandwidth.  Each Resource Priority group consists of:

  - CONTROLLED_RESOURCES: which resource types the group affects
  - ENABLE_VALUE / ENABLE_REGISTER: enable/disable the group
  - PRIORITY_COUNT / PRIORITY_REGISTER: read/write the priority level

The patch series is organized in three parts:

  - patches 1-4 extend the CPPC data structures and _CPC parser to handle
    Package-type entries and parse Resource Priority sub-packages into
    structured descriptors.

  - patches 5-7 refactor existing register I/O helpers to support direct
    register access and add accessor functions for Resource Priority
    attributes (enable, priority count, priority value).

  - patch 8 creates a "resource_priority" sysfs hierarchy under each
    cpufreq policy to expose the Resource Priority attributes to userspace.

Changelog:
v4:
 - Patch 7: Set *num_resources to 0 when controlled resources number
   invalid in cppc_get_resource_priority_resources(), therefore, the caller
   will not read the unassigned `resources` pointer.
 - Patch 8: Remove kfree() from the error path of
   cppc_create_res_prio_sysfs() because cppc_res_prio_release() will do it.

v3:
 - Patch 2: Add the (i - 2) == RESOURCE_PRIORITY condition back to ensure
   the new code behaves the same as the original.
 - Patch 4: Assign -ENODATA to ret when an unexpected ACPI_TYPE_PACKAGE
   appears.
 - Patch 5: Initialize the `optional` field of the remaining cpc_regs to
   true so that they are treated as unsupported.
 - Patch 8: Call cppc_cpufreq_cpu_fie_exit() and set perf to lowest_perf
   when cppc_create_res_prio_sysfs() fails. Call kobject_put() and kfree()
   when kobject_init_and_add() fails in cppc_create_res_prio_sysfs().
 - Link: https://lore.kernel.org/all/20260812015217.74598-1-zhenglifeng1@huawei.com/

v2:
 - Patch 1: Revert changes to the CPC_SUPPORTED() macro.
 - Link: https://lore.kernel.org/all/20260804085042.4118193-1-zhenglifeng1@huawei.com/

v1:
 - Link: https://lore.kernel.org/all/20260717024502.3520445-1-zhenglifeng1@huawei.com/

---

RFC: sysfs placement

The sysfs interface is currently placed under /sys/devices/system/cpu/cpufreq/
(i.e. the cpufreq policy directory). I am unsure whether this is the best
location and would appreciate reviewer feedback.

The concern is that Resource Priority covers resource types beyond CPU
frequency control:

  - PROCESSOR_BOOST, PROCESSOR_THROTTLE -- clearly CPU-frequency related
  - L2_CACHE, L3_CACHE -- cache partitioning, not frequency
  - MEMORY_BANDWIDTH -- memory QoS, not frequency

Placing the attributes under cpufreq makes sense for boost/throttle but
feels semantically wrong for cache and memory bandwidth resources.

I would appreciate feedback on whether cpufreq is the right home for
this interface, or whether a different location would be more appropriate.

Lifeng Zheng (8):
  ACPI: CPPC: Prepare cpc_register_resource for Package-type entries
  ACPI: CPPC: Refactor element parsing into parse_cpc_element()
  ACPI: CPPC: Refactor resource cleanup into free_reg_resource()
  ACPI: CPPC: Parse Resource Priority Register entries from _CPC package
  ACPI: CPPC: Store optional flag in cpc_register_resource
  ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg()
  ACPI: CPPC: Add Resource Priority accessors
  cpufreq: cppc: Expose Resource Priority attributes via sysfs

 drivers/acpi/cppc_acpi.c       | 719 +++++++++++++++++++++++++++------
 drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++++-
 include/acpi/cppc_acpi.h       |  60 +++
 3 files changed, 930 insertions(+), 130 deletions(-)

-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 1/8] ACPI: CPPC: Prepare cpc_register_resource for Package-type entries
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 2/8] ACPI: CPPC: Refactor element parsing into parse_cpc_element() Lifeng Zheng
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) introduces the Resource
Priority entry, which is a Package of sub-packages rather than a
plain Integer or Buffer register.  The existing cpc_register_resource
union only accommodates Integer and Buffer (register descriptor)
fields.

Add a Package variant to the cpc_entry union so that nested
structures such as RESOURCE_PRIORITY can store their element count
and a dynamically allocated array of child cpc_register_resource
descriptors.

No functional change intended; the new Package fields are not yet
consumed by any caller.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 include/acpi/cppc_acpi.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 8693890a7275..1839582b80be 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -71,6 +71,14 @@ struct cpc_register_resource {
 	union {
 		struct cpc_reg reg;
 		u64 int_value;
+		/*
+		 * CPPC v4: nested Package (e.g. RESOURCE_PRIORITY),
+		 * elements dynamically allocated
+		 */
+		struct {
+			u32 count;
+			struct cpc_register_resource *elements;
+		} package;
 	} cpc_entry;
 };
 
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 2/8] ACPI: CPPC: Refactor element parsing into parse_cpc_element()
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 1/8] ACPI: CPPC: Prepare cpc_register_resource for Package-type entries Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 3/8] ACPI: CPPC: Refactor resource cleanup into free_reg_resource() Lifeng Zheng
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

acpi_cppc_processor_probe() contains a large inline block (~70 lines)
that handles ACPI_TYPE_INTEGER and ACPI_TYPE_BUFFER entries in the _CPC
package.  This block will need to be reused for parsing individual
elements inside the nested RESOURCE_PRIORITY sub-packages that CPPC v4
defines (Section 8.4.6.1.2.7).

Extract the Integer / Buffer handling into a standalone parse_cpc_element()
helper so that both the top-level _CPC loop and the upcoming package
parser can share the same logic for register validation, PCC subspace
tracking, SystemMemory ioremap, and SystemIO / FFH checks.

No functional change; the new function reproduces the original behaviour
exactly.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 186 +++++++++++++++++++++++----------------
 1 file changed, 108 insertions(+), 78 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9f572f481241..47ececf214c4 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -650,6 +650,109 @@ static int pcc_data_alloc(int pcc_ss_id)
 	return 0;
 }
 
+/**
+ * parse_cpc_element - Parse a single CPC element into a cpc_register_resource.
+ * @cpc_obj:          Pointer to the ACPI object representing the CPC element.
+ * @cpc_reg:          Output CPC register resource to populate.
+ * @pcc_subspace_id:  In/out pointer to PCC subspace ID; extracted once on first
+ *                    PCC-type register and validated for consistency thereafter.
+ * @cpu:              CPU number, used for debug messages.
+ * @entry_num:        Index within the CPC table entries, used for diagnostics.
+ *
+ * Handles ACPI_TYPE_INTEGER (static value) and ACPI_TYPE_BUFFER (register
+ * descriptor).  Sets up PCC subspace tracking, ioremap for SystemMemory,
+ * and validates SystemIO / FFH register parameters.
+ *
+ * Return: 0 on success, -ENODATA on invalid or unsupported data.
+ */
+static int parse_cpc_element(union acpi_object *cpc_obj,
+			     struct cpc_register_resource *cpc_reg,
+			     int *pcc_subspace_id, u32 cpu, unsigned int entry_num)
+{
+	struct cpc_reg *gas_t;
+
+	if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
+		cpc_reg->type = ACPI_TYPE_INTEGER;
+		cpc_reg->cpc_entry.int_value = cpc_obj->integer.value;
+	} else if (cpc_obj->type == ACPI_TYPE_BUFFER) {
+		gas_t = (struct cpc_reg *)cpc_obj->buffer.pointer;
+
+		/*
+		 * The PCC Subspace index is encoded inside
+		 * the CPC table entries. The same PCC index
+		 * will be used for all the PCC entries,
+		 * so extract it only once.
+		 */
+		if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
+			if (*pcc_subspace_id < 0) {
+				*pcc_subspace_id = gas_t->access_width;
+				if (pcc_data_alloc(*pcc_subspace_id))
+					return -ENODATA;
+			} else if (*pcc_subspace_id != gas_t->access_width) {
+				pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
+					 cpu);
+				return -ENODATA;
+			}
+		} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+			if (gas_t->address) {
+				void __iomem *addr;
+				size_t access_width;
+
+				if (!osc_cpc_flexible_adr_space_confirmed) {
+					pr_debug("Flexible address space capability not supported\n");
+					if (!cpc_supported_by_cpu())
+						return -ENODATA;
+				}
+
+				access_width = GET_BIT_WIDTH(gas_t) / 8;
+				addr = ioremap(gas_t->address, access_width);
+				if (!addr)
+					return -ENODATA;
+				cpc_reg->sys_mem_vaddr = addr;
+			}
+		} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+			if (gas_t->access_width < 1 || gas_t->access_width > 3) {
+				/*
+				 * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit.
+				 * SystemIO doesn't implement 64-bit
+				 * registers.
+				 */
+				pr_debug("Invalid access width %d for SystemIO register in _CPC\n",
+					 gas_t->access_width);
+				return -ENODATA;
+			}
+			if (gas_t->address & OVER_16BTS_MASK) {
+				/* SystemIO registers use 16-bit integer addresses */
+				pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n",
+					 gas_t->address);
+				return -ENODATA;
+			}
+			if (!osc_cpc_flexible_adr_space_confirmed) {
+				pr_debug("Flexible address space capability not supported\n");
+				if (!cpc_supported_by_cpu())
+					return -ENODATA;
+			}
+		} else {
+			if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE ||
+			    !cpc_ffh_supported()) {
+				/* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */
+				pr_debug("Unsupported register type (%d) in _CPC\n",
+					 gas_t->space_id);
+				return -ENODATA;
+			}
+		}
+
+		cpc_reg->type = ACPI_TYPE_BUFFER;
+		memcpy(&cpc_reg->cpc_entry.reg, gas_t, sizeof(*gas_t));
+	} else {
+		pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
+			 entry_num, cpu);
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
 /*
  * An example CPC table looks like the following.
  *
@@ -690,7 +793,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
 	union acpi_object *out_obj, *cpc_obj;
 	struct cpc_desc *cpc_ptr;
-	struct cpc_reg *gas_t;
 	struct device *cpu_dev;
 	acpi_handle handle = pr->handle;
 	unsigned int num_ent, i, cpc_rev;
@@ -778,80 +880,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	for (i = 2; i < num_ent; i++) {
 		cpc_obj = &out_obj->package.elements[i];
 
-		if (cpc_obj->type == ACPI_TYPE_INTEGER)	{
-			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
-			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value;
-		} else if (cpc_obj->type == ACPI_TYPE_BUFFER) {
-			gas_t = (struct cpc_reg *)
-				cpc_obj->buffer.pointer;
-
-			/*
-			 * The PCC Subspace index is encoded inside
-			 * the CPC table entries. The same PCC index
-			 * will be used for all the PCC entries,
-			 * so extract it only once.
-			 */
-			if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
-				if (pcc_subspace_id < 0) {
-					pcc_subspace_id = gas_t->access_width;
-					if (pcc_data_alloc(pcc_subspace_id))
-						goto out_free;
-				} else if (pcc_subspace_id != gas_t->access_width) {
-					pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
-						 pr->id);
-					goto out_free;
-				}
-			} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
-				if (gas_t->address) {
-					void __iomem *addr;
-					size_t access_width;
-
-					if (!osc_cpc_flexible_adr_space_confirmed) {
-						pr_debug("Flexible address space capability not supported\n");
-						if (!cpc_supported_by_cpu())
-							goto out_free;
-					}
-
-					access_width = GET_BIT_WIDTH(gas_t) / 8;
-					addr = ioremap(gas_t->address, access_width);
-					if (!addr)
-						goto out_free;
-					cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
-				}
-			} else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
-				if (gas_t->access_width < 1 || gas_t->access_width > 3) {
-					/*
-					 * 1 = 8-bit, 2 = 16-bit, and 3 = 32-bit.
-					 * SystemIO doesn't implement 64-bit
-					 * registers.
-					 */
-					pr_debug("Invalid access width %d for SystemIO register in _CPC\n",
-						 gas_t->access_width);
-					goto out_free;
-				}
-				if (gas_t->address & OVER_16BTS_MASK) {
-					/* SystemIO registers use 16-bit integer addresses */
-					pr_debug("Invalid IO port %llu for SystemIO register in _CPC\n",
-						 gas_t->address);
-					goto out_free;
-				}
-				if (!osc_cpc_flexible_adr_space_confirmed) {
-					pr_debug("Flexible address space capability not supported\n");
-					if (!cpc_supported_by_cpu())
-						goto out_free;
-				}
-			} else {
-				if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
-					/* Support only PCC, SystemMemory, SystemIO, and FFH type regs. */
-					pr_debug("Unsupported register type (%d) in _CPC\n",
-						 gas_t->space_id);
-					goto out_free;
-				}
-			}
-
-			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
-			memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
-		} else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
+		if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
 			/*
 			 * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a
 			 * Package of Resource Priority Register Descriptor sub-packages.
@@ -862,9 +891,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
 			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0;
 		} else {
-			pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
-				 i, pr->id);
-			goto out_free;
+			ret = parse_cpc_element(cpc_obj, &cpc_ptr->cpc_regs[i-2],
+						&pcc_subspace_id, pr->id, i);
+			if (ret)
+				goto out_free;
 		}
 	}
 	per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 3/8] ACPI: CPPC: Refactor resource cleanup into free_reg_resource()
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 1/8] ACPI: CPPC: Prepare cpc_register_resource for Package-type entries Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 2/8] ACPI: CPPC: Refactor element parsing into parse_cpc_element() Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package Lifeng Zheng
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

Both the error path of acpi_cppc_processor_probe() and the normal
cleanup in acpi_cppc_processor_exit() iterate over the cpc_regs[]
array and iounmap() any SystemMemory virtual addresses that were
set up during probe.

CPPC v4 adds Package-type entries that own dynamically allocated
sub-elements which also need to be freed.  Inlining this recursive
cleanup at every call-site would be error-prone and repetitive.

Extract the per-register cleanup logic into free_reg_resource(),
which releases any iomapped address and, for Package-type entries,
recursively frees all child elements and the elements array itself.
Convert both the probe error path and _exit() to use the new helper.

No functional change for existing Integer / Buffer entries.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 41 ++++++++++++++++++++++++++++------------
 1 file changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 47ececf214c4..dce15d875c62 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -753,6 +753,30 @@ static int parse_cpc_element(union acpi_object *cpc_obj,
 	return 0;
 }
 
+/**
+ * free_reg_resource - Free resources held by a CPC register resource.
+ * @cpc_reg: Pointer to the CPC register resource to clean up.
+ *
+ * Releases any iomapped SystemMemory address and, for Package-type
+ * resources, recursively frees all nested elements before freeing the
+ * elements array itself.
+ */
+static void free_reg_resource(struct cpc_register_resource *cpc_reg)
+{
+	void __iomem *addr = cpc_reg->sys_mem_vaddr;
+	int i;
+
+	if (addr)
+		iounmap(addr);
+
+	if (cpc_reg->type == ACPI_TYPE_PACKAGE) {
+		for (i = 0; i < cpc_reg->cpc_entry.package.count; i++)
+			free_reg_resource(&cpc_reg->cpc_entry.package.elements[i]);
+
+		kfree(cpc_reg->cpc_entry.package.elements);
+	}
+}
+
 /*
  * An example CPC table looks like the following.
  *
@@ -965,12 +989,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 
 out_free:
 	/* Free all the mapped sys mem areas for this CPU */
-	for (i = 2; i < cpc_ptr->num_entries; i++) {
-		void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+	for (i = 2; i < cpc_ptr->num_entries; i++)
+		free_reg_resource(&cpc_ptr->cpc_regs[i-2]);
 
-		if (addr)
-			iounmap(addr);
-	}
 	kfree(cpc_ptr);
 
 out_buf_free:
@@ -989,7 +1010,6 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 {
 	struct cpc_desc *cpc_ptr;
 	unsigned int i;
-	void __iomem *addr;
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
 
 	if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
@@ -1007,12 +1027,9 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 	if (!cpc_ptr)
 		return;
 
-	/* Free all the mapped sys mem areas for this CPU */
-	for (i = 2; i < cpc_ptr->num_entries; i++) {
-		addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
-		if (addr)
-			iounmap(addr);
-	}
+	/* Free all the mapped sys mem areas and nested package resources for this CPU */
+	for (i = 2; i < cpc_ptr->num_entries; i++)
+		free_reg_resource(&cpc_ptr->cpc_regs[i-2]);
 
 	kobject_put(&cpc_ptr->kobj);
 	kfree(cpc_ptr);
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (2 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 3/8] ACPI: CPPC: Refactor resource cleanup into free_reg_resource() Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-28 14:06   ` Christian Loehle
  2026-09-22 12:41 ` [PATCH v4 5/8] ACPI: CPPC: Store optional flag in cpc_register_resource Lifeng Zheng
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority
entry as a Package of sub-packages, each containing:

  - CONTROLLED_RESOURCES: a Package of integer resource IDs
  - ENABLE_VALUE / ENABLE_REGISTER: enable/disable control
  - PRIORITY_COUNT / PRIORITY_REGISTER: priority level setting

These allow OSPM to set relative priority among processors for shared
resources such as boost, throttle, L2/L3 cache, and memory bandwidth.

Implement parse_priority_regs() which:

  1. Validates each sub-package has the expected element count
     (RESOURCE_PRIORITY_NUM).
  2. Allocates cpc_register_resource arrays for the sub-package
     elements and the nested CONTROLLED_RESOURCES list.
  3. Parses CONTROLLED_RESOURCES as integers and the remaining
     entries (ENABLE_REGISTER, PRIORITY_REGISTER, etc.) via
     parse_cpc_element() so that register descriptors, PCC subspace
     tracking, and ioremap are handled consistently.
  4. Wires the parser into the main _CPC probe loop, replacing the
     previous "package type not supported" stub with full parsing for
     RESOURCE_PRIORITY while rejecting unexpected Package entries.

The probe and _exit() error/cleanup paths already use
free_reg_resource(), which recursively frees Package-type entries,
so no additional cleanup changes are needed.

Add the resource_priority_regs enumeration that defines the indices
into each Resource Priority sub-package (CONTROLLED_RESOURCES,
ENABLE_VALUE, ENABLE_REGISTER, PRIORITY_COUNT, PRIORITY_REGISTER)
and RESOURCE_PRIORITY_NUM as the element count sentinel.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 137 ++++++++++++++++++++++++++++++++++++---
 include/acpi/cppc_acpi.h |  13 ++++
 2 files changed, 142 insertions(+), 8 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index dce15d875c62..b5563d75d6c5 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -777,6 +777,91 @@ static void free_reg_resource(struct cpc_register_resource *cpc_reg)
 	}
 }
 
+/**
+ * parse_priority_regs - Parse the RESOURCE_PRIORITY nested package structure.
+ * @cpc_obj:         ACPI Package object for the RESOURCE_PRIORITY entry.
+ * @regs:            Output array of cpc_register_resource to fill.
+ * @pcc_subspace_id: In/out pointer to PCC subspace ID.
+ * @cpu:             CPU number, used for debug messages.
+ *
+ * The RESOURCE_PRIORITY entry (CPPC v4) is a Package of sub-packages.
+ * Each sub-package has RESOURCE_PRIORITY_NUM elements:
+ *   [0] = Package of integers (CONTROLLED_RESOURCES list)
+ *   [1] = ENABLE_VALUE, [2] = ENABLE_REGISTER,
+ *   [3] = PRIORITY_COUNT, [4] = PRIORITY_REGISTER
+ *
+ * Return: 0 on success, -ENODATA on malformed data, -ENOMEM on allocation failure.
+ */
+static int parse_priority_regs(union acpi_object *cpc_obj,
+			       struct cpc_register_resource *regs,
+			       int *pcc_subspace_id, u32 cpu)
+{
+	struct cpc_register_resource *reg_elements;
+	union acpi_object reg_desc_obj;
+	unsigned int i, j, resources_count;
+	int ret;
+
+	for (i = 0; i < cpc_obj->package.count; i++) {
+		reg_desc_obj = cpc_obj->package.elements[i];
+		if (reg_desc_obj.type != ACPI_TYPE_PACKAGE ||
+		    reg_desc_obj.package.count != RESOURCE_PRIORITY_NUM) {
+			pr_debug("Malformed priority regs sub-pkg: type %d count %d, expected %d for CPU:%d\n",
+				 reg_desc_obj.type, reg_desc_obj.package.count,
+				 RESOURCE_PRIORITY_NUM, cpu);
+			return -ENODATA;
+		}
+
+		reg_elements = kzalloc_objs(struct cpc_register_resource, RESOURCE_PRIORITY_NUM);
+		if (!reg_elements) {
+			pr_debug("Failed to allocate reg_elements for CPU:%d\n", cpu);
+			return -ENOMEM;
+		}
+
+		/*
+		 * Assign values immediately after successful allocation to ensure that resources
+		 * can be properly released.
+		 */
+		regs[i].type = ACPI_TYPE_PACKAGE;
+		regs[i].cpc_entry.package.count = RESOURCE_PRIORITY_NUM;
+		regs[i].cpc_entry.package.elements = reg_elements;
+
+		resources_count = reg_desc_obj.package.elements[0].package.count;
+
+		if (reg_desc_obj.package.elements[0].type != ACPI_TYPE_PACKAGE ||
+		    !resources_count) {
+			pr_debug("Invalid priority sub-elements: type %d count %d for CPU:%d\n",
+				 reg_desc_obj.package.elements[0].type, resources_count, cpu);
+			return -ENODATA;
+		}
+
+		reg_elements[0].cpc_entry.package.elements =
+			kzalloc_objs(struct cpc_register_resource, resources_count);
+		if (!reg_elements[0].cpc_entry.package.elements) {
+			pr_debug("Failed to allocate %d priority sub-elements for CPU:%d\n",
+				 resources_count, cpu);
+			return -ENOMEM;
+		}
+
+		reg_elements[0].type = ACPI_TYPE_PACKAGE;
+		reg_elements[0].cpc_entry.package.count = resources_count;
+
+		for (j = 0; j < reg_elements[0].cpc_entry.package.count; j++) {
+			reg_elements[0].cpc_entry.package.elements[j].type = ACPI_TYPE_INTEGER;
+			reg_elements[0].cpc_entry.package.elements[j].cpc_entry.int_value =
+				reg_desc_obj.package.elements[0].package.elements[j].integer.value;
+		}
+
+		for (j = 1; j < RESOURCE_PRIORITY_NUM; j++) {
+			ret = parse_cpc_element(&reg_desc_obj.package.elements[j], &reg_elements[j],
+						pcc_subspace_id, cpu, RESOURCE_PRIORITY);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 /*
  * An example CPC table looks like the following.
  *
@@ -815,6 +900,7 @@ static void free_reg_resource(struct cpc_register_resource *cpc_reg)
 int acpi_cppc_processor_probe(struct acpi_processor *pr)
 {
 	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+	struct cpc_register_resource *pkg_elements;
 	union acpi_object *out_obj, *cpc_obj;
 	struct cpc_desc *cpc_ptr;
 	struct device *cpu_dev;
@@ -823,6 +909,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	int pcc_subspace_id = -1;
 	acpi_status status;
 	int ret = -ENODATA;
+	u32 pkg_count;
 
 	if (!osc_sb_cppc2_support_acked) {
 		pr_debug("CPPC v2 _OSC not acked\n");
@@ -904,16 +991,50 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	for (i = 2; i < num_ent; i++) {
 		cpc_obj = &out_obj->package.elements[i];
 
-		if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
+		/*
+		 * Package-type entries are used for nested structures such as
+		 * RESOURCE_PRIORITY (CPPC v4). Only RESOURCE_PRIORITY is
+		 * currently supported; any other Package entry is rejected.
+		 */
+		if (cpc_obj->type == ACPI_TYPE_PACKAGE) {
+			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_PACKAGE;
+			cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = 0;
+			cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = NULL;
+
+			pkg_count = cpc_obj->package.count;
+			if (!pkg_count) {
+				pr_debug("Empty package entry at index %d for CPU:%d\n",
+					 i, pr->id);
+				continue;
+			}
+
+			pkg_elements = kzalloc_objs(struct cpc_register_resource, pkg_count);
+			if (!pkg_elements) {
+				ret = -ENOMEM;
+				goto out_free;
+			}
+
 			/*
-			 * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a
-			 * Package of Resource Priority Register Descriptor sub-packages.
-			 * Parsing the full structure is not yet supported.
-			 * Mark the register as unsupported for now.
+			 * Assign values immediately after successful allocation to ensure that
+			 * resources can be properly released.
 			 */
-			pr_debug("CPU:%d Resource Priority not supported\n", pr->id);
-			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
-			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0;
+			cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = pkg_count;
+			cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = pkg_elements;
+
+			if (i - 2 == RESOURCE_PRIORITY) {
+				ret = parse_priority_regs(cpc_obj, pkg_elements,
+							  &pcc_subspace_id, pr->id);
+				if (ret)
+					goto out_free;
+
+				pr_debug("Parsed RESOURCE_PRIORITY (%d sub-pkgs) for CPU:%d\n",
+					 pkg_count, pr->id);
+			} else {
+				pr_debug("Unexpected ACPI_TYPE_PACKAGE at index %d for CPU:%d\n",
+					 i, pr->id);
+				ret = -ENODATA;
+				goto out_free;
+			}
 		} else {
 			ret = parse_cpc_element(cpc_obj, &cpc_ptr->cpc_regs[i-2],
 						&pcc_subspace_id, pr->id, i);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 1839582b80be..19f8a722654b 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -123,6 +123,19 @@ enum cppc_regs {
 	RESOURCE_PRIORITY,
 };
 
+/*
+ * Indices into each sub-package of the RESOURCE_PRIORITY entry.
+ * RESOURCE_PRIORITY_NUM serves as the element count / loop bound.
+ */
+enum resource_priority_regs {
+	CONTROLLED_RESOURCES,	/* Package of integer resource IDs */
+	ENABLE_VALUE,		/* Enable/disable value */
+	ENABLE_REGISTER,	/* Register for enable/disable control */
+	PRIORITY_COUNT,		/* Number of priority levels */
+	PRIORITY_REGISTER,	/* Register for priority setting */
+	RESOURCE_PRIORITY_NUM,	/* Number of elements (sentinel) */
+};
+
 /*
  * Categorization of registers as described
  * in the ACPI v.5.1 spec.
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 5/8] ACPI: CPPC: Store optional flag in cpc_register_resource
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (3 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 6/8] ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg() Lifeng Zheng
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

The current optionality check uses a compile-time bitmask (REG_OPTIONAL)
applied at call sites via IS_OPTIONAL_CPC_REG(reg_idx).  This requires
every caller to know the register index, which will not work for registers
accessed without a fixed index (e.g. entries inside Resource Priority
sub-packages).

Add a boolean 'optional' field to cpc_register_resource so that each
register element carries its own optionality.  Populate the field during
_CPC probe (for main registers) and parse_priority_regs() (for Resource
Priority sub-packages), using the existing REG_OPTIONAL and
RES_PRIO_OPTIONAL bitmasks respectively.

Replace the IS_OPTIONAL_CPC_REG() check in cppc_get_reg_val() with a
direct test of reg->optional, and remove the IS_OPTIONAL_CPC_REG() macro.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 14 +++++++++-----
 include/acpi/cppc_acpi.h |  1 +
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index b5563d75d6c5..53c71888108d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -137,11 +137,11 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
 #define REG_OPTIONAL (0x7FC7D0)
 
 /*
- * Use the index of the register in per-cpu cpc_regs[] to check if
- * it's an optional one.
+ * Each bit indicates the optionality of the register in resource
+ * priority register descriptor with the corresponding index. 0 means
+ * mandatory and 1 means optional.
  */
-#define IS_OPTIONAL_CPC_REG(reg_idx) (REG_OPTIONAL & (1U << (reg_idx)))
-
+#define RES_PRIO_OPTIONAL (0x6)
 /*
  * Arbitrary Retries in case the remote processor is slow to respond
  * to PCC commands. Keeping it high enough to cover emulators where
@@ -844,6 +844,7 @@ static int parse_priority_regs(union acpi_object *cpc_obj,
 
 		reg_elements[0].type = ACPI_TYPE_PACKAGE;
 		reg_elements[0].cpc_entry.package.count = resources_count;
+		reg_elements[0].optional = RES_PRIO_OPTIONAL & 1U;
 
 		for (j = 0; j < reg_elements[0].cpc_entry.package.count; j++) {
 			reg_elements[0].cpc_entry.package.elements[j].type = ACPI_TYPE_INTEGER;
@@ -852,6 +853,7 @@ static int parse_priority_regs(union acpi_object *cpc_obj,
 		}
 
 		for (j = 1; j < RESOURCE_PRIORITY_NUM; j++) {
+			reg_elements[j].optional = RES_PRIO_OPTIONAL & (1U << j);
 			ret = parse_cpc_element(&reg_desc_obj.package.elements[j], &reg_elements[j],
 						pcc_subspace_id, cpu, RESOURCE_PRIORITY);
 			if (ret)
@@ -990,6 +992,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	/* Iterate through remaining entries in _CPC */
 	for (i = 2; i < num_ent; i++) {
 		cpc_obj = &out_obj->package.elements[i];
+		cpc_ptr->cpc_regs[i-2].optional = REG_OPTIONAL & (1U << (i-2));
 
 		/*
 		 * Package-type entries are used for nested structures such as
@@ -1060,6 +1063,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	 * LOWEST_FREQ and NOMINAL_FREQ regs as unsupported
 	 */
 	for (i = num_ent - 2; i < MAX_CPC_REG_ENT; i++) {
+		cpc_ptr->cpc_regs[i].optional = true;
 		cpc_ptr->cpc_regs[i].type = ACPI_TYPE_INTEGER;
 		cpc_ptr->cpc_regs[i].cpc_entry.int_value = 0;
 	}
@@ -1410,7 +1414,7 @@ static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val)
 
 	reg = &cpc_desc->cpc_regs[reg_idx];
 
-	if ((reg->type == ACPI_TYPE_INTEGER && IS_OPTIONAL_CPC_REG(reg_idx) &&
+	if ((reg->type == ACPI_TYPE_INTEGER && reg->optional &&
 	     !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER &&
 	     IS_NULL_REG(&reg->cpc_entry.reg))) {
 		pr_debug("CPC register is not supported\n");
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 19f8a722654b..c0b8d52016cd 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -80,6 +80,7 @@ struct cpc_register_resource {
 			struct cpc_register_resource *elements;
 		} package;
 	} cpc_entry;
+	bool optional;
 };
 
 /* Container to hold the CPC details for each CPU */
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 6/8] ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg()
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (4 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 5/8] ACPI: CPPC: Store optional flag in cpc_register_resource Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-22 12:41 ` [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors Lifeng Zheng
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

cppc_get_reg_val() and cppc_set_reg_val() combine two responsibilities:
looking up the per-CPU cpc_desc and then performing the actual register
read/write (including null/optional checks and PCC handling).

Split out the register I/O logic into cpc_read_reg() and cpc_write_reg()
that accept a struct cpc_register_resource pointer directly.  This allows
callers that already hold a register reference -- such as the upcoming
Resource Priority accessors -- to read or write a register without going
through the per-CPU descriptor lookup by index.

Also rename the PCC wrappers from cppc_get/set_reg_val_in_pcc() to
cpc_read/write_in_pcc() to align with the new naming convention.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 89 ++++++++++++++++++++++++++--------------
 1 file changed, 58 insertions(+), 31 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 53c71888108d..2c2b25ec3e27 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1374,7 +1374,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
 	return ret_val;
 }
 
-static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val)
+static int cpc_read_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val)
 {
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
@@ -1399,35 +1399,52 @@ static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u
 	return ret;
 }
 
-static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val)
+/**
+ * cpc_read_reg - Read value from a register element that may be Integer or Buffer.
+ * @cpu: CPU number.
+ * @reg: Pointer to the CPC register element.
+ * @val: Output value.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if null/unsupported, negative on error.
+ */
+static int cpc_read_reg(int cpu, struct cpc_register_resource *reg, u64 *val)
 {
-	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
-	struct cpc_register_resource *reg;
-
 	if (val == NULL)
 		return -EINVAL;
 
-	if (!cpc_desc) {
-		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
-		return -ENODEV;
-	}
-
-	reg = &cpc_desc->cpc_regs[reg_idx];
-
-	if ((reg->type == ACPI_TYPE_INTEGER && reg->optional &&
-	     !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER &&
-	     IS_NULL_REG(&reg->cpc_entry.reg))) {
-		pr_debug("CPC register is not supported\n");
-		return -EOPNOTSUPP;
+	if (reg->type == ACPI_TYPE_INTEGER) {
+		if (reg->optional && !reg->cpc_entry.int_value)
+			goto err_unsupported;
+	} else if (reg->type == ACPI_TYPE_BUFFER) {
+		if (IS_NULL_REG(&reg->cpc_entry.reg))
+			goto err_unsupported;
+	} else {
+		goto err_unsupported;
 	}
 
 	if (CPC_IN_PCC(reg))
-		return cppc_get_reg_val_in_pcc(cpu, reg, val);
+		return cpc_read_in_pcc(cpu, reg, val);
 
 	return cpc_read(cpu, reg, val);
+
+err_unsupported:
+	pr_debug("CPC register is not supported\n");
+	return -EOPNOTSUPP;
+}
+
+static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+
+	if (!cpc_desc) {
+		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+		return -ENODEV;
+	}
+
+	return cpc_read_reg(cpu, &cpc_desc->cpc_regs[reg_idx], val);
 }
 
-static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val)
+static int cpc_write_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val)
 {
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
@@ -1452,18 +1469,16 @@ static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u
 	return ret;
 }
 
-static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
+/**
+ * cpc_write_reg - Write a CPC register.
+ * @cpu: CPU number.
+ * @reg: Pointer to the CPC register resource.
+ * @val: Value to write.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+static int cpc_write_reg(int cpu, struct cpc_register_resource *reg, u64 val)
 {
-	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
-	struct cpc_register_resource *reg;
-
-	if (!cpc_desc) {
-		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
-		return -ENODEV;
-	}
-
-	reg = &cpc_desc->cpc_regs[reg_idx];
-
 	/* if a register is writeable, it must be a buffer and not null */
 	if ((reg->type != ACPI_TYPE_BUFFER) || IS_NULL_REG(&reg->cpc_entry.reg)) {
 		pr_debug("CPC register is not supported\n");
@@ -1471,11 +1486,23 @@ static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
 	}
 
 	if (CPC_IN_PCC(reg))
-		return cppc_set_reg_val_in_pcc(cpu, reg, val);
+		return cpc_write_in_pcc(cpu, reg, val);
 
 	return cpc_write(cpu, reg, val);
 }
 
+static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+
+	if (!cpc_desc) {
+		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+		return -ENODEV;
+	}
+
+	return cpc_write_reg(cpu, &cpc_desc->cpc_regs[reg_idx], val);
+}
+
 /**
  * cppc_get_desired_perf - Get the desired performance register value.
  * @cpunum: CPU from which to get desired performance.
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (5 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 6/8] ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg() Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-28 13:59   ` Christian Loehle
  2026-09-22 12:41 ` [PATCH v4 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs Lifeng Zheng
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority
mechanism that lets OSPM control relative priority among processors for
shared resources such as processor boost, throttle, L2/L3 cache, and
memory bandwidth.

Add the following exported APIs for user-space and driver consumers:

  - cppc_get_resource_priority_count() -- number of sub-packages
  - cppc_get_resource_priority_resources() -- resource type IDs
  - cppc_get/set_res_priority_enable() -- enable/disable a group
  - cppc_get_res_priority_count() -- priority levels in a group
  - cppc_get/set_res_priority() -- read/write the priority value

These wrappers navigate the nested Package structure parsed earlier by
parse_priority_regs() and delegate register I/O to cpc_read_reg() /
cpc_write_reg().

Provide stubs returning -EOPNOTSUPP for the !CONFIG_ACPI_CPPC_LIB case.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/acpi/cppc_acpi.c | 264 +++++++++++++++++++++++++++++++++++++++
 include/acpi/cppc_acpi.h |  37 ++++++
 2 files changed, 301 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 2c2b25ec3e27..f90d727798e8 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -2284,6 +2284,270 @@ int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
 
+/**
+ * get_res_prio_subpkg - Get pointer to the elements array of a RESOURCE_PRIORITY sub-package.
+ * @cpu: CPU number.
+ * @index: Sub-package index (0 to count-1).
+ *
+ * Return: Pointer to the sub-package's elements array, or NULL on error.
+ *
+ * The layout within each sub-package element is:
+ *   elements[CONTROLLED_RESOURCES] = [0]
+ *   elements[ENABLE_VALUE]         = [1]
+ *   elements[ENABLE_REGISTER]      = [2]
+ *   elements[PRIORITY_COUNT]       = [3]
+ *   elements[PRIORITY_REGISTER]    = [4]
+ */
+static struct cpc_register_resource *get_res_prio_subpkg(int cpu, int index)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cpc_register_resource *rp_pkg;
+
+	if (!cpc_desc)
+		return NULL;
+
+	rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY];
+	if (rp_pkg->type != ACPI_TYPE_PACKAGE)
+		return NULL;
+
+	if (index < 0 || index >= rp_pkg->cpc_entry.package.count)
+		return NULL;
+
+	return rp_pkg->cpc_entry.package.elements[index].cpc_entry.package.elements;
+}
+
+/**
+ * cppc_get_resource_priority_count - Get number of Resource Priority sub-packages.
+ * @cpu: CPU number.
+ * @count: Output number of resource priority groups.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if RESOURCE_PRIORITY not provided by firmware.
+ */
+int cppc_get_resource_priority_count(int cpu, int *count)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cpc_register_resource *rp_pkg;
+
+	if (!count)
+		return -EINVAL;
+
+	if (!cpc_desc)
+		return -ENODEV;
+
+	rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY];
+	if (rp_pkg->type != ACPI_TYPE_PACKAGE)
+		return -EOPNOTSUPP;
+
+	*count = rp_pkg->cpc_entry.package.count;
+	if (*count <= 0)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_resource_priority_count);
+
+/**
+ * cppc_get_resource_priority_resources - Read Controlled Resources list for a sub-package.
+ * @cpu: CPU number.
+ * @index: Sub-package index (0 to count-1).
+ * @resources: Output array of resource type IDs (caller-allocated).
+ * @num_resources: Input = array capacity, output = actual count.
+ *
+ * Return: 0 on success, -EOPNOTSUPP, -EINVAL, etc.
+ */
+int cppc_get_resource_priority_resources(int cpu, int index,
+					 u32 *resources, int *num_resources)
+{
+	struct cpc_register_resource *elems;
+	struct cpc_register_resource *cr_pkg;
+	int i, cr_count;
+
+	if (!resources || !num_resources || *num_resources <= 0)
+		return -EINVAL;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	cr_pkg = &elems[CONTROLLED_RESOURCES];
+	if (cr_pkg->type != ACPI_TYPE_PACKAGE)
+		return -EOPNOTSUPP;
+
+	cr_count = cr_pkg->cpc_entry.package.count;
+	if (cr_count <= 0) {
+		*num_resources = 0;
+		return 0;
+	}
+
+	*num_resources = min(cr_count, *num_resources);
+
+	for (i = 0; i < *num_resources; i++)
+		resources[i] = cr_pkg->cpc_entry.package.elements[i].cpc_entry.int_value;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_resource_priority_resources);
+
+/**
+ * cppc_get_res_priority_enable - Read enable state of a Resource Priority register.
+ * @cpu: CPU number.
+ * @index: Sub-package index.
+ * @enable: Output true if enabled, false if disabled.
+ *
+ * Compares the current ENABLE_REGISTER value against ENABLE_VALUE.
+ * If ENABLE_REGISTER is null/unsupported, returns -EOPNOTSUPP.
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int cppc_get_res_priority_enable(int cpu, int index, bool *enable)
+{
+	struct cpc_register_resource *elems;
+	struct cpc_register_resource *enable_reg;
+	u64 reg_val, enable_val;
+	int ret;
+
+	if (!enable)
+		return -EINVAL;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	enable_reg = &elems[ENABLE_REGISTER];
+	if (enable_reg->type != ACPI_TYPE_BUFFER ||
+	    IS_NULL_REG(&enable_reg->cpc_entry.reg))
+		return -EOPNOTSUPP;
+
+	ret = cpc_read_reg(cpu, enable_reg, &reg_val);
+	if (ret)
+		return ret;
+
+	ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &enable_val);
+	if (ret)
+		return ret;
+
+	*enable = (reg_val == enable_val);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cppc_get_res_priority_enable);
+
+/**
+ * cppc_set_res_priority_enable - Set enable state of a Resource Priority register.
+ * @cpu: CPU number.
+ * @index: Sub-package index.
+ * @enable: true to enable (write ENABLE_VALUE), false to disable (write 0).
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int cppc_set_res_priority_enable(int cpu, int index, bool enable)
+{
+	struct cpc_register_resource *elems;
+	struct cpc_register_resource *enable_reg;
+	u64 val;
+	int ret;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	enable_reg = &elems[ENABLE_REGISTER];
+
+	if (enable) {
+		ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &val);
+		if (ret)
+			return ret;
+	} else {
+		val = 0;
+	}
+
+	return cpc_write_reg(cpu, enable_reg, val);
+}
+EXPORT_SYMBOL_GPL(cppc_set_res_priority_enable);
+
+/**
+ * cppc_get_res_priority_count - Read priority count for a Resource Priority register.
+ * @cpu: CPU number.
+ * @index: Sub-package index.
+ * @count: Output priority count (>= 2 per spec).
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int cppc_get_res_priority_count(int cpu, int index, u64 *count)
+{
+	struct cpc_register_resource *elems;
+
+	if (!count)
+		return -EINVAL;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	return cpc_read_reg(cpu, &elems[PRIORITY_COUNT], count);
+}
+EXPORT_SYMBOL_GPL(cppc_get_res_priority_count);
+
+/**
+ * cppc_get_res_priority - Read priority value for a Resource Priority register.
+ * @cpu: CPU number.
+ * @index: Sub-package index.
+ * @priority: Output priority value.
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int cppc_get_res_priority(int cpu, int index, u64 *priority)
+{
+	struct cpc_register_resource *elems;
+	struct cpc_register_resource *prio_reg;
+
+	if (!priority)
+		return -EINVAL;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	prio_reg = &elems[PRIORITY_REGISTER];
+	if (prio_reg->type != ACPI_TYPE_BUFFER ||
+	    IS_NULL_REG(&prio_reg->cpc_entry.reg))
+		return -EOPNOTSUPP;
+
+	return cpc_read_reg(cpu, prio_reg, priority);
+}
+EXPORT_SYMBOL_GPL(cppc_get_res_priority);
+
+/**
+ * cppc_set_res_priority - Write priority value for a Resource Priority register.
+ * @cpu: CPU number.
+ * @index: Sub-package index.
+ * @priority: Priority value to write (valid range: [0, PriorityCount - 1]).
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int cppc_set_res_priority(int cpu, int index, u64 priority)
+{
+	struct cpc_register_resource *elems;
+	struct cpc_register_resource *prio_reg;
+	u64 prio_count;
+	int ret;
+
+	elems = get_res_prio_subpkg(cpu, index);
+	if (!elems)
+		return -EOPNOTSUPP;
+
+	ret = cpc_read_reg(cpu, &elems[PRIORITY_COUNT], &prio_count);
+	if (ret)
+		return ret;
+
+	if (priority >= prio_count)
+		return -EINVAL;
+
+	prio_reg = &elems[PRIORITY_REGISTER];
+
+	return cpc_write_reg(cpu, prio_reg, priority);
+}
+EXPORT_SYMBOL_GPL(cppc_set_res_priority);
+
 /**
  * cppc_get_transition_latency - returns frequency transition latency in ns
  * @cpu_num: CPU number for per_cpu().
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index c0b8d52016cd..86a4764c504d 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -208,6 +208,14 @@ extern int cppc_get_auto_sel(int cpu, bool *enable);
 extern int cppc_set_auto_sel(int cpu, bool enable);
 extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
 extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
+extern int cppc_get_resource_priority_count(int cpu, int *count);
+extern int cppc_get_resource_priority_resources(int cpu, int index,
+						u32 *resources, int *num_resources);
+extern int cppc_get_res_priority_enable(int cpu, int index, bool *enable);
+extern int cppc_set_res_priority_enable(int cpu, int index, bool enable);
+extern int cppc_get_res_priority_count(int cpu, int index, u64 *count);
+extern int cppc_get_res_priority(int cpu, int index, u64 *priority);
+extern int cppc_set_res_priority(int cpu, int index, u64 priority);
 extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
 extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
 extern int amd_detect_prefcore(bool *detected);
@@ -312,6 +320,35 @@ static inline int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_resource_priority_count(int cpu, int *count)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_resource_priority_resources(int cpu, int index,
+						       u32 *resources, int *num_resources)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_res_priority_enable(int cpu, int index, bool *enable)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_res_priority_enable(int cpu, int index, bool enable)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_res_priority_count(int cpu, int index, u64 *count)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_res_priority(int cpu, int index, u64 *priority)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_res_priority(int cpu, int index, u64 priority)
+{
+	return -EOPNOTSUPP;
+}
 static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
 {
 	return -ENODEV;
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (6 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors Lifeng Zheng
@ 2026-09-22 12:41 ` Lifeng Zheng
  2026-09-25 20:23 ` [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Rafael J. Wysocki (Intel)
  2026-09-28 13:51 ` Christian Loehle
  9 siblings, 0 replies; 13+ messages in thread
From: Lifeng Zheng @ 2026-09-22 12:41 UTC (permalink / raw)
  To: rafael, viresh.kumar, saket.dumbre, lenb, ionela.voinescu,
	zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye, zhenglifeng1

Add a "resource_priority" kobject directory under each cpufreq policy
that has CPPC v4 Resource Priority entries.  Inside it, create a
sub-directory per resource priority group (indexed 0..N-1) with the
following sysfs attributes:

  controlled_resources  (RO) -- resource type IDs as human-readable names
  enable                (RW) -- enable/disable the priority group
  priority_count        (RO) -- number of priority levels
  priority              (RW) -- current priority value

Create the sysfs hierarchy in cppc_cpufreq_cpu_init() and tear it down
in cppc_cpufreq_cpu_exit().  A void *res_prio_data pointer in
cppc_cpudata tracks the allocation.

These attributes allow administrators and power-management daemons to
inspect and tune Resource Priority settings at runtime without
requiring platform-specific tools.

Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++++++++++++++++++++++++-
 include/acpi/cppc_acpi.h       |   1 +
 2 files changed, 280 insertions(+), 2 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index f6cea0c54dd9..497c10e806ac 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -640,12 +640,15 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
 	policy->driver_data = NULL;
 }
 
+static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy);
+static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy);
+
 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	unsigned int cpu = policy->cpu;
 	struct cppc_cpudata *cpu_data;
 	struct cppc_perf_caps *caps;
-	int ret;
+	int ret, retval;
 
 	cpu_data = cppc_cpufreq_get_cpu_data(cpu);
 	if (!cpu_data) {
@@ -715,7 +718,19 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	}
 
 	cppc_cpufreq_cpu_fie_init(policy);
-	return 0;
+
+	ret = cppc_create_res_prio_sysfs(policy);
+	if (!ret)
+		return 0;
+
+	cppc_cpufreq_cpu_fie_exit(policy);
+
+	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
+
+	retval = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+	if (retval)
+		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
+			 caps->lowest_perf, cpu, retval);
 
 out:
 	cppc_cpufreq_put_cpu_data(policy);
@@ -729,6 +744,7 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 	unsigned int cpu = policy->cpu;
 	int ret;
 
+	cppc_remove_res_prio_sysfs(policy);
 	cppc_cpufreq_cpu_fie_exit(policy);
 
 	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
@@ -1026,6 +1042,267 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
 	NULL,
 };
 
+/* ====== Resource Priority sysfs interface (CPPC v4) ====== */
+
+struct cppc_res_prio_group {
+	struct kobject kobj;
+	int index;
+	unsigned int cpu;
+};
+
+struct cppc_res_prio_data {
+	struct kobject kobj;
+	int num_groups;
+	struct cppc_res_prio_group groups[];
+};
+
+static const char * const resource_type_names[] = {
+	NULL,
+	"processor_boost",
+	"processor_throttle",
+	"l2_cache",
+	"l3_cache",
+	"memory_bandwidth",
+};
+
+#define RESOURCE_TYPE_MAX 5
+
+static const char *resource_type_to_name(u32 id)
+{
+	if (id >= 1 && id <= RESOURCE_TYPE_MAX)
+		return resource_type_names[id];
+	return NULL;
+}
+
+static ssize_t controlled_resources_show(struct kobject *kobj,
+					 struct kobj_attribute *attr, char *buf)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	u32 resources[32];
+	int num = ARRAY_SIZE(resources);
+	int ret, i, pos = 0;
+
+	ret = cppc_get_resource_priority_resources(grp->cpu, grp->index,
+						   resources, &num);
+	if (ret)
+		return ret;
+
+	if (num == 0)
+		return sysfs_emit(buf, "\n");
+
+	for (i = 0; i < num; i++) {
+		const char *name = resource_type_to_name(resources[i]);
+
+		if (name)
+			pos += sysfs_emit_at(buf, pos, "%s", name);
+		else
+			pos += sysfs_emit_at(buf, pos, "unknown(0x%02x)", resources[i]);
+
+		if (i < num - 1)
+			pos += sysfs_emit_at(buf, pos, " ");
+	}
+
+	pos += sysfs_emit_at(buf, pos, "\n");
+	return pos;
+}
+
+static ssize_t enable_show(struct kobject *kobj,
+			   struct kobj_attribute *attr, char *buf)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	bool val;
+	int ret;
+
+	ret = cppc_get_res_priority_enable(grp->cpu, grp->index, &val);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t enable_store(struct kobject *kobj,
+			    struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	bool val;
+	int ret;
+
+	ret = kstrtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	ret = cppc_set_res_priority_enable(grp->cpu, grp->index, val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t priority_count_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	u64 val;
+	int ret;
+
+	ret = cppc_get_res_priority_count(grp->cpu, grp->index, &val);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t priority_show(struct kobject *kobj,
+			     struct kobj_attribute *attr, char *buf)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	u64 val;
+	int ret;
+
+	ret = cppc_get_res_priority(grp->cpu, grp->index, &val);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%llu\n", val);
+}
+
+static ssize_t priority_store(struct kobject *kobj,
+			      struct kobj_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct cppc_res_prio_group *grp =
+		container_of(kobj, struct cppc_res_prio_group, kobj);
+	u64 val;
+	int ret;
+
+	ret = kstrtou64(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = cppc_set_res_priority(grp->cpu, grp->index, val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static struct kobj_attribute attr_controlled_resources =
+	__ATTR(controlled_resources, 0444, controlled_resources_show, NULL);
+static struct kobj_attribute attr_enable =
+	__ATTR(enable, 0644, enable_show, enable_store);
+static struct kobj_attribute attr_priority_count =
+	__ATTR(priority_count, 0444, priority_count_show, NULL);
+static struct kobj_attribute attr_priority =
+	__ATTR(priority, 0644, priority_show, priority_store);
+
+static struct attribute *res_prio_group_attrs[] = {
+	&attr_controlled_resources.attr,
+	&attr_enable.attr,
+	&attr_priority_count.attr,
+	&attr_priority.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(res_prio_group);
+
+static void cppc_res_prio_group_release(struct kobject *kobj)
+{
+	/*
+	 * cppc_res_prio_group is embedded in the flexible array of
+	 * cppc_res_prio_data and freed when the parent is released.
+	 */
+}
+
+static const struct kobj_type cppc_res_prio_group_ktype = {
+	.release = cppc_res_prio_group_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = res_prio_group_groups,
+};
+
+static void cppc_res_prio_release(struct kobject *kobj)
+{
+	struct cppc_res_prio_data *data =
+		container_of(kobj, struct cppc_res_prio_data, kobj);
+
+	kfree(data);
+}
+
+static const struct kobj_type cppc_res_prio_ktype = {
+	.release = cppc_res_prio_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_res_prio_data *data = cpu_data->res_prio_data;
+	int i;
+
+	if (!data)
+		return;
+
+	for (i = 0; i < data->num_groups; i++)
+		kobject_put(&data->groups[i].kobj);
+
+	kobject_put(&data->kobj);
+	cpu_data->res_prio_data = NULL;
+}
+
+static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_res_prio_data *data;
+	int num, i, ret;
+
+	ret = cppc_get_resource_priority_count(policy->cpu, &num);
+	if (ret || num <= 0)
+		return 0;
+
+	data = kzalloc(struct_size(data, groups, num), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->num_groups = num;
+
+	ret = kobject_init_and_add(&data->kobj, &cppc_res_prio_ktype,
+				   &policy->kobj, "resource_priority");
+	if (ret) {
+		kobject_put(&data->kobj);
+		return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		data->groups[i].index = i;
+		data->groups[i].cpu = policy->cpu;
+
+		ret = kobject_init_and_add(&data->groups[i].kobj,
+					   &cppc_res_prio_group_ktype,
+					   &data->kobj, "%d", i);
+		if (ret) {
+			for (; i >= 0; i--)
+				kobject_put(&data->groups[i].kobj);
+			kobject_put(&data->kobj);
+			return ret;
+		}
+	}
+
+	cpu_data->res_prio_data = data;
+	return 0;
+}
+
 static struct cpufreq_driver cppc_cpufreq_driver = {
 	.flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS,
 	.verify = cppc_verify_policy,
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 86a4764c504d..481fca627389 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -175,6 +175,7 @@ struct cppc_cpudata {
 	struct cppc_perf_fb_ctrs perf_fb_ctrs;
 	unsigned int shared_type;
 	cpumask_var_t shared_cpu_map;
+	void *res_prio_data;
 };
 
 #ifdef CONFIG_ACPI_CPPC_LIB
-- 
2.33.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (7 preceding siblings ...)
  2026-09-22 12:41 ` [PATCH v4 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs Lifeng Zheng
@ 2026-09-25 20:23 ` Rafael J. Wysocki (Intel)
  2026-09-28 13:51 ` Christian Loehle
  9 siblings, 0 replies; 13+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-09-25 20:23 UTC (permalink / raw)
  To: Lifeng Zheng, Christian Loehle
  Cc: viresh.kumar, ionela.voinescu, zhanjie9, pierre.gondois, sumitg,
	linux-acpi, linux-kernel, linux-pm, linuxarm, yubowen8,
	zhangpengjie2, wangzhi12, linhongye

+Christian

Christian, can you have a look at this, please?

On Tue, Sep 22, 2026 at 2:43 PM Lifeng Zheng <zhenglifeng1@huawei.com> wrote:
>
> This series implements support for the CPPC v4 Resource Priority Register
> mechanism defined in ACPI 6.6, Section 8.4.6.1.2.7, and exposes it to
> userspace via sysfs.
>
> Resource Priority allows OSPM to control the relative priority among
> processors for shared resources. OSPM can utilize these sysfs interfaces
> to configure resource priorities, allowing resources to be preferentially
> allocated to more important tasks.
>
> The controlled resource types include processor boost, throttle, L2 cache,
> L3 cache, and memory bandwidth.  Each Resource Priority group consists of:
>
>   - CONTROLLED_RESOURCES: which resource types the group affects
>   - ENABLE_VALUE / ENABLE_REGISTER: enable/disable the group
>   - PRIORITY_COUNT / PRIORITY_REGISTER: read/write the priority level
>
> The patch series is organized in three parts:
>
>   - patches 1-4 extend the CPPC data structures and _CPC parser to handle
>     Package-type entries and parse Resource Priority sub-packages into
>     structured descriptors.
>
>   - patches 5-7 refactor existing register I/O helpers to support direct
>     register access and add accessor functions for Resource Priority
>     attributes (enable, priority count, priority value).
>
>   - patch 8 creates a "resource_priority" sysfs hierarchy under each
>     cpufreq policy to expose the Resource Priority attributes to userspace.
>
> Changelog:
> v4:
>  - Patch 7: Set *num_resources to 0 when controlled resources number
>    invalid in cppc_get_resource_priority_resources(), therefore, the caller
>    will not read the unassigned `resources` pointer.
>  - Patch 8: Remove kfree() from the error path of
>    cppc_create_res_prio_sysfs() because cppc_res_prio_release() will do it.
>
> v3:
>  - Patch 2: Add the (i - 2) == RESOURCE_PRIORITY condition back to ensure
>    the new code behaves the same as the original.
>  - Patch 4: Assign -ENODATA to ret when an unexpected ACPI_TYPE_PACKAGE
>    appears.
>  - Patch 5: Initialize the `optional` field of the remaining cpc_regs to
>    true so that they are treated as unsupported.
>  - Patch 8: Call cppc_cpufreq_cpu_fie_exit() and set perf to lowest_perf
>    when cppc_create_res_prio_sysfs() fails. Call kobject_put() and kfree()
>    when kobject_init_and_add() fails in cppc_create_res_prio_sysfs().
>  - Link: https://lore.kernel.org/all/20260812015217.74598-1-zhenglifeng1@huawei.com/
>
> v2:
>  - Patch 1: Revert changes to the CPC_SUPPORTED() macro.
>  - Link: https://lore.kernel.org/all/20260804085042.4118193-1-zhenglifeng1@huawei.com/
>
> v1:
>  - Link: https://lore.kernel.org/all/20260717024502.3520445-1-zhenglifeng1@huawei.com/
>
> ---
>
> RFC: sysfs placement
>
> The sysfs interface is currently placed under /sys/devices/system/cpu/cpufreq/
> (i.e. the cpufreq policy directory). I am unsure whether this is the best
> location and would appreciate reviewer feedback.
>
> The concern is that Resource Priority covers resource types beyond CPU
> frequency control:
>
>   - PROCESSOR_BOOST, PROCESSOR_THROTTLE -- clearly CPU-frequency related
>   - L2_CACHE, L3_CACHE -- cache partitioning, not frequency
>   - MEMORY_BANDWIDTH -- memory QoS, not frequency
>
> Placing the attributes under cpufreq makes sense for boost/throttle but
> feels semantically wrong for cache and memory bandwidth resources.
>
> I would appreciate feedback on whether cpufreq is the right home for
> this interface, or whether a different location would be more appropriate.
>
> Lifeng Zheng (8):
>   ACPI: CPPC: Prepare cpc_register_resource for Package-type entries
>   ACPI: CPPC: Refactor element parsing into parse_cpc_element()
>   ACPI: CPPC: Refactor resource cleanup into free_reg_resource()
>   ACPI: CPPC: Parse Resource Priority Register entries from _CPC package
>   ACPI: CPPC: Store optional flag in cpc_register_resource
>   ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg()
>   ACPI: CPPC: Add Resource Priority accessors
>   cpufreq: cppc: Expose Resource Priority attributes via sysfs
>
>  drivers/acpi/cppc_acpi.c       | 719 +++++++++++++++++++++++++++------
>  drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++++-
>  include/acpi/cppc_acpi.h       |  60 +++
>  3 files changed, 930 insertions(+), 130 deletions(-)
>
> --
> 2.33.0
>
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface
  2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
                   ` (8 preceding siblings ...)
  2026-09-25 20:23 ` [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Rafael J. Wysocki (Intel)
@ 2026-09-28 13:51 ` Christian Loehle
  9 siblings, 0 replies; 13+ messages in thread
From: Christian Loehle @ 2026-09-28 13:51 UTC (permalink / raw)
  To: Lifeng Zheng, rafael, viresh.kumar, saket.dumbre, lenb,
	ionela.voinescu, zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye

On 9/22/26 13:41, Lifeng Zheng wrote:
> This series implements support for the CPPC v4 Resource Priority Register
> mechanism defined in ACPI 6.6, Section 8.4.6.1.2.7, and exposes it to
> userspace via sysfs.
> 
> Resource Priority allows OSPM to control the relative priority among
> processors for shared resources. OSPM can utilize these sysfs interfaces
> to configure resource priorities, allowing resources to be preferentially
> allocated to more important tasks.
> 
> The controlled resource types include processor boost, throttle, L2 cache,
> L3 cache, and memory bandwidth.  Each Resource Priority group consists of:
> >   - CONTROLLED_RESOURCES: which resource types the group affects
>   - ENABLE_VALUE / ENABLE_REGISTER: enable/disable the group
>   - PRIORITY_COUNT / PRIORITY_REGISTER: read/write the priority level
> 
> The patch series is organized in three parts:
> 
>   - patches 1-4 extend the CPPC data structures and _CPC parser to handle
>     Package-type entries and parse Resource Priority sub-packages into
>     structured descriptors.
> 
>   - patches 5-7 refactor existing register I/O helpers to support direct
>     register access and add accessor functions for Resource Priority
>     attributes (enable, priority count, priority value).
> 
>   - patch 8 creates a "resource_priority" sysfs hierarchy under each
>     cpufreq policy to expose the Resource Priority attributes to userspace.
> 
> Changelog:
> v4:
>  - Patch 7: Set *num_resources to 0 when controlled resources number
>    invalid in cppc_get_resource_priority_resources(), therefore, the caller
>    will not read the unassigned `resources` pointer.
>  - Patch 8: Remove kfree() from the error path of
>    cppc_create_res_prio_sysfs() because cppc_res_prio_release() will do it.
> 
> v3:
>  - Patch 2: Add the (i - 2) == RESOURCE_PRIORITY condition back to ensure
>    the new code behaves the same as the original.
>  - Patch 4: Assign -ENODATA to ret when an unexpected ACPI_TYPE_PACKAGE
>    appears.
>  - Patch 5: Initialize the `optional` field of the remaining cpc_regs to
>    true so that they are treated as unsupported.
>  - Patch 8: Call cppc_cpufreq_cpu_fie_exit() and set perf to lowest_perf
>    when cppc_create_res_prio_sysfs() fails. Call kobject_put() and kfree()
>    when kobject_init_and_add() fails in cppc_create_res_prio_sysfs().
>  - Link: https://lore.kernel.org/all/20260812015217.74598-1-zhenglifeng1@huawei.com/
> 
> v2:
>  - Patch 1: Revert changes to the CPC_SUPPORTED() macro.
>  - Link: https://lore.kernel.org/all/20260804085042.4118193-1-zhenglifeng1@huawei.com/
> 
> v1:
>  - Link: https://lore.kernel.org/all/20260717024502.3520445-1-zhenglifeng1@huawei.com/
> 
> ---
> 
> RFC: sysfs placement
> 
> The sysfs interface is currently placed under /sys/devices/system/cpu/cpufreq/
> (i.e. the cpufreq policy directory). I am unsure whether this is the best
> location and would appreciate reviewer feedback.
> 
> The concern is that Resource Priority covers resource types beyond CPU
> frequency control:
> 
>   - PROCESSOR_BOOST, PROCESSOR_THROTTLE -- clearly CPU-frequency related
>   - L2_CACHE, L3_CACHE -- cache partitioning, not frequency
>   - MEMORY_BANDWIDTH -- memory QoS, not frequency
> 
> Placing the attributes under cpufreq makes sense for boost/throttle but
> feels semantically wrong for cache and memory bandwidth resources.
> 
> I would appreciate feedback on whether cpufreq is the right home for
> this interface, or whether a different location would be more appropriate.

Another issue is regarding adjacent interfaces (Intel-RDT, AMD-QoS, Arm-MPAM,
RISCV-CBQR for memory and cache. I'm hoping no sane platform advertises both,
but should the kernel sanitize these?

Additionally what if we ever do want to be more 'clever' in the kernel about
aggregating these hints of "boost-worthiness" of tasks/CPUs, do we just disable
the low level sysfs controls here then?

> 
> Lifeng Zheng (8):
>   ACPI: CPPC: Prepare cpc_register_resource for Package-type entries
>   ACPI: CPPC: Refactor element parsing into parse_cpc_element()
>   ACPI: CPPC: Refactor resource cleanup into free_reg_resource()
>   ACPI: CPPC: Parse Resource Priority Register entries from _CPC package
>   ACPI: CPPC: Store optional flag in cpc_register_resource
>   ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg()
>   ACPI: CPPC: Add Resource Priority accessors
>   cpufreq: cppc: Expose Resource Priority attributes via sysfs
> 
>  drivers/acpi/cppc_acpi.c       | 719 +++++++++++++++++++++++++++------
>  drivers/cpufreq/cppc_cpufreq.c | 281 ++++++++++++-
>  include/acpi/cppc_acpi.h       |  60 +++
>  3 files changed, 930 insertions(+), 130 deletions(-)
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors
  2026-09-22 12:41 ` [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors Lifeng Zheng
@ 2026-09-28 13:59   ` Christian Loehle
  0 siblings, 0 replies; 13+ messages in thread
From: Christian Loehle @ 2026-09-28 13:59 UTC (permalink / raw)
  To: Lifeng Zheng, rafael, viresh.kumar, saket.dumbre, lenb,
	ionela.voinescu, zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye

On 9/22/26 13:41, Lifeng Zheng wrote:
> CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority
> mechanism that lets OSPM control relative priority among processors for
> shared resources such as processor boost, throttle, L2/L3 cache, and
> memory bandwidth.
> 
> Add the following exported APIs for user-space and driver consumers:
> 
>   - cppc_get_resource_priority_count() -- number of sub-packages
>   - cppc_get_resource_priority_resources() -- resource type IDs
>   - cppc_get/set_res_priority_enable() -- enable/disable a group
>   - cppc_get_res_priority_count() -- priority levels in a group
>   - cppc_get/set_res_priority() -- read/write the priority value

Priority Register is defined as Write, not R/W.

> 
> These wrappers navigate the nested Package structure parsed earlier by
> parse_priority_regs() and delegate register I/O to cpc_read_reg() /
> cpc_write_reg().
> 
> Provide stubs returning -EOPNOTSUPP for the !CONFIG_ACPI_CPPC_LIB case.
> 
> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
> ---
>  drivers/acpi/cppc_acpi.c | 264 +++++++++++++++++++++++++++++++++++++++
>  include/acpi/cppc_acpi.h |  37 ++++++
>  2 files changed, 301 insertions(+)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 2c2b25ec3e27..f90d727798e8 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -2284,6 +2284,270 @@ int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
>  }
>  EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
>  
> +/**
> + * get_res_prio_subpkg - Get pointer to the elements array of a RESOURCE_PRIORITY sub-package.
> + * @cpu: CPU number.
> + * @index: Sub-package index (0 to count-1).
> + *
> + * Return: Pointer to the sub-package's elements array, or NULL on error.
> + *
> + * The layout within each sub-package element is:
> + *   elements[CONTROLLED_RESOURCES] = [0]
> + *   elements[ENABLE_VALUE]         = [1]
> + *   elements[ENABLE_REGISTER]      = [2]
> + *   elements[PRIORITY_COUNT]       = [3]
> + *   elements[PRIORITY_REGISTER]    = [4]
> + */
> +static struct cpc_register_resource *get_res_prio_subpkg(int cpu, int index)
> +{
> +	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> +	struct cpc_register_resource *rp_pkg;
> +
> +	if (!cpc_desc)
> +		return NULL;
> +
> +	rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY];
> +	if (rp_pkg->type != ACPI_TYPE_PACKAGE)
> +		return NULL;
> +
> +	if (index < 0 || index >= rp_pkg->cpc_entry.package.count)
> +		return NULL;
> +
> +	return rp_pkg->cpc_entry.package.elements[index].cpc_entry.package.elements;
> +}
> +
> +/**
> + * cppc_get_resource_priority_count - Get number of Resource Priority sub-packages.
> + * @cpu: CPU number.
> + * @count: Output number of resource priority groups.
> + *
> + * Return: 0 on success, -EOPNOTSUPP if RESOURCE_PRIORITY not provided by firmware.
> + */
> +int cppc_get_resource_priority_count(int cpu, int *count)
> +{
> +	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> +	struct cpc_register_resource *rp_pkg;
> +
> +	if (!count)
> +		return -EINVAL;
> +
> +	if (!cpc_desc)
> +		return -ENODEV;
> +
> +	rp_pkg = &cpc_desc->cpc_regs[RESOURCE_PRIORITY];
> +	if (rp_pkg->type != ACPI_TYPE_PACKAGE)
> +		return -EOPNOTSUPP;
> +
> +	*count = rp_pkg->cpc_entry.package.count;
> +	if (*count <= 0)
> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_resource_priority_count);
> +
> +/**
> + * cppc_get_resource_priority_resources - Read Controlled Resources list for a sub-package.
> + * @cpu: CPU number.
> + * @index: Sub-package index (0 to count-1).
> + * @resources: Output array of resource type IDs (caller-allocated).
> + * @num_resources: Input = array capacity, output = actual count.
> + *
> + * Return: 0 on success, -EOPNOTSUPP, -EINVAL, etc.
> + */
> +int cppc_get_resource_priority_resources(int cpu, int index,
> +					 u32 *resources, int *num_resources)
> +{
> +	struct cpc_register_resource *elems;
> +	struct cpc_register_resource *cr_pkg;
> +	int i, cr_count;
> +
> +	if (!resources || !num_resources || *num_resources <= 0)
> +		return -EINVAL;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	cr_pkg = &elems[CONTROLLED_RESOURCES];
> +	if (cr_pkg->type != ACPI_TYPE_PACKAGE)
> +		return -EOPNOTSUPP;
> +
> +	cr_count = cr_pkg->cpc_entry.package.count;
> +	if (cr_count <= 0) {
> +		*num_resources = 0;
> +		return 0;
> +	}
> +
> +	*num_resources = min(cr_count, *num_resources);
> +
> +	for (i = 0; i < *num_resources; i++)
> +		resources[i] = cr_pkg->cpc_entry.package.elements[i].cpc_entry.int_value;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_resource_priority_resources);
> +
> +/**
> + * cppc_get_res_priority_enable - Read enable state of a Resource Priority register.
> + * @cpu: CPU number.
> + * @index: Sub-package index.
> + * @enable: Output true if enabled, false if disabled.
> + *
> + * Compares the current ENABLE_REGISTER value against ENABLE_VALUE.
> + * If ENABLE_REGISTER is null/unsupported, returns -EOPNOTSUPP.
> + *
> + * Return: 0 on success, negative error otherwise.
> + */
> +int cppc_get_res_priority_enable(int cpu, int index, bool *enable)
> +{
> +	struct cpc_register_resource *elems;
> +	struct cpc_register_resource *enable_reg;
> +	u64 reg_val, enable_val;
> +	int ret;
> +
> +	if (!enable)
> +		return -EINVAL;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	enable_reg = &elems[ENABLE_REGISTER];
> +	if (enable_reg->type != ACPI_TYPE_BUFFER ||
> +	    IS_NULL_REG(&enable_reg->cpc_entry.reg))
> +		return -EOPNOTSUPP;
> +
> +	ret = cpc_read_reg(cpu, enable_reg, &reg_val);
> +	if (ret)
> +		return ret;
> +
> +	ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &enable_val);
> +	if (ret)
> +		return ret;
> +
> +	*enable = (reg_val == enable_val);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_res_priority_enable);
> +
> +/**
> + * cppc_set_res_priority_enable - Set enable state of a Resource Priority register.
> + * @cpu: CPU number.
> + * @index: Sub-package index.
> + * @enable: true to enable (write ENABLE_VALUE), false to disable (write 0).
> + *
> + * Return: 0 on success, negative error otherwise.
> + */
> +int cppc_set_res_priority_enable(int cpu, int index, bool enable)
> +{
> +	struct cpc_register_resource *elems;
> +	struct cpc_register_resource *enable_reg;
> +	u64 val;
> +	int ret;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	enable_reg = &elems[ENABLE_REGISTER];
> +
> +	if (enable) {
> +		ret = cpc_read_reg(cpu, &elems[ENABLE_VALUE], &val);
> +		if (ret)
> +			return ret;
> +	} else {
> +		val = 0;
> +	}
> +
> +	return cpc_write_reg(cpu, enable_reg, val);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_res_priority_enable);
> +
> +/**
> + * cppc_get_res_priority_count - Read priority count for a Resource Priority register.
> + * @cpu: CPU number.
> + * @index: Sub-package index.
> + * @count: Output priority count (>= 2 per spec).
> + *
> + * Return: 0 on success, negative error otherwise.
> + */
> +int cppc_get_res_priority_count(int cpu, int index, u64 *count)
> +{
> +	struct cpc_register_resource *elems;
> +
> +	if (!count)
> +		return -EINVAL;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	return cpc_read_reg(cpu, &elems[PRIORITY_COUNT], count);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_res_priority_count);
> +
> +/**
> + * cppc_get_res_priority - Read priority value for a Resource Priority register.
> + * @cpu: CPU number.
> + * @index: Sub-package index.
> + * @priority: Output priority value.
> + *
> + * Return: 0 on success, negative error otherwise.
> + */
> +int cppc_get_res_priority(int cpu, int index, u64 *priority)
> +{
> +	struct cpc_register_resource *elems;
> +	struct cpc_register_resource *prio_reg;
> +
> +	if (!priority)
> +		return -EINVAL;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	prio_reg = &elems[PRIORITY_REGISTER];
> +	if (prio_reg->type != ACPI_TYPE_BUFFER ||
> +	    IS_NULL_REG(&prio_reg->cpc_entry.reg))
> +		return -EOPNOTSUPP;
> +
> +	return cpc_read_reg(cpu, prio_reg, priority);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_res_priority);
> +
> +/**
> + * cppc_set_res_priority - Write priority value for a Resource Priority register.
> + * @cpu: CPU number.
> + * @index: Sub-package index.
> + * @priority: Priority value to write (valid range: [0, PriorityCount - 1]).
> + *
> + * Return: 0 on success, negative error otherwise.
> + */
> +int cppc_set_res_priority(int cpu, int index, u64 priority)
> +{
> +	struct cpc_register_resource *elems;
> +	struct cpc_register_resource *prio_reg;
> +	u64 prio_count;
> +	int ret;
> +
> +	elems = get_res_prio_subpkg(cpu, index);
> +	if (!elems)
> +		return -EOPNOTSUPP;
> +
> +	ret = cpc_read_reg(cpu, &elems[PRIORITY_COUNT], &prio_count);
> +	if (ret)
> +		return ret;
> +
> +	if (priority >= prio_count)
> +		return -EINVAL;
> +
> +	prio_reg = &elems[PRIORITY_REGISTER];
> +
> +	return cpc_write_reg(cpu, prio_reg, priority);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_res_priority);
> +
>  /**
>   * cppc_get_transition_latency - returns frequency transition latency in ns
>   * @cpu_num: CPU number for per_cpu().
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index c0b8d52016cd..86a4764c504d 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -208,6 +208,14 @@ extern int cppc_get_auto_sel(int cpu, bool *enable);
>  extern int cppc_set_auto_sel(int cpu, bool enable);
>  extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
>  extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
> +extern int cppc_get_resource_priority_count(int cpu, int *count);
> +extern int cppc_get_resource_priority_resources(int cpu, int index,
> +						u32 *resources, int *num_resources);
> +extern int cppc_get_res_priority_enable(int cpu, int index, bool *enable);
> +extern int cppc_set_res_priority_enable(int cpu, int index, bool enable);
> +extern int cppc_get_res_priority_count(int cpu, int index, u64 *count);
> +extern int cppc_get_res_priority(int cpu, int index, u64 *priority);
> +extern int cppc_set_res_priority(int cpu, int index, u64 priority);
>  extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
>  extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
>  extern int amd_detect_prefcore(bool *detected);
> @@ -312,6 +320,35 @@ static inline int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
>  {
>  	return -EOPNOTSUPP;
>  }
> +static inline int cppc_get_resource_priority_count(int cpu, int *count)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_resource_priority_resources(int cpu, int index,
> +						       u32 *resources, int *num_resources)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_res_priority_enable(int cpu, int index, bool *enable)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_res_priority_enable(int cpu, int index, bool enable)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_res_priority_count(int cpu, int index, u64 *count)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_res_priority(int cpu, int index, u64 *priority)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_res_priority(int cpu, int index, u64 priority)
> +{
> +	return -EOPNOTSUPP;
> +}
>  static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
>  {
>  	return -ENODEV;


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package
  2026-09-22 12:41 ` [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package Lifeng Zheng
@ 2026-09-28 14:06   ` Christian Loehle
  0 siblings, 0 replies; 13+ messages in thread
From: Christian Loehle @ 2026-09-28 14:06 UTC (permalink / raw)
  To: Lifeng Zheng, rafael, viresh.kumar, saket.dumbre, lenb,
	ionela.voinescu, zhanjie9, pierre.gondois, sumitg
  Cc: linux-acpi, linux-kernel, linux-pm, acpica-devel, linuxarm,
	yubowen8, zhangpengjie2, wangzhi12, linhongye

On 9/22/26 13:41, Lifeng Zheng wrote:
> CPPC v4 (ACPI 6.6, Section 8.4.6.1.2.7) defines the Resource Priority
> entry as a Package of sub-packages, each containing:
> 
>   - CONTROLLED_RESOURCES: a Package of integer resource IDs
>   - ENABLE_VALUE / ENABLE_REGISTER: enable/disable control
>   - PRIORITY_COUNT / PRIORITY_REGISTER: priority level setting
> 
> These allow OSPM to set relative priority among processors for shared
> resources such as boost, throttle, L2/L3 cache, and memory bandwidth.
> 
> Implement parse_priority_regs() which:
> 
>   1. Validates each sub-package has the expected element count
>      (RESOURCE_PRIORITY_NUM).
>   2. Allocates cpc_register_resource arrays for the sub-package
>      elements and the nested CONTROLLED_RESOURCES list.
>   3. Parses CONTROLLED_RESOURCES as integers and the remaining
>      entries (ENABLE_REGISTER, PRIORITY_REGISTER, etc.) via
>      parse_cpc_element() so that register descriptors, PCC subspace
>      tracking, and ioremap are handled consistently.
>   4. Wires the parser into the main _CPC probe loop, replacing the
>      previous "package type not supported" stub with full parsing for
>      RESOURCE_PRIORITY while rejecting unexpected Package entries.
> 
> The probe and _exit() error/cleanup paths already use
> free_reg_resource(), which recursively frees Package-type entries,
> so no additional cleanup changes are needed.
> 
> Add the resource_priority_regs enumeration that defines the indices
> into each Resource Priority sub-package (CONTROLLED_RESOURCES,
> ENABLE_VALUE, ENABLE_REGISTER, PRIORITY_COUNT, PRIORITY_REGISTER)
> and RESOURCE_PRIORITY_NUM as the element count sentinel.
> 
> Signed-off-by: Lifeng Zheng <zhenglifeng1@huawei.com>
> ---
>  drivers/acpi/cppc_acpi.c | 137 ++++++++++++++++++++++++++++++++++++---
>  include/acpi/cppc_acpi.h |  13 ++++
>  2 files changed, 142 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index dce15d875c62..b5563d75d6c5 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -777,6 +777,91 @@ static void free_reg_resource(struct cpc_register_resource *cpc_reg)
>  	}
>  }
>  
> +/**
> + * parse_priority_regs - Parse the RESOURCE_PRIORITY nested package structure.
> + * @cpc_obj:         ACPI Package object for the RESOURCE_PRIORITY entry.
> + * @regs:            Output array of cpc_register_resource to fill.
> + * @pcc_subspace_id: In/out pointer to PCC subspace ID.
> + * @cpu:             CPU number, used for debug messages.
> + *
> + * The RESOURCE_PRIORITY entry (CPPC v4) is a Package of sub-packages.
> + * Each sub-package has RESOURCE_PRIORITY_NUM elements:
> + *   [0] = Package of integers (CONTROLLED_RESOURCES list)
> + *   [1] = ENABLE_VALUE, [2] = ENABLE_REGISTER,
> + *   [3] = PRIORITY_COUNT, [4] = PRIORITY_REGISTER
> + *
> + * Return: 0 on success, -ENODATA on malformed data, -ENOMEM on allocation failure.
> + */
> +static int parse_priority_regs(union acpi_object *cpc_obj,
> +			       struct cpc_register_resource *regs,
> +			       int *pcc_subspace_id, u32 cpu)
> +{
> +	struct cpc_register_resource *reg_elements;
> +	union acpi_object reg_desc_obj;
> +	unsigned int i, j, resources_count;
> +	int ret;
> +
> +	for (i = 0; i < cpc_obj->package.count; i++) {
> +		reg_desc_obj = cpc_obj->package.elements[i];
> +		if (reg_desc_obj.type != ACPI_TYPE_PACKAGE ||
> +		    reg_desc_obj.package.count != RESOURCE_PRIORITY_NUM) {
> +			pr_debug("Malformed priority regs sub-pkg: type %d count %d, expected %d for CPU:%d\n",
> +				 reg_desc_obj.type, reg_desc_obj.package.count,
> +				 RESOURCE_PRIORITY_NUM, cpu);
> +			return -ENODATA;
> +		}
> +
> +		reg_elements = kzalloc_objs(struct cpc_register_resource, RESOURCE_PRIORITY_NUM);
> +		if (!reg_elements) {
> +			pr_debug("Failed to allocate reg_elements for CPU:%d\n", cpu);
> +			return -ENOMEM;
> +		}
> +
> +		/*
> +		 * Assign values immediately after successful allocation to ensure that resources
> +		 * can be properly released.
> +		 */
> +		regs[i].type = ACPI_TYPE_PACKAGE;
> +		regs[i].cpc_entry.package.count = RESOURCE_PRIORITY_NUM;
> +		regs[i].cpc_entry.package.elements = reg_elements;
> +
> +		resources_count = reg_desc_obj.package.elements[0].package.count;
> +
> +		if (reg_desc_obj.package.elements[0].type != ACPI_TYPE_PACKAGE ||
> +		    !resources_count) {
> +			pr_debug("Invalid priority sub-elements: type %d count %d for CPU:%d\n",
> +				 reg_desc_obj.package.elements[0].type, resources_count, cpu);
> +			return -ENODATA;
> +		}
> +
> +		reg_elements[0].cpc_entry.package.elements =
> +			kzalloc_objs(struct cpc_register_resource, resources_count);
> +		if (!reg_elements[0].cpc_entry.package.elements) {
> +			pr_debug("Failed to allocate %d priority sub-elements for CPU:%d\n",
> +				 resources_count, cpu);
> +			return -ENOMEM;
> +		}
> +
> +		reg_elements[0].type = ACPI_TYPE_PACKAGE;
> +		reg_elements[0].cpc_entry.package.count = resources_count;
> +
> +		for (j = 0; j < reg_elements[0].cpc_entry.package.count; j++) {
> +			reg_elements[0].cpc_entry.package.elements[j].type = ACPI_TYPE_INTEGER;

ACPI_TYPE_INTEGER could be checked instead of just assumed (and the value copied) to be sure.

> +			reg_elements[0].cpc_entry.package.elements[j].cpc_entry.int_value =
> +				reg_desc_obj.package.elements[0].package.elements[j].integer.value;
> +		}
> +
> +		for (j = 1; j < RESOURCE_PRIORITY_NUM; j++) {
> +			ret = parse_cpc_element(&reg_desc_obj.package.elements[j], &reg_elements[j],
> +						pcc_subspace_id, cpu, RESOURCE_PRIORITY);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * An example CPC table looks like the following.
>   *
> @@ -815,6 +900,7 @@ static void free_reg_resource(struct cpc_register_resource *cpc_reg)
>  int acpi_cppc_processor_probe(struct acpi_processor *pr)
>  {
>  	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
> +	struct cpc_register_resource *pkg_elements;
>  	union acpi_object *out_obj, *cpc_obj;
>  	struct cpc_desc *cpc_ptr;
>  	struct device *cpu_dev;
> @@ -823,6 +909,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
>  	int pcc_subspace_id = -1;
>  	acpi_status status;
>  	int ret = -ENODATA;
> +	u32 pkg_count;
>  
>  	if (!osc_sb_cppc2_support_acked) {
>  		pr_debug("CPPC v2 _OSC not acked\n");
> @@ -904,16 +991,50 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
>  	for (i = 2; i < num_ent; i++) {
>  		cpc_obj = &out_obj->package.elements[i];
>  
> -		if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
> +		/*
> +		 * Package-type entries are used for nested structures such as
> +		 * RESOURCE_PRIORITY (CPPC v4). Only RESOURCE_PRIORITY is
> +		 * currently supported; any other Package entry is rejected.
> +		 */
> +		if (cpc_obj->type == ACPI_TYPE_PACKAGE) {
> +			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_PACKAGE;
> +			cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = 0;
> +			cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = NULL;
> +
> +			pkg_count = cpc_obj->package.count;
> +			if (!pkg_count) {
> +				pr_debug("Empty package entry at index %d for CPU:%d\n",
> +					 i, pr->id);
> +				continue;
> +			}
> +
> +			pkg_elements = kzalloc_objs(struct cpc_register_resource, pkg_count);
> +			if (!pkg_elements) {
> +				ret = -ENOMEM;
> +				goto out_free;
> +			}
> +
>  			/*
> -			 * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a
> -			 * Package of Resource Priority Register Descriptor sub-packages.
> -			 * Parsing the full structure is not yet supported.
> -			 * Mark the register as unsupported for now.
> +			 * Assign values immediately after successful allocation to ensure that
> +			 * resources can be properly released.
>  			 */
> -			pr_debug("CPU:%d Resource Priority not supported\n", pr->id);
> -			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
> -			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0;
> +			cpc_ptr->cpc_regs[i-2].cpc_entry.package.count = pkg_count;
> +			cpc_ptr->cpc_regs[i-2].cpc_entry.package.elements = pkg_elements;
> +
> +			if (i - 2 == RESOURCE_PRIORITY) {
> +				ret = parse_priority_regs(cpc_obj, pkg_elements,
> +							  &pcc_subspace_id, pr->id);
> +				if (ret)
> +					goto out_free;
> +
> +				pr_debug("Parsed RESOURCE_PRIORITY (%d sub-pkgs) for CPU:%d\n",
> +					 pkg_count, pr->id);
> +			} else {
> +				pr_debug("Unexpected ACPI_TYPE_PACKAGE at index %d for CPU:%d\n",
> +					 i, pr->id);
> +				ret = -ENODATA;
> +				goto out_free;
> +			}
>  		} else {
>  			ret = parse_cpc_element(cpc_obj, &cpc_ptr->cpc_regs[i-2],
>  						&pcc_subspace_id, pr->id, i);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 1839582b80be..19f8a722654b 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -123,6 +123,19 @@ enum cppc_regs {
>  	RESOURCE_PRIORITY,
>  };
>  
> +/*
> + * Indices into each sub-package of the RESOURCE_PRIORITY entry.
> + * RESOURCE_PRIORITY_NUM serves as the element count / loop bound.
> + */
> +enum resource_priority_regs {
> +	CONTROLLED_RESOURCES,	/* Package of integer resource IDs */
> +	ENABLE_VALUE,		/* Enable/disable value */
> +	ENABLE_REGISTER,	/* Register for enable/disable control */
> +	PRIORITY_COUNT,		/* Number of priority levels */
> +	PRIORITY_REGISTER,	/* Register for priority setting */
> +	RESOURCE_PRIORITY_NUM,	/* Number of elements (sentinel) */
> +};
> +
>  /*
>   * Categorization of registers as described
>   * in the ACPI v.5.1 spec.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-09-28 14:06 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-22 12:41 [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 1/8] ACPI: CPPC: Prepare cpc_register_resource for Package-type entries Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 2/8] ACPI: CPPC: Refactor element parsing into parse_cpc_element() Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 3/8] ACPI: CPPC: Refactor resource cleanup into free_reg_resource() Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 4/8] ACPI: CPPC: Parse Resource Priority Register entries from _CPC package Lifeng Zheng
2026-09-28 14:06   ` Christian Loehle
2026-09-22 12:41 ` [PATCH v4 5/8] ACPI: CPPC: Store optional flag in cpc_register_resource Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 6/8] ACPI: CPPC: Factor out cpc_read_reg() and cpc_write_reg() Lifeng Zheng
2026-09-22 12:41 ` [PATCH v4 7/8] ACPI: CPPC: Add Resource Priority accessors Lifeng Zheng
2026-09-28 13:59   ` Christian Loehle
2026-09-22 12:41 ` [PATCH v4 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs Lifeng Zheng
2026-09-25 20:23 ` [PATCH v4 0/8] ACPI: CPPC: Resource Priority Register support and sysfs interface Rafael J. Wysocki (Intel)
2026-09-28 13:51 ` Christian Loehle

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®