Linux Power Management development
 help / color / mirror / Atom feed
From: Lifeng Zheng <zhenglifeng1@huawei.com>
To: <rafael@kernel.org>, <viresh.kumar@linaro.org>,
	<saket.dumbre@intel.com>, <lenb@kernel.org>,
	<ionela.voinescu@arm.com>, <zhanjie9@hisilicon.com>,
	<pierre.gondois@arm.com>, <sumitg@nvidia.com>
Cc: <linux-acpi@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<linux-pm@vger.kernel.org>, <acpica-devel@lists.linux.dev>,
	<linuxarm@huawei.com>, <yubowen8@huawei.com>,
	<zhangpengjie2@huawei.com>, <wangzhi12@huawei.com>,
	<linhongye@h-partners.com>, <zhenglifeng1@huawei.com>
Subject: [PATCH v4 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs
Date: Tue, 22 Sep 2026 20:41:21 +0800	[thread overview]
Message-ID: <20260922124121.3426219-9-zhenglifeng1@huawei.com> (raw)
In-Reply-To: <20260922124121.3426219-1-zhenglifeng1@huawei.com>

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


  parent reply	other threads:[~2026-09-22 12:43 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Lifeng Zheng [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260922124121.3426219-9-zhenglifeng1@huawei.com \
    --to=zhenglifeng1@huawei.com \
    --cc=acpica-devel@lists.linux.dev \
    --cc=ionela.voinescu@arm.com \
    --cc=lenb@kernel.org \
    --cc=linhongye@h-partners.com \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linuxarm@huawei.com \
    --cc=pierre.gondois@arm.com \
    --cc=rafael@kernel.org \
    --cc=saket.dumbre@intel.com \
    --cc=sumitg@nvidia.com \
    --cc=viresh.kumar@linaro.org \
    --cc=wangzhi12@huawei.com \
    --cc=yubowen8@huawei.com \
    --cc=zhangpengjie2@huawei.com \
    --cc=zhanjie9@hisilicon.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox