Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH v8 2/2] cpufreq: Add boost_freq_req QoS request
From: Zhongqiu Han @ 2026-03-29  9:00 UTC (permalink / raw)
  To: Pierre Gondois, linux-kernel
  Cc: Lifeng Zheng, Huang Rui, Gautham R. Shenoy, Mario Limonciello,
	Perry Yuan, Rafael J. Wysocki, Viresh Kumar, linux-pm,
	zhongqiu.han
In-Reply-To: <20260326204404.1401849-3-pierre.gondois@arm.com>

On 3/27/2026 4:44 AM, Pierre Gondois wrote:
> The Power Management Quality of Service (PM QoS) allows to
> aggregate constraints from multiple entities. It is currently
> used to manage the min/max frequency of a given policy.
> 
> Frequency constraints can come for instance from:
> - Thermal framework: acpi_thermal_cpufreq_init()
> - Firmware: _PPC objects: acpi_processor_ppc_init()
> - User: by setting policyX/scaling_[min|max]_freq
> The minimum of the max frequency constraints is used to compute
> the resulting maximum allowed frequency.
> 
> When enabling boost frequencies, the same frequency request object
> (policy->max_freq_req) as to handle requests from users is used.
> As a result, when setting:
> - scaling_max_freq
> - boost
> The last sysfs file used overwrites the request from the other
> sysfs file.
> 
> To avoid this, create a per-policy boost_freq_req to save the boost
> constraints instead of overwriting the last scaling_max_freq
> constraint.
> 
> policy_set_boost() calls the cpufreq set_boost callback.
> Update the newly added boost_freq_req request from there:
> - whenever boost is toggled
> - to cover all possible paths
> 
> In the existing .set_boost() callbacks:
> - Don't update policy->max as this is done through the qos notifier
>    cpufreq_notifier_max() which calls cpufreq_set_policy().
> - Remove freq_qos_update_request() calls as the qos request is now
>    done in policy_set_boost() and updates the new boost_freq_req
> 
> $ ## Init state
> scaling_max_freq:1000000
> cpuinfo_max_freq:1000000
> 
> $ echo 700000 > scaling_max_freq
> scaling_max_freq:700000
> cpuinfo_max_freq:1000000
> 
> $ echo 1 > ../boost
> scaling_max_freq:1200000
> cpuinfo_max_freq:1200000
> 
> $ echo 800000 > scaling_max_freq
> scaling_max_freq:800000
> cpuinfo_max_freq:1200000
> 
> $ ## Final step:
> $ ## Without the patches:
> $ echo 0 > ../boost
> scaling_max_freq:1000000
> cpuinfo_max_freq:1000000
> 
> $ ## With the patches:
> $ echo 0 > ../boost
> scaling_max_freq:800000
> cpuinfo_max_freq:1000000
> 
> Note:
> cpufreq_frequency_table_cpuinfo() updates policy->min
> and max from:
> A.
> cpufreq_boost_set_sw()
> \-cpufreq_frequency_table_cpuinfo()
> B.
> cpufreq_policy_online()
> \-cpufreq_table_validate_and_sort()
>    \-cpufreq_frequency_table_cpuinfo()
> Keep these updates as some drivers expect policy->min and
> max to be set through B.
> 
> Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
> Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
> ---
>   drivers/cpufreq/amd-pstate.c   |  2 --
>   drivers/cpufreq/cppc_cpufreq.c | 10 ++------
>   drivers/cpufreq/cpufreq.c      | 46 +++++++++++++++++++++++-----------
>   include/linux/cpufreq.h        |  1 +
>   4 files changed, 34 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 5aa9fcd80cf51..d0675d6a19fe1 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -769,8 +769,6 @@ static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on)
>   	else if (policy->cpuinfo.max_freq > nominal_freq)
>   		policy->cpuinfo.max_freq = nominal_freq;
>   
> -	policy->max = policy->cpuinfo.max_freq;
> -
>   	if (cppc_state == AMD_PSTATE_PASSIVE) {
>   		ret = freq_qos_update_request(&cpudata->req[1], policy->cpuinfo.max_freq);
>   		if (ret < 0)
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 011f35cb47b94..f4f574fbe547b 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -807,17 +807,11 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
>   {
>   	struct cppc_cpudata *cpu_data = policy->driver_data;
>   	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> -	int ret;
>   
>   	if (state)
> -		policy->max = cppc_perf_to_khz(caps, caps->highest_perf);
> +		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->highest_perf);
>   	else
> -		policy->max = cppc_perf_to_khz(caps, caps->nominal_perf);
> -	policy->cpuinfo.max_freq = policy->max;
> -
> -	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
> -	if (ret < 0)
> -		return ret;
> +		policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->nominal_perf);
>   
>   	return 0;
>   }
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 5757f12633d16..d2f393d738a39 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -609,10 +609,19 @@ static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
>   	policy->boost_enabled = enable;
>   
>   	ret = cpufreq_driver->set_boost(policy, enable);
> -	if (ret)
> +	if (ret) {
>   		policy->boost_enabled = !policy->boost_enabled;
> +		return ret;
> +	}
>   
> -	return ret;
> +	ret = freq_qos_update_request(policy->boost_freq_req, policy->cpuinfo.max_freq);
> +	if (ret < 0) {
> +		policy->boost_enabled = !policy->boost_enabled;
> +		cpufreq_driver->set_boost(policy, policy->boost_enabled);
> +		return ret;
> +	}
> +
> +	return 0;
>   }
>   
>   static ssize_t store_local_boost(struct cpufreq_policy *policy,
> @@ -1377,6 +1386,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
>   	}
>   
>   	freq_qos_remove_request(policy->min_freq_req);
> +	freq_qos_remove_request(policy->boost_freq_req);
>   	kfree(policy->min_freq_req);
>   
>   	cpufreq_policy_put_kobj(policy);
> @@ -1445,26 +1455,38 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
>   	cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);
>   
>   	if (new_policy) {
> +		unsigned int count;
> +
>   		for_each_cpu(j, policy->related_cpus) {
>   			per_cpu(cpufreq_cpu_data, j) = policy;
>   			add_cpu_dev_symlink(policy, j, get_cpu_device(j));
>   		}
>   
> -		policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
> +		count = policy->boost_supported ? 3 : 2;
> +		policy->min_freq_req = kzalloc(count * sizeof(*policy->min_freq_req),
>   					       GFP_KERNEL);
>   		if (!policy->min_freq_req) {
>   			ret = -ENOMEM;
>   			goto out_destroy_policy;
>   		}
>   
> +		if (policy->boost_supported) {
> +			policy->boost_freq_req = policy->min_freq_req + 2;
> +
> +			ret = freq_qos_add_request(&policy->constraints,
> +						   policy->boost_freq_req,
> +						   FREQ_QOS_MAX,
> +						   policy->cpuinfo.max_freq);
> +			if (ret < 0) {
> +				policy->boost_freq_req = NULL;
> +				goto out_destroy_policy;
> +			}
> +		}
> +
>   		ret = freq_qos_add_request(&policy->constraints,
>   					   policy->min_freq_req, FREQ_QOS_MIN,
>   					   FREQ_QOS_MIN_DEFAULT_VALUE);
>   		if (ret < 0) {
> -			/*
> -			 * So we don't call freq_qos_remove_request() for an
> -			 * uninitialized request.
> -			 */
>   			kfree(policy->min_freq_req);
>   			policy->min_freq_req = NULL;
>   			goto out_destroy_policy;

Hi Pierre, Viresh,

Sorry for the late follow-up on v8. While re-reading the patch, I
noticed a potential UAF issue on an error path — I might be missing
something, so I'd appreciate a double-check.

min_freq_req, max_freq_req and boost_freq_req all point into the same
contiguous kzalloc'd block:

slot0 (min_freq_req + 0) -> min_freq_req
slot1 (min_freq_req + 1) -> max_freq_req
slot2 (min_freq_req + 2) -> boost_freq_req

If boost_freq_req is successfully added to the QoS constraints list, but
the subsequent freq_qos_add_request() for min_freq_req fails, the error
path does:

kfree(policy->min_freq_req); /* frees the entire block, including slot2
*/
policy->min_freq_req = NULL;
goto out_destroy_policy;

policy->boost_freq_req is not set to NULL here, so it becomes a dangling
pointer into freed memory.
cpufreq_policy_free() is then called from cpufreq_online() and does:

freq_qos_remove_request(policy->boost_freq_req); /* UAF */
or this boost qos req will leak.


If freq_qos_add_request() for min_freq_req fails, maybe we can remove
boost qos first, such as:

if (ret < 0) {
	if (policy->boost_freq_req) {
		freq_qos_remove_request(policy->boost_freq_req);
		policy->boost_freq_req = NULL;
	}
	kfree(policy->min_freq_req);
	policy->min_freq_req = NULL;
	goto out_destroy_policy;
}



Besides, if freq_qos_add_request() for boost_freq_req fails first on
cpufreq_policy_online(),  policy->min_freq_req is valid pointer but qos
req is inactive, will trigger one warn on
freq_qos_remove_request(policy->min_freq_req) {
	if (WARN(!freq_qos_request_active(req),
		"%s() called for unknown object\n", __func__))
		return -EINVAL;
}


> @@ -2788,16 +2810,10 @@ int cpufreq_boost_set_sw(struct cpufreq_policy *policy, int state)
>   		return -ENXIO;
>   
>   	ret = cpufreq_frequency_table_cpuinfo(policy);
> -	if (ret) {
> +	if (ret)
>   		pr_err("%s: Policy frequency update failed\n", __func__);
> -		return ret;
> -	}
> -
> -	ret = freq_qos_update_request(policy->max_freq_req, policy->max);
> -	if (ret < 0)
> -		return ret;
>   
> -	return 0;
> +	return ret;
>   }
>   EXPORT_SYMBOL_GPL(cpufreq_boost_set_sw);
>   
> diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> index cc894fc389710..89157e367eefa 100644
> --- a/include/linux/cpufreq.h
> +++ b/include/linux/cpufreq.h
> @@ -81,6 +81,7 @@ struct cpufreq_policy {
>   	struct freq_constraints	constraints;
>   	struct freq_qos_request	*min_freq_req;
>   	struct freq_qos_request	*max_freq_req;
> +	struct freq_qos_request *boost_freq_req;
>   
>   	struct cpufreq_frequency_table	*freq_table;
>   	enum cpufreq_table_sorting freq_table_sorted;


-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply

* Re: [PATCH v2 1/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Krzysztof Kozlowski @ 2026-03-29  9:29 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260329061523.98346-2-krishnagopi487@gmail.com>

On Sun, Mar 29, 2026 at 11:45:19AM +0530, Gopi Krishna Menon wrote:
> Convert the SPEAr Thermal Sensor bindings to DT schema.
> 
> Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
> ---
> Changes since v1:
> - Changed unevaluatedProperties to additionalProperties

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v2 2/2] ARM: dts: st: spear: rename thermal_flags to st,thermal-flags
From: Krzysztof Kozlowski @ 2026-03-29  9:31 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260329061523.98346-3-krishnagopi487@gmail.com>

On Sun, Mar 29, 2026 at 11:45:20AM +0530, Gopi Krishna Menon wrote:
> st,thermal-flags is a required property in SPEAr Thermal Sensor node,
> which is incorrectly written as thermal_flags in spear13xx.dtsi.
> 
> Rename thermal_flags to st,thermal-flags to fix the property name

Does this have an impact? If yes, then why no fixes? If no, then why
not? How this could ever worked? Maybe this is completely unnecessary.

We already talked about this and I don't get why this change is neeeded
and why we discuss the same problem.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v2 2/2] ARM: dts: st: spear: rename thermal_flags to st,thermal-flags
From: Krzysztof Kozlowski @ 2026-03-29  9:34 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260329-starfish-of-eternal-storm-f16de5@quoll>

On 29/03/2026 11:31, Krzysztof Kozlowski wrote:
> On Sun, Mar 29, 2026 at 11:45:20AM +0530, Gopi Krishna Menon wrote:
>> st,thermal-flags is a required property in SPEAr Thermal Sensor node,
>> which is incorrectly written as thermal_flags in spear13xx.dtsi.
>>
>> Rename thermal_flags to st,thermal-flags to fix the property name
> 
> Does this have an impact? If yes, then why no fixes? If no, then why
> not? How this could ever worked? Maybe this is completely unnecessary.
> 
> We already talked about this and I don't get why this change is neeeded
> and why we discuss the same problem.

and by "this change" I meant, "rename" part, instead of "removal".

Your task is analyze entire code, understand what was wrong and provide
proper solution.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH 1/7] dt-bindings: power: qcom-rpmpd: Split MSM8953 and SDM632
From: Krzysztof Kozlowski @ 2026-03-29  9:37 UTC (permalink / raw)
  To: Barnabás Czémán
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Ulf Hansson, Mathieu Poirier, Konrad Dybcio, Stephan Gerhold,
	linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <20260327-sdm632-rpmpd-v1-1-6098dc997d66@mainlining.org>

On Fri, Mar 27, 2026 at 09:11:43PM +0100, Barnabás Czémán wrote:
> Remove modem related bindings from MSM8953 rpmpd because MSM8953 MSS
> is using mss-supply as a regulator usually it is pm8953_s1.
> Split SDM632 bindings from MSM8953 because SDM632 is using mss-supply
> as a pm domain.
> 
> Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
> ---
>  .../devicetree/bindings/power/qcom,rpmpd.yaml        |  1 +
>  include/dt-bindings/power/qcom-rpmpd.h               | 20 +++++++++++++-------
>  2 files changed, 14 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> index 8174ceeab572..659936d6a46e 100644
> --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> @@ -48,6 +48,7 @@ properties:
>            - qcom,sc7280-rpmhpd
>            - qcom,sc8180x-rpmhpd
>            - qcom,sc8280xp-rpmhpd
> +          - qcom,sdm632-rpmpd
>            - qcom,sdm660-rpmpd
>            - qcom,sdm670-rpmhpd
>            - qcom,sdm845-rpmhpd
> diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h
> index 4371ac941f29..2d82434b993c 100644
> --- a/include/dt-bindings/power/qcom-rpmpd.h
> +++ b/include/dt-bindings/power/qcom-rpmpd.h
> @@ -84,13 +84,11 @@
>  #define QM215_VDDMX_AO		MSM8917_VDDMX_AO
>  
>  /* MSM8953 Power Domain Indexes */
> -#define MSM8953_VDDMD		0

ABI break / impact and due to two changes combined I don't really
understand why. Why MSS using mss-supply makes this ABI invalid/wrong?

> -#define MSM8953_VDDMD_AO	1
> -#define MSM8953_VDDCX		2
> -#define MSM8953_VDDCX_AO	3
> -#define MSM8953_VDDCX_VFL	4
> -#define MSM8953_VDDMX		5
> -#define MSM8953_VDDMX_AO	6
> +#define MSM8953_VDDCX		RPMPD_VDDCX
> +#define MSM8953_VDDCX_AO	RPMPD_VDDCX_AO
> +#define MSM8953_VDDCX_VFL	RPMPD_VDDCX_VFL
> +#define MSM8953_VDDMX		RPMPD_VDDMX
> +#define MSM8953_VDDMX_AO	RPMPD_VDDMX_AO

I don't see how this is related to new compatible and SDM632.

>  
>  /* MSM8974 Power Domain Indexes */
>  #define MSM8974_VDDCX		0
> @@ -156,6 +154,14 @@
>  #define QCS404_LPIMX		5
>  #define QCS404_LPIMX_VFL	6

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 1/7] dt-bindings: power: qcom-rpmpd: Split MSM8953 and SDM632
From: Dmitry Baryshkov @ 2026-03-29  9:51 UTC (permalink / raw)
  To: Barnabás Czémán
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Ulf Hansson, Mathieu Poirier, Konrad Dybcio, Stephan Gerhold,
	linux-arm-msm, devicetree, linux-kernel, linux-pm,
	linux-remoteproc
In-Reply-To: <39a320e472ddc6d44c950a995b577e77@mainlining.org>

On Sat, Mar 28, 2026 at 09:22:19AM +0100, Barnabás Czémán wrote:
> On 2026-03-27 21:26, Dmitry Baryshkov wrote:
> > On Fri, Mar 27, 2026 at 09:11:43PM +0100, Barnabás Czémán wrote:
> > > Remove modem related bindings from MSM8953 rpmpd because MSM8953 MSS
> > > is using mss-supply as a regulator usually it is pm8953_s1.
> > > Split SDM632 bindings from MSM8953 because SDM632 is using mss-supply
> > > as a pm domain.
> > > 
> > > Signed-off-by: Barnabás Czémán <barnabas.czeman@mainlining.org>
> > > ---
> > >  .../devicetree/bindings/power/qcom,rpmpd.yaml        |  1 +
> > >  include/dt-bindings/power/qcom-rpmpd.h               | 20
> > > +++++++++++++-------
> > >  2 files changed, 14 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> > > b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> > > index 8174ceeab572..659936d6a46e 100644
> > > --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> > > +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
> > > @@ -48,6 +48,7 @@ properties:
> > >            - qcom,sc7280-rpmhpd
> > >            - qcom,sc8180x-rpmhpd
> > >            - qcom,sc8280xp-rpmhpd
> > > +          - qcom,sdm632-rpmpd
> > >            - qcom,sdm660-rpmpd
> > >            - qcom,sdm670-rpmhpd
> > >            - qcom,sdm845-rpmhpd
> > > diff --git a/include/dt-bindings/power/qcom-rpmpd.h
> > > b/include/dt-bindings/power/qcom-rpmpd.h
> > > index 4371ac941f29..2d82434b993c 100644
> > > --- a/include/dt-bindings/power/qcom-rpmpd.h
> > > +++ b/include/dt-bindings/power/qcom-rpmpd.h
> > > @@ -84,13 +84,11 @@
> > >  #define QM215_VDDMX_AO		MSM8917_VDDMX_AO
> > > 
> > >  /* MSM8953 Power Domain Indexes */
> > > -#define MSM8953_VDDMD		0
> > > -#define MSM8953_VDDMD_AO	1
> > > -#define MSM8953_VDDCX		2
> > > -#define MSM8953_VDDCX_AO	3
> > > -#define MSM8953_VDDCX_VFL	4
> > > -#define MSM8953_VDDMX		5
> > > -#define MSM8953_VDDMX_AO	6
> > > +#define MSM8953_VDDCX		RPMPD_VDDCX
> > > +#define MSM8953_VDDCX_AO	RPMPD_VDDCX_AO
> > > +#define MSM8953_VDDCX_VFL	RPMPD_VDDCX_VFL
> > > +#define MSM8953_VDDMX		RPMPD_VDDMX
> > > +#define MSM8953_VDDMX_AO	RPMPD_VDDMX_AO
> > 
> > Well, no. This is an ABI break. It will make previous DT to stop from
> > working. You can drop unused indices, but you can not change the values
> > used by the existing domains.
> Do these indices never can be changed?

You can add new indices and you can (with some care) drop existing
incorrecr or unused ones. You can't reassign indices though. The rule of
thumb is that old DTs should continue to work without rebuilding.

> > 
> > > 
> > >  /* MSM8974 Power Domain Indexes */
> > >  #define MSM8974_VDDCX		0
> > > @@ -156,6 +154,14 @@
> > >  #define QCS404_LPIMX		5
> > >  #define QCS404_LPIMX_VFL	6
> > > 
> > > +/* SDM632 Power Domain Indexes */
> > > +#define SDM632_VDDMD		0
> > > +#define SDM632_VDDCX		1
> > > +#define SDM632_VDDCX_AO		2
> > > +#define SDM632_VDDCX_VFL	3
> > > +#define SDM632_VDDMX		4
> > > +#define SDM632_VDDMX_AO		5
> > 
> > Please use RPMHPD_* instead of introducing new entries.
> I do not understand completely, should I use RPHPD bindings in rpmpd driver
> or
> I should use rpmhpd driver for SDM632?

Sorry, I meant RPMPD_*

> > 
> > > +
> > >  /* SDM660 Power Domains */
> > >  #define SDM660_VDDCX		RPMPD_VDDCX
> > >  #define SDM660_VDDCX_AO		RPMPD_VDDCX_AO
> > > 
> > > --
> > > 2.53.0
> > > 

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH v4 3/4] thermal/qcom/lmh: support SDM670 and its CPU clusters
From: Dmitry Baryshkov @ 2026-03-29 10:44 UTC (permalink / raw)
  To: Richard Acayan
  Cc: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Amit Kucheria,
	Thara Gopinath, Bjorn Andersson, Konrad Dybcio, linux-arm-msm,
	linux-pm, devicetree
In-Reply-To: <20260328014041.83777-4-mailingradian@gmail.com>

On Fri, Mar 27, 2026 at 09:40:40PM -0400, Richard Acayan wrote:
> The LMh driver was made for Qualcomm SoCs with clusters of 4 CPUs, but
> some SoCs divide the CPUs into different sizes of clusters. In SDM670,
> the first 6 CPUs are in the little cluster and the next 2 are in the big
> cluster. Define the clusters in the match data and define the different
> cluster configuration for SDM670.
> 
> Currently, this only supports 8 CPUs and tolerates linking to any CPU in
> the cluster.
> 
> Signed-off-by: Richard Acayan <mailingradian@gmail.com>
> ---
>  drivers/thermal/qcom/lmh.c | 69 +++++++++++++++++++++++++++++++-------
>  1 file changed, 56 insertions(+), 13 deletions(-)
> 
> +static const struct lmh_soc_data sdm670_lmh_data = {
> +	.enable_algos = true,
> +	.node_ids = {
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +	},
> +};
> +
> +static const struct lmh_soc_data sdm845_lmh_data = {
> +	.enable_algos = true,
> +	.node_ids = {
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +	},
> +};

These tables made me wonder, can we determine this information from the
DT? For example, by reading the qcom,freq-domain property. But...

> +
> +static const struct lmh_soc_data sm8150_lmh_data = {
> +	.enable_algos = false,
> +	.node_ids = {
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER0_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +		LMH_CLUSTER1_NODE_ID,
> +	},
> +};

... this might be problematic, unless this entry is broken. On SM8150 we
have three freq domains, but up to now we were programming two clustern
nodes. Of course it is possible to define that node_id is 0 for freq
domain 0 and 1 for domains 1 and 2.

> +
>  static const struct of_device_id lmh_table[] = {
> -	{ .compatible = "qcom,sc8180x-lmh", },
> -	{ .compatible = "qcom,sdm845-lmh", .data = (void *)LMH_ENABLE_ALGOS},
> -	{ .compatible = "qcom,sm8150-lmh", },
> +	{ .compatible = "qcom,sc8180x-lmh", .data = &sm8150_lmh_data },
> +	{ .compatible = "qcom,sdm670-lmh", .data = &sdm670_lmh_data },
> +	{ .compatible = "qcom,sdm845-lmh", .data = &sdm845_lmh_data },
> +	{ .compatible = "qcom,sm8150-lmh", .data = &sm8150_lmh_data },
>  	{}
>  };
>  MODULE_DEVICE_TABLE(of, lmh_table);
> -- 
> 2.53.0
> 

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH v3 0/3] thermal: spacemit: Add support for SpacemiT K1 SoC thermal sensor
From: Gong Shuai @ 2026-03-29 10:45 UTC (permalink / raw)
  To: shuweiwoo
  Cc: alex, aou, conor+dt, daniel.lezcano, devicetree, dlan, krzk+dt,
	krzysztof.kozlowski, linux-kernel, linux-pm, linux-riscv,
	lukasz.luba, p.zabel, palmer, pjw, rafael, robh, rui.zhang,
	spacemit, gsh517025
In-Reply-To: <20260119-patchv2-k1-thermal-v3-0-3d82c9ebe8a4@163.com>

Hi Shuwei,

> Introduce support for the on-die thermal sensor found
> on the SpacemiT K1 SoC.
> 
> Include the device tree binding documentation in YAML format, the
> thermal sensor driver implementation, and the device tree changes to
> enable the sensor on K1 SoC.
> 
> ---
> Changes in v3:
> - Fix indentation and variable types
> - Simplify clock management and redundant assignments
> - Link to v2: https://lore.kernel.org/r/20251216-patchv2-k1-thermal-v1-0-d4b31fe9c904@163.com
> 
> Changes in v2:
> - Move driver to drivers/thermal/spacemit/ and update Kconfig/Makefile
> - Address reviewer feedback on style and structure
> - Improve variable naming and comments
> - Link to v1: https://lore.kernel.org/r/20251127-b4-k1-thermal-v1-0-f32ce47b1aba@163.com
> 
> ---
> Shuwei Wu (3):
>       dt-bindings: thermal: Add SpacemiT K1 thermal sensor
>       thermal: spacemit: k1: Add thermal sensor support
>       riscv: dts: spacemit: Add thermal sensor for K1 SoC
> 
>  .../bindings/thermal/spacemit,k1-tsensor.yaml      |  76 ++++++
>  arch/riscv/boot/dts/spacemit/k1.dtsi               | 101 ++++++++
>  drivers/thermal/Kconfig                            |   2 +
>  drivers/thermal/Makefile                           |   1 +
>  drivers/thermal/spacemit/Kconfig                   |  19 ++
>  drivers/thermal/spacemit/Makefile                  |   3 +
>  drivers/thermal/spacemit/k1_tsensor.c              | 281 +++++++++++++++++++++
>  7 files changed, 483 insertions(+)
> ---
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> change-id: 20251215-patchv2-k1-thermal-5ffb838fc1cc
> 
> Best regards,
> -- 
> Shuwei Wu <shuweiwoo@163.com>

This patch series works well on OrangePi-RV2 with mainline kernel 7.0.0-rc5

$ cat /sys/class/thermal/thermal_zone*/type
soc-thermal
package-thermal
gpu-thermal
cluster0-thermal
cluster1-thermal
$ cat /sys/class/thermal/thermal_zone*/temp
35000
37000
36000
36000
37000
$ sensors
cluster1_thermal-virtual-0
Adapter: Virtual device
temp1:        +38.0 C

gpu_thermal-virtual-0
Adapter: Virtual device
temp1:        +36.0 C

soc_thermal-virtual-0
Adapter: Virtual device
temp1:        +36.0 C

cluster0_thermal-virtual-0
Adapter: Virtual device
temp1:        +37.0 C

package_thermal-virtual-0
Adapter: Virtual device
temp1:        +37.0 C


Tested-by: Gong Shuai <gsh517025@gmail.com>

Thanks.

^ permalink raw reply

* [PATCH v3 0/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Gopi Krishna Menon @ 2026-03-29 12:34 UTC (permalink / raw)
  To: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt
  Cc: Gopi Krishna Menon, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry

This patch series converts SPEAr Thermal Sensor bindings to DT schema
and removes the thermal_flags property from spear13xx.dtsi.

Changes since v2:
- Reword the commit message and subject to correct explanation in patch 2
- No changes in patch 1
Changes since v1:
- Changed unevaluatedProperties to additionalProperties in the binding
- Reword the commit message and subject in the second patch

Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings

Gopi Krishna Menon (2):
  dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
  ARM: dts: st: spear: remove undocumented thermal_flags property

 .../bindings/thermal/spear-thermal.txt        | 14 --------
 .../thermal/st,thermal-spear1340.yaml         | 36 +++++++++++++++++++
 arch/arm/boot/dts/st/spear13xx.dtsi           |  1 -
 3 files changed, 36 insertions(+), 15 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/thermal/spear-thermal.txt
 create mode 100644 Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml

-- 
2.52.0


^ permalink raw reply

* [PATCH v3 1/2] dt-bindings: thermal: st,thermal-spear1340: convert to dtschema
From: Gopi Krishna Menon @ 2026-03-29 12:34 UTC (permalink / raw)
  To: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt
  Cc: Gopi Krishna Menon, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry, Krzysztof Kozlowski
In-Reply-To: <20260329123449.309814-1-krishnagopi487@gmail.com>

Convert the SPEAr Thermal Sensor bindings to DT schema.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
---
Changes since v2:
- No changes
Changes since v1:
- Changed unevaluatedProperties to additionalProperties in the binding
- Reword the commit message and subject in the second patch

Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings

 .../bindings/thermal/spear-thermal.txt        | 14 --------
 .../thermal/st,thermal-spear1340.yaml         | 36 +++++++++++++++++++
 2 files changed, 36 insertions(+), 14 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/thermal/spear-thermal.txt
 create mode 100644 Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml

diff --git a/Documentation/devicetree/bindings/thermal/spear-thermal.txt b/Documentation/devicetree/bindings/thermal/spear-thermal.txt
deleted file mode 100644
index 93e3b67c102d..000000000000
--- a/Documentation/devicetree/bindings/thermal/spear-thermal.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-* SPEAr Thermal
-
-Required properties:
-- compatible : "st,thermal-spear1340"
-- reg : Address range of the thermal registers
-- st,thermal-flags: flags used to enable thermal sensor
-
-Example:
-
-	thermal@fc000000 {
-		compatible = "st,thermal-spear1340";
-		reg = <0xfc000000 0x1000>;
-		st,thermal-flags = <0x7000>;
-	};
diff --git a/Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml b/Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml
new file mode 100644
index 000000000000..e3462a974691
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/st,thermal-spear1340.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/st,thermal-spear1340.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SPEAr Thermal Sensor
+
+maintainers:
+  - Viresh Kumar <vireshk@kernel.org>
+
+properties:
+  compatible:
+    const: st,thermal-spear1340
+
+  reg:
+    maxItems: 1
+
+  st,thermal-flags:
+    description: flags used to enable thermal sensor
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+  - compatible
+  - reg
+  - st,thermal-flags
+
+additionalProperties: false
+
+examples:
+  - |
+    thermal@fc000000 {
+      compatible = "st,thermal-spear1340";
+      reg = <0xfc000000 0x1000>;
+      st,thermal-flags = <0x7000>;
+    };
-- 
2.52.0


^ permalink raw reply related

* [PATCH v3 2/2] ARM: dts: st: spear: remove undocumented thermal_flags property
From: Gopi Krishna Menon @ 2026-03-29 12:34 UTC (permalink / raw)
  To: rafael, daniel.lezcano, rui.zhang, lukasz.luba, robh, krzk+dt,
	vireshk, conor+dt
  Cc: Gopi Krishna Menon, linux-pm, devicetree, linux-kernel,
	linux-arm-kernel, soc, daniel.baluta, simona.toaca, d-gole,
	m-chawdhry
In-Reply-To: <20260329123449.309814-1-krishnagopi487@gmail.com>

spear13xx.dtsi defines a thermal_flags property in spear thermal sensor
node which is both unused in kernel and undocumented in spear thermal
sensor's binding.

There were no dtbs_check warnings associated with this property as the
underlying spear thermal binding was not converted to DTSchema.

Most likely st,thermal-flags is a misspelling of thermal_flags in
spear13xx.dtsi. Since both st/spear1310.dtsi and st/spear1340.dtsi
define st,thermal-flags property in spear thermal sensor node, we can
safely remove this property from spear13xx.dtsi.

Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
---
Changes since v2:
- Reword the commit message and subject to correct explanation in patch 2
Changes since v1:
- Changed unevaluatedProperties to additionalProperties in the binding
- Reword the commit message and subject in the second patch

Note:
* This patch is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings

 arch/arm/boot/dts/st/spear13xx.dtsi | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm/boot/dts/st/spear13xx.dtsi b/arch/arm/boot/dts/st/spear13xx.dtsi
index 159e941708ca..0bb88f2d4ef5 100644
--- a/arch/arm/boot/dts/st/spear13xx.dtsi
+++ b/arch/arm/boot/dts/st/spear13xx.dtsi
@@ -332,7 +332,6 @@ wdt@ec800620 {
 			thermal@e07008c4 {
 				compatible = "st,thermal-spear1340";
 				reg = <0xe07008c4 0x4>;
-				thermal_flags = <0x7000>;
 			};
 		};
 	};
-- 
2.52.0


^ permalink raw reply related

* Re: [GIT PULL] interconnect fix for 7.0-rc
From: Greg KH @ 2026-03-29 12:48 UTC (permalink / raw)
  To: Georgi Djakov; +Cc: linux-pm, linux-kernel
In-Reply-To: <20260324152553.612127-1-djakov@kernel.org>

On Tue, Mar 24, 2026 at 05:25:53PM +0200, Georgi Djakov wrote:
> Hello Greg,
> 
> This pull request contains one driver fix for v7.0. The details are
> in the signed tag. It have been also in linux-next for a week. Please
> pull into char-misc-linus when you get a chance.
> 
> Thanks,
> Georgi
> 
> The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
> 
>   Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
> 
> are available in the Git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc.git tags/icc-7.0-rc6

PUlled and pushed out, thanks.

greg k-h

^ permalink raw reply

* RE: [char-misc v3] mei: me: reduce the scope on unexpected reset
From: Usyskin, Alexander @ 2026-03-29 14:30 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, Adin, Menachem, Wysocki, Rafael J,
	linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
	Todd Brandt
In-Reply-To: <CAJZ5v0hvf_q=g-PzerYnqZBjnDEsnnA405ZnD-BumPJG56HHoA@mail.gmail.com>

> Subject: Re: [char-misc v3] mei: me: reduce the scope on unexpected reset
> 
> On Thu, Mar 26, 2026 at 4:14 PM Alexander Usyskin
> <alexander.usyskin@intel.com> wrote:
> >
> > After commit 2cedb296988c ("mei: me: trigger link reset if hw ready is
> unexpected")
> > some devices started to show long resume times (5-7 seconds).
> > This happens as mei falsely detects unready hardware,
> > starts parallel link reset flow and triggers link reset timeouts
> > in the resume callback.
> >
> > Address it by performing detection of unready hardware only
> > when driver is in the ENABLED state instead of blacklisting
> > states as done in the original patch.
> >
> > Reported-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> > Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221023
> > Tested-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> > Fixes: 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected")
> > Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> > ---
> >
> > V3: reword commit message, add Rafael and PM list
> >
> > V2: rebase over v7.0-rc4
> >
> >  drivers/misc/mei/hw-me.c | 14 ++++----------
> >  1 file changed, 4 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
> > index d4612c659784..1e4a41ac428f 100644
> > --- a/drivers/misc/mei/hw-me.c
> > +++ b/drivers/misc/mei/hw-me.c
> > @@ -1337,19 +1337,13 @@ irqreturn_t mei_me_irq_thread_handler(int
> irq, void *dev_id)
> >         /*  check if we need to start the dev */
> >         if (!mei_host_is_ready(dev)) {
> >                 if (mei_hw_is_ready(dev)) {
> > -                       /* synchronized by dev mutex */
> 
> I think that the comment above is still valid, isn't it?  If so, why remove it?
> 
> > -                       if (waitqueue_active(&dev->wait_hw_ready)) {
> > -                               dev_dbg(&dev->dev, "we need to start the dev.\n");
> > -                               dev->recvd_hw_ready = true;
> > -                               wake_up(&dev->wait_hw_ready);
> > -                       } else if (dev->dev_state != MEI_DEV_UNINITIALIZED &&
> > -                                  dev->dev_state != MEI_DEV_POWERING_DOWN &&
> > -                                  dev->dev_state != MEI_DEV_POWER_DOWN) {
> > +                       if (dev->dev_state == MEI_DEV_ENABLED) {
> >                                 dev_dbg(&dev->dev, "Force link reset.\n");
> >                                 schedule_work(&dev->reset_work);
> >                         } else {
> 
> Isn't the waitqueue_active() check needed any more?
> 

The fix simplifies original patch as in MEI_DEV_ENABLED state there will be no active waitqueue.
This also removes need for comment about waitqueue synchronization.

- - 
Thanks,
Sasha



^ permalink raw reply

* Re: [char-misc v3] mei: me: reduce the scope on unexpected reset
From: Rafael J. Wysocki @ 2026-03-29 16:13 UTC (permalink / raw)
  To: Usyskin, Alexander
  Cc: Rafael J. Wysocki, Greg Kroah-Hartman, Adin, Menachem,
	Wysocki, Rafael J, linux-pm@vger.kernel.org,
	linux-kernel@vger.kernel.org, Todd Brandt
In-Reply-To: <CY5PR11MB6366E99764D582E2845DDE90ED55A@CY5PR11MB6366.namprd11.prod.outlook.com>

On Sun, Mar 29, 2026 at 4:30 PM Usyskin, Alexander
<alexander.usyskin@intel.com> wrote:
>
> > Subject: Re: [char-misc v3] mei: me: reduce the scope on unexpected reset
> >
> > On Thu, Mar 26, 2026 at 4:14 PM Alexander Usyskin
> > <alexander.usyskin@intel.com> wrote:
> > >
> > > After commit 2cedb296988c ("mei: me: trigger link reset if hw ready is
> > unexpected")
> > > some devices started to show long resume times (5-7 seconds).
> > > This happens as mei falsely detects unready hardware,
> > > starts parallel link reset flow and triggers link reset timeouts
> > > in the resume callback.
> > >
> > > Address it by performing detection of unready hardware only
> > > when driver is in the ENABLED state instead of blacklisting
> > > states as done in the original patch.
> > >
> > > Reported-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> > > Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221023
> > > Tested-by: Todd Brandt <todd.e.brandt@linux.intel.com>
> > > Fixes: 2cedb296988c ("mei: me: trigger link reset if hw ready is unexpected")
> > > Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> > > ---
> > >
> > > V3: reword commit message, add Rafael and PM list
> > >
> > > V2: rebase over v7.0-rc4
> > >
> > >  drivers/misc/mei/hw-me.c | 14 ++++----------
> > >  1 file changed, 4 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
> > > index d4612c659784..1e4a41ac428f 100644
> > > --- a/drivers/misc/mei/hw-me.c
> > > +++ b/drivers/misc/mei/hw-me.c
> > > @@ -1337,19 +1337,13 @@ irqreturn_t mei_me_irq_thread_handler(int
> > irq, void *dev_id)
> > >         /*  check if we need to start the dev */
> > >         if (!mei_host_is_ready(dev)) {
> > >                 if (mei_hw_is_ready(dev)) {
> > > -                       /* synchronized by dev mutex */
> >
> > I think that the comment above is still valid, isn't it?  If so, why remove it?
> >
> > > -                       if (waitqueue_active(&dev->wait_hw_ready)) {
> > > -                               dev_dbg(&dev->dev, "we need to start the dev.\n");
> > > -                               dev->recvd_hw_ready = true;
> > > -                               wake_up(&dev->wait_hw_ready);
> > > -                       } else if (dev->dev_state != MEI_DEV_UNINITIALIZED &&
> > > -                                  dev->dev_state != MEI_DEV_POWERING_DOWN &&
> > > -                                  dev->dev_state != MEI_DEV_POWER_DOWN) {
> > > +                       if (dev->dev_state == MEI_DEV_ENABLED) {
> > >                                 dev_dbg(&dev->dev, "Force link reset.\n");
> > >                                 schedule_work(&dev->reset_work);
> > >                         } else {
> >
> > Isn't the waitqueue_active() check needed any more?
> >
>
> The fix simplifies original patch as in MEI_DEV_ENABLED state there will be no active waitqueue.
> This also removes need for comment about waitqueue synchronization.

I see, thanks.

It would be good to add the above information to the patch changelog.

With that, please feel free to add

Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>

to the patch.

^ permalink raw reply

* [PATCH] PM / wakeup: Allocate class wakeup_class statically
From: Heiner Kallweit @ 2026-03-29 16:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Pavel Machek, Len Brown, Greg Kroah-Hartman,
	Danilo Krummrich
  Cc: Linux PM, driver-core

Allocating wakeup_class statically avoids a little runtime overhead.
Define groups and device release function as part of the class, so that
we don't have to repeat this for each class device.
Whilst at it, constify wakeup_source_attrs[].

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/base/power/wakeup_stats.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c
index 308f8bde9..72beb8fce 100644
--- a/drivers/base/power/wakeup_stats.c
+++ b/drivers/base/power/wakeup_stats.c
@@ -18,8 +18,6 @@
 
 #include "power.h"
 
-static struct class *wakeup_class;
-
 #define wakeup_attr(_name)						\
 static ssize_t _name##_show(struct device *dev,				\
 			    struct device_attribute *attr, char *buf)	\
@@ -114,7 +112,7 @@ static ssize_t prevent_suspend_time_ms_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(prevent_suspend_time_ms);
 
-static struct attribute *wakeup_source_attrs[] = {
+static const struct attribute *const wakeup_source_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_active_count.attr,
 	&dev_attr_event_count.attr,
@@ -135,6 +133,12 @@ static void device_create_release(struct device *dev)
 	kfree(dev);
 }
 
+static const struct class wakeup_class = {
+	.name = "wakeup",
+	.dev_release = device_create_release,
+	.dev_groups = wakeup_source_groups,
+};
+
 static struct device *wakeup_source_device_create(struct device *parent,
 						  struct wakeup_source *ws)
 {
@@ -149,10 +153,8 @@ static struct device *wakeup_source_device_create(struct device *parent,
 
 	device_initialize(dev);
 	dev->devt = MKDEV(0, 0);
-	dev->class = wakeup_class;
+	dev->class = &wakeup_class;
 	dev->parent = parent;
-	dev->groups = wakeup_source_groups;
-	dev->release = device_create_release;
 	dev_set_drvdata(dev, ws);
 	device_set_pm_not_required(dev);
 
@@ -212,8 +214,6 @@ void wakeup_source_sysfs_remove(struct wakeup_source *ws)
 
 static int __init wakeup_sources_sysfs_init(void)
 {
-	wakeup_class = class_create("wakeup");
-
-	return PTR_ERR_OR_ZERO(wakeup_class);
+	return class_register(&wakeup_class);
 }
 postcore_initcall(wakeup_sources_sysfs_init);
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH 3/3] arm64/sched: Enable CPPC-based asympacking
From: Christian Loehle @ 2026-03-29 17:49 UTC (permalink / raw)
  To: Valentin Schneider, arighi
  Cc: peterz, vincent.guittot, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis
In-Reply-To: <xhsmhy0jdffak.mognet@vschneid-thinkpadt14sgen2i.remote.csb>

On 3/27/26 15:44, Valentin Schneider wrote:
> On 25/03/26 18:13, Christian Loehle wrote:
>> @@ -1753,7 +1760,7 @@ static inline int topology_arch_sched_asym_flags(void)
>>  #ifdef CONFIG_SCHED_SMT
>>  int cpu_smt_flags(void)
>>  {
>> -	return SD_SHARE_CPUCAPACITY | SD_SHARE_LLC;
>> +	return SD_SHARE_CPUCAPACITY | SD_SHARE_LLC | arch_sched_asym_flags();
>>  }
>>  
>>  const struct cpumask *tl_smt_mask(struct sched_domain_topology_level *tl, int cpu)
>> @@ -1765,7 +1772,7 @@ const struct cpumask *tl_smt_mask(struct sched_domain_topology_level *tl, int cp
>>  #ifdef CONFIG_SCHED_CLUSTER
>>  int cpu_cluster_flags(void)
>>  {
>> -	return SD_CLUSTER | SD_SHARE_LLC;
>> +	return SD_CLUSTER | SD_SHARE_LLC | arch_sched_asym_flags();
>>  }
>>  
>>  const struct cpumask *tl_cls_mask(struct sched_domain_topology_level *tl, int cpu)
>> @@ -1777,7 +1784,7 @@ const struct cpumask *tl_cls_mask(struct sched_domain_topology_level *tl, int cp
>>  #ifdef CONFIG_SCHED_MC
>>  int cpu_core_flags(void)
>>  {
>> -	return SD_SHARE_LLC;
>> +	return SD_SHARE_LLC | arch_sched_asym_flags();
>>  }
>>
> 
> So while the binning problem applies to more than one architecture, I'm not
> sure we want this to be generally applied to all topology levels. This is
> /technically/ not a problem since even if a topology level has
> SD_ASYM_PACKING, all CPUs at that level can have the same priority, but
> in that case it's a bit wasteful.
> 
> I don't have any better ideas ATM to keep this arch-specific via
> set_sched_topology(), like how x86 and powerpc handle asym packing.
> 

Right, I think it doesn't make sense at SMT level, but the rest seems
sensible IMO?
It is noted for v2, although unless Andrea sees vastly different results
for Grace than for Vera appetite for this is rather low anyway.

^ permalink raw reply

* Re: [PATCH] dt-bindings: power: reset: cortina,gemini-power-controller: convert to DT schema
From: Linus Walleij @ 2026-03-29 18:57 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Krzysztof Kozlowski, Rob Herring (Arm), Khushal Chitturi,
	devicetree, linux-kernel, linux-pm, Krzysztof Kozlowski,
	Conor Dooley
In-Reply-To: <aciItMCdBbrVvMKB@venus>

On Sun, Mar 29, 2026 at 4:09 AM Sebastian Reichel <sre@kernel.org> wrote:

> The problem is the node name (power-controller@4b000000), which is
> reserved for power domains. You can keep the compatible.

Ah, sweet, Khushal can you change this?

I think you can use gemini-poweroff@4b000000 because
this is pretty much a poweroff thingie.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH v6 0/5] amd-pstate Dynamic EPP and raw EPP
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)

Dynamic EPP allows the kernel to register amd-pstate as part of
a platform profile. It will change EPP modes matching the user's
preference to the platform profile sysfs files as well as power
adapter state.

Raw EPP allows userspace to write integers to
energy_performance_preference.

This is based off superm1/linux-next + [1]

Note: v5 had some feedback to always default dynamic EPP to disabled for
server, but I think that's very confusing if the kconfig or kernel command
line option don't work for server but do for client.
Server effectively be a no-op because the platform profile will start
in performance anyway.

v5: [2]

Link: https://lore.kernel.org/linux-pm/20260326193620.649441-1-mario.limonciello@amd.com/ [1]
Link: https://lore.kernel.org/linux-pm/20260106051441.60093-1-superm1@kernel.org/ [2]

Mario Limonciello (AMD) (5):
  cpufreq/amd-pstate: Add dynamic energy performance preference
  cpufreq/amd-pstate: add kernel command line to override dynamic epp
  cpufreq/amd-pstate: Add support for platform profile class
  cpufreq/amd-pstate: Add support for raw EPP writes
  cpufreq/amd-pstate-ut: Add a unit test for raw EPP

 .../admin-guide/kernel-parameters.txt         |   7 +
 Documentation/admin-guide/pm/amd-pstate.rst   |  41 ++-
 drivers/cpufreq/Kconfig.x86                   |  13 +
 drivers/cpufreq/amd-pstate-ut.c               | 109 +++++++
 drivers/cpufreq/amd-pstate.c                  | 287 ++++++++++++++++--
 drivers/cpufreq/amd-pstate.h                  |  21 +-
 6 files changed, 449 insertions(+), 29 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH v6 1/5] cpufreq/amd-pstate: Add dynamic energy performance preference
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)
In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org>

Dynamic energy performance preference changes the EPP profile based on
whether the machine is running on AC or DC power.

A notification chain from the power supply core is used to adjust EPP
values on plug in or plug out events.

When enabled, the driver exposes a sysfs toggle for dynamic EPP, blocks
manual writes to energy_performance_preference, and keeps the policy in
performance mode while it "owns" the EPP updates.

For non-server systems:
    * the default EPP for AC mode is `performance`.
    * the default EPP for DC mode is `balance_performance`.

For server systems dynamic EPP is mostly a no-op.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v5->v6:
 * Set the power supply notifier callback before registration
 * Expand the changelog to cover the sysfs toggle and manual EPP blocking
 * Add missing kdoc
---
 Documentation/admin-guide/pm/amd-pstate.rst |  18 ++-
 drivers/cpufreq/Kconfig.x86                 |  12 ++
 drivers/cpufreq/amd-pstate.c                | 137 ++++++++++++++++++--
 drivers/cpufreq/amd-pstate.h                |  10 +-
 4 files changed, 163 insertions(+), 14 deletions(-)

diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index d6c2f233ab239..0e4355fe13558 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -325,7 +325,7 @@ and user can change current preference according to energy or performance needs
 Please get all support profiles list from
 ``energy_performance_available_preferences`` attribute, all the profiles are
 integer values defined between 0 to 255 when EPP feature is enabled by platform
-firmware, if EPP feature is disabled, driver will ignore the written value
+firmware, but if the dynamic EPP feature is enabled, driver will block writes.
 This attribute is read-write.
 
 ``boost``
@@ -347,6 +347,22 @@ boost or `1` to enable it, for the respective CPU using the sysfs path
 Other performance and frequency values can be read back from
 ``/sys/devices/system/cpu/cpuX/acpi_cppc/``, see :ref:`cppc_sysfs`.
 
+Dynamic energy performance profile
+==================================
+The amd-pstate driver supports dynamically selecting the energy performance
+profile based on whether the machine is running on AC or DC power.
+
+Whether this behavior is enabled by default with the kernel config option
+`CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`. This behavior can also be overridden
+at runtime by the sysfs file ``/sys/devices/system/cpu/cpufreq/policyX/dynamic_epp``.
+
+When set to enabled, the driver will select a different energy performance
+profile when the machine is running on battery or AC power.
+When set to disabled, the driver will not change the energy performance profile
+based on the power source and will not react to user desired power state.
+
+Attempting to manually write to the ``energy_performance_preference`` sysfs
+file will fail when ``dynamic_epp`` is enabled.
 
 ``amd-pstate`` vs ``acpi-cpufreq``
 ======================================
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
index 2c5c228408bf2..cdaa8d858045a 100644
--- a/drivers/cpufreq/Kconfig.x86
+++ b/drivers/cpufreq/Kconfig.x86
@@ -68,6 +68,18 @@ config X86_AMD_PSTATE_DEFAULT_MODE
 	  For details, take a look at:
 	  <file:Documentation/admin-guide/pm/amd-pstate.rst>.
 
+config X86_AMD_PSTATE_DYNAMIC_EPP
+	bool "AMD Processor P-State dynamic EPP support"
+	depends on X86_AMD_PSTATE
+	default n
+	help
+	  Allow the kernel to dynamically change the energy performance
+	  value from events like ACPI platform profile and AC adapter plug
+	  events.
+
+	  This feature can also be changed at runtime, this configuration
+	  option only sets the kernel default value behavior.
+
 config X86_AMD_PSTATE_UT
 	tristate "selftest for AMD Processor P-State driver"
 	depends on X86 && ACPI_PROCESSOR
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index f207252eb5f5f..2e3fb1fd280a0 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -36,6 +36,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/uaccess.h>
+#include <linux/power_supply.h>
 #include <linux/static_call.h>
 #include <linux/topology.h>
 
@@ -86,6 +87,11 @@ static struct cpufreq_driver amd_pstate_driver;
 static struct cpufreq_driver amd_pstate_epp_driver;
 static int cppc_state = AMD_PSTATE_UNDEFINED;
 static bool amd_pstate_prefcore = true;
+#ifdef CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP
+static bool dynamic_epp = CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP;
+#else
+static bool dynamic_epp;
+#endif
 static struct quirk_entry *quirks;
 
 /*
@@ -1155,6 +1161,74 @@ static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
 	kfree(cpudata);
 }
 
+static int amd_pstate_get_balanced_epp(struct cpufreq_policy *policy)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+
+	if (power_supply_is_system_supplied())
+		return cpudata->epp_default_ac;
+	else
+		return cpudata->epp_default_dc;
+}
+
+static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
+					    unsigned long event, void *data)
+{
+	struct amd_cpudata *cpudata = container_of(nb, struct amd_cpudata, power_nb);
+	struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
+	u8 epp;
+	int ret;
+
+	if (event != PSY_EVENT_PROP_CHANGED)
+		return NOTIFY_OK;
+
+	epp = amd_pstate_get_balanced_epp(policy);
+
+	ret = amd_pstate_set_epp(policy, epp);
+	if (ret)
+		pr_warn("Failed to set CPU %d EPP %u: %d\n", cpudata->cpu, epp, ret);
+
+	return NOTIFY_OK;
+}
+static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+
+	if (cpudata->power_nb.notifier_call)
+		power_supply_unreg_notifier(&cpudata->power_nb);
+	cpudata->dynamic_epp = false;
+}
+
+static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+	int ret;
+	u8 epp;
+
+	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+	epp = amd_pstate_get_balanced_epp(policy);
+	ret = amd_pstate_set_epp(policy, epp);
+	if (ret)
+		return ret;
+
+	/* only enable notifier if things will actually change */
+	if (cpudata->epp_default_ac != cpudata->epp_default_dc) {
+		cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier;
+		ret = power_supply_reg_notifier(&cpudata->power_nb);
+		if (ret)
+			goto cleanup;
+	}
+
+	cpudata->dynamic_epp = true;
+
+	return 0;
+
+cleanup:
+	amd_pstate_clear_dynamic_epp(policy);
+
+	return ret;
+}
+
 /* Sysfs attributes */
 
 /*
@@ -1244,14 +1318,19 @@ static ssize_t store_energy_performance_preference(
 	ssize_t ret;
 	u8 epp;
 
+	if (cpudata->dynamic_epp) {
+		pr_debug("EPP cannot be set when dynamic EPP is enabled\n");
+		return -EBUSY;
+	}
+
 	ret = sysfs_match_string(energy_perf_strings, buf);
 	if (ret < 0)
 		return -EINVAL;
 
-	if (!ret)
-		epp = cpudata->epp_default;
-	else
+	if (ret)
 		epp = epp_values[ret];
+	else
+		epp = amd_pstate_get_balanced_epp(policy);
 
 	if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
 		pr_debug("EPP cannot be set under performance policy\n");
@@ -1259,6 +1338,8 @@ static ssize_t store_energy_performance_preference(
 	}
 
 	ret = amd_pstate_set_epp(policy, epp);
+	if (ret)
+		return ret;
 
 	return ret ? ret : count;
 }
@@ -1620,12 +1701,40 @@ static ssize_t prefcore_show(struct device *dev,
 	return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
 }
 
+static ssize_t dynamic_epp_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp));
+}
+
+static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b,
+				 const char *buf, size_t count)
+{
+	bool enabled;
+	int ret;
+
+	ret = kstrtobool(buf, &enabled);
+	if (ret)
+		return ret;
+
+	if (dynamic_epp == enabled)
+		return -EINVAL;
+
+	/* reinitialize with desired dynamic EPP value */
+	dynamic_epp = enabled;
+	ret = amd_pstate_change_driver_mode(cppc_state);
+
+	return ret ? ret : count;
+}
+
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(prefcore);
+static DEVICE_ATTR_RW(dynamic_epp);
 
 static struct attribute *pstate_global_attributes[] = {
 	&dev_attr_status.attr,
 	&dev_attr_prefcore.attr,
+	&dev_attr_dynamic_epp.attr,
 	NULL
 };
 
@@ -1715,22 +1824,20 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 	if (amd_pstate_acpi_pm_profile_server() ||
 	    amd_pstate_acpi_pm_profile_undefined()) {
 		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
-		cpudata->epp_default = amd_pstate_get_epp(cpudata);
+		cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);
 	} else {
 		policy->policy = CPUFREQ_POLICY_POWERSAVE;
-		cpudata->epp_default = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
+		cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
+		cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
 	}
 
-	ret = amd_pstate_set_epp(policy, cpudata->epp_default);
+	if (dynamic_epp)
+		ret = amd_pstate_set_dynamic_epp(policy);
+	else
+		ret = amd_pstate_set_epp(policy, amd_pstate_get_balanced_epp(policy));
 	if (ret)
 		goto free_cpudata1;
 
-	ret = amd_pstate_init_floor_perf(policy);
-	if (ret) {
-		dev_err(dev, "Failed to initialize Floor Perf (%d)\n", ret);
-		goto free_cpudata1;
-	}
-
 	current_pstate_driver->adjust_perf = NULL;
 
 	return 0;
@@ -1753,6 +1860,8 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
 		amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
 		amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 
+		if (cpudata->dynamic_epp)
+			amd_pstate_clear_dynamic_epp(policy);
 		kfree(cpudata);
 		policy->driver_data = NULL;
 	}
@@ -1790,6 +1899,10 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)
 	if (!policy->cpuinfo.max_freq)
 		return -ENODEV;
 
+	/* policy can't be changed to powersave policy while dynamic epp is enabled */
+	if (policy->policy == CPUFREQ_POLICY_POWERSAVE && cpudata->dynamic_epp)
+		return -EBUSY;
+
 	cpudata->policy = policy->policy;
 
 	ret = amd_pstate_epp_update_limit(policy, true);
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index 32b8b26ce388f..d929ae3163b3d 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -85,6 +85,11 @@ struct amd_aperf_mperf {
  * 		  AMD P-State driver supports preferred core featue.
  * @epp_cached: Cached CPPC energy-performance preference value
  * @policy: Cpufreq policy value
+ * @suspended: If CPU core if offlined
+ * @epp_default_ac: Default EPP value for AC power source
+ * @epp_default_dc: Default EPP value for DC power source
+ * @dynamic_epp: Whether dynamic EPP is enabled
+ * @power_nb: Notifier block for power events
  *
  * The amd_cpudata is key private data for each CPU thread in AMD P-State, and
  * represents all the attributes and goals that AMD P-State requests at runtime.
@@ -118,7 +123,10 @@ struct amd_cpudata {
 	/* EPP feature related attributes*/
 	u32	policy;
 	bool	suspended;
-	u8	epp_default;
+	u8	epp_default_ac;
+	u8	epp_default_dc;
+	bool	dynamic_epp;
+	struct notifier_block power_nb;
 };
 
 /*
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 2/5] cpufreq/amd-pstate: add kernel command line to override dynamic epp
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)
In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org>

Add `amd_dynamic_epp=enable` and `amd_dynamic_epp=disable` to override
the kernel configuration option `CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`
locally.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v5->v6:
 * Fix the amd_dynamic_epp kernel parameter name in amd-pstate.rst
---
 Documentation/admin-guide/kernel-parameters.txt |  7 +++++++
 Documentation/admin-guide/pm/amd-pstate.rst     |  7 +++++++
 drivers/cpufreq/amd-pstate.c                    | 11 +++++++++++
 3 files changed, 25 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 03a550630644f..9552819051cd8 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -493,6 +493,13 @@ Kernel parameters
 			disable
 			  Disable amd-pstate preferred core.
 
+	amd_dynamic_epp=
+			[X86]
+			disable
+			  Disable amd-pstate dynamic EPP.
+			enable
+			  Enable amd-pstate dynamic EPP.
+
 	amijoy.map=	[HW,JOY] Amiga joystick support
 			Map of devices attached to JOY0DAT and JOY1DAT
 			Format: <a>,<b>
diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index 0e4355fe13558..210207d301aa5 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -474,6 +474,13 @@ For systems that support ``amd-pstate`` preferred core, the core rankings will
 always be advertised by the platform. But OS can choose to ignore that via the
 kernel parameter ``amd_prefcore=disable``.
 
+``amd_dynamic_epp``
+
+When AMD pstate is in auto mode, dynamic EPP will control whether the kernel
+autonomously changes the EPP mode. The default is configured by
+``CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`` but can be explicitly enabled with
+``amd_dynamic_epp=enable`` or disabled with ``amd_dynamic_epp=disable``.
+
 User Space Interface in ``sysfs`` - General
 ===========================================
 
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 2e3fb1fd280a0..e96f1da5c7b38 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -2224,8 +2224,19 @@ static int __init amd_prefcore_param(char *str)
 	return 0;
 }
 
+static int __init amd_dynamic_epp_param(char *str)
+{
+	if (!strcmp(str, "disable"))
+		dynamic_epp = false;
+	if (!strcmp(str, "enable"))
+		dynamic_epp = true;
+
+	return 0;
+}
+
 early_param("amd_pstate", amd_pstate_param);
 early_param("amd_prefcore", amd_prefcore_param);
+early_param("amd_dynamic_epp", amd_dynamic_epp_param);
 
 MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
 MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 3/5] cpufreq/amd-pstate: Add support for platform profile class
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)
In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org>

The platform profile core allows multiple drivers and devices to
register platform profile support.

When the legacy platform profile interface is used all drivers will
adjust the platform profile as well.

Add support for registering every CPU with the platform profile handler
when dynamic EPP is enabled.

The end result will be that changing the platform profile will modify
EPP accordingly.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v5->v6:
 * Keep the platform profile patch focused on platform profile support
 * Move the raw EPP plumbing into the follow-up raw EPP patch
 * Move the test helper exports into the unit-test patch
---
 Documentation/admin-guide/pm/amd-pstate.rst |   4 +-
 drivers/cpufreq/Kconfig.x86                 |   1 +
 drivers/cpufreq/amd-pstate.c                | 110 ++++++++++++++++++--
 drivers/cpufreq/amd-pstate.h                |   6 ++
 4 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index 210207d301aa5..2d92c8072b83c 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -357,7 +357,9 @@ Whether this behavior is enabled by default with the kernel config option
 at runtime by the sysfs file ``/sys/devices/system/cpu/cpufreq/policyX/dynamic_epp``.
 
 When set to enabled, the driver will select a different energy performance
-profile when the machine is running on battery or AC power.
+profile when the machine is running on battery or AC power. The driver will
+also register with the platform profile handler to receive notifications of
+user desired power state and react to those.
 When set to disabled, the driver will not change the energy performance profile
 based on the power source and will not react to user desired power state.
 
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
index cdaa8d858045a..a0dbb9808ae99 100644
--- a/drivers/cpufreq/Kconfig.x86
+++ b/drivers/cpufreq/Kconfig.x86
@@ -40,6 +40,7 @@ config X86_AMD_PSTATE
 	select ACPI_PROCESSOR
 	select ACPI_CPPC_LIB if X86_64
 	select CPU_FREQ_GOV_SCHEDUTIL if SMP
+	select ACPI_PLATFORM_PROFILE
 	help
 	  This driver adds a CPUFreq driver which utilizes a fine grain
 	  processor performance frequency control range instead of legacy
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index e96f1da5c7b38..93cda05ffa855 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1182,6 +1182,10 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
 	if (event != PSY_EVENT_PROP_CHANGED)
 		return NOTIFY_OK;
 
+	/* dynamic actions are only applied while platform profile is in balanced */
+	if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED)
+		return 0;
+
 	epp = amd_pstate_get_balanced_epp(policy);
 
 	ret = amd_pstate_set_epp(policy, epp);
@@ -1190,12 +1194,81 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
 
 	return NOTIFY_OK;
 }
+
+static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices)
+{
+	set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+	return 0;
+}
+
+static int amd_pstate_profile_get(struct device *dev,
+				  enum platform_profile_option *profile)
+{
+	struct amd_cpudata *cpudata = dev_get_drvdata(dev);
+
+	*profile = cpudata->current_profile;
+
+	return 0;
+}
+
+static int amd_pstate_profile_set(struct device *dev,
+				  enum platform_profile_option profile)
+{
+	struct amd_cpudata *cpudata = dev_get_drvdata(dev);
+	struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
+	int ret;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		if (cpudata->policy != CPUFREQ_POLICY_POWERSAVE)
+			cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
+		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE);
+		if (ret)
+			return ret;
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		if (cpudata->policy != CPUFREQ_POLICY_POWERSAVE)
+			cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
+		ret = amd_pstate_set_epp(policy,
+					 amd_pstate_get_balanced_epp(policy));
+		if (ret)
+			return ret;
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE);
+		if (ret)
+			return ret;
+		break;
+	default:
+		pr_err("Unknown Platform Profile %d\n", profile);
+		return -EOPNOTSUPP;
+	}
+
+	cpudata->current_profile = profile;
+
+	return 0;
+}
+
+static const struct platform_profile_ops amd_pstate_profile_ops = {
+	.probe = amd_pstate_profile_probe,
+	.profile_set = amd_pstate_profile_set,
+	.profile_get = amd_pstate_profile_get,
+};
+
 static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 
 	if (cpudata->power_nb.notifier_call)
 		power_supply_unreg_notifier(&cpudata->power_nb);
+	if (cpudata->ppdev) {
+		platform_profile_remove(cpudata->ppdev);
+		cpudata->ppdev = NULL;
+	}
+	kfree(cpudata->profile_name);
 	cpudata->dynamic_epp = false;
 }
 
@@ -1206,11 +1279,35 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 	u8 epp;
 
 	policy->policy = CPUFREQ_POLICY_PERFORMANCE;
-	epp = amd_pstate_get_balanced_epp(policy);
+	switch (cpudata->current_profile) {
+	case PLATFORM_PROFILE_PERFORMANCE:
+		epp = AMD_CPPC_EPP_PERFORMANCE;
+		break;
+	case PLATFORM_PROFILE_LOW_POWER:
+		epp = AMD_CPPC_EPP_POWERSAVE;
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		epp = amd_pstate_get_balanced_epp(policy);
+		break;
+	default:
+		pr_err("Unknown Platform Profile %d\n", cpudata->current_profile);
+		return -EOPNOTSUPP;
+	}
 	ret = amd_pstate_set_epp(policy, epp);
 	if (ret)
 		return ret;
 
+	cpudata->profile_name = kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", cpudata->cpu);
+
+	cpudata->ppdev = platform_profile_register(get_cpu_device(policy->cpu),
+						   cpudata->profile_name,
+						   policy->driver_data,
+						   &amd_pstate_profile_ops);
+	if (IS_ERR(cpudata->ppdev)) {
+		ret = PTR_ERR(cpudata->ppdev);
+		goto cleanup;
+	}
+
 	/* only enable notifier if things will actually change */
 	if (cpudata->epp_default_ac != cpudata->epp_default_dc) {
 		cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier;
@@ -1311,8 +1408,8 @@ static ssize_t show_energy_performance_available_preferences(
 	return offset;
 }
 
-static ssize_t store_energy_performance_preference(
-		struct cpufreq_policy *policy, const char *buf, size_t count)
+static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
+					   const char *buf, size_t count)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	ssize_t ret;
@@ -1332,7 +1429,7 @@ static ssize_t store_energy_performance_preference(
 	else
 		epp = amd_pstate_get_balanced_epp(policy);
 
-	if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
+	if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
 		pr_debug("EPP cannot be set under performance policy\n");
 		return -EBUSY;
 	}
@@ -1344,8 +1441,7 @@ static ssize_t store_energy_performance_preference(
 	return ret ? ret : count;
 }
 
-static ssize_t show_energy_performance_preference(
-				struct cpufreq_policy *policy, char *buf)
+static ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	u8 preference, epp;
@@ -1825,10 +1921,12 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 	    amd_pstate_acpi_pm_profile_undefined()) {
 		policy->policy = CPUFREQ_POLICY_PERFORMANCE;
 		cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);
+		cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
 	} else {
 		policy->policy = CPUFREQ_POLICY_POWERSAVE;
 		cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
 		cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
+		cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
 	}
 
 	if (dynamic_epp)
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index d929ae3163b3d..a7e52f79a8029 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -9,6 +9,7 @@
 #define _LINUX_AMD_PSTATE_H
 
 #include <linux/pm_qos.h>
+#include <linux/platform_profile.h>
 
 /*********************************************************************
  *                        AMD P-state INTERFACE                       *
@@ -127,6 +128,11 @@ struct amd_cpudata {
 	u8	epp_default_dc;
 	bool	dynamic_epp;
 	struct notifier_block power_nb;
+
+	/* platform profile */
+	enum platform_profile_option current_profile;
+	struct device *ppdev;
+	char *profile_name;
 };
 
 /*
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 4/5] cpufreq/amd-pstate: Add support for raw EPP writes
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)
In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org>

The energy performance preference field of the CPPC request MSR
supports values from 0 to 255, but the strings only offer 4 values.

The other values are useful for tuning the performance of some
workloads.

Add support for writing the raw energy performance preference value
to the sysfs file.  If the last value written was an integer then
an integer will be returned.  If the last value written was a string
then a string will be returned.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v5->v6:
 * Move the raw EPP parsing and readback changes into this patch
 * Use the kernel bool literal `false` for the raw EPP tracking flag
---
 Documentation/admin-guide/pm/amd-pstate.rst | 16 ++++++---
 drivers/cpufreq/amd-pstate.c                | 36 +++++++++++++++------
 drivers/cpufreq/amd-pstate.h                |  1 +
 3 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index 2d92c8072b83c..6ff2b477f0472 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -316,16 +316,22 @@ A list of all the supported EPP preferences that could be used for
 These profiles represent different hints that are provided
 to the low-level firmware about the user's desired energy vs efficiency
 tradeoff.  ``default`` represents the epp value is set by platform
-firmware. This attribute is read-only.
+firmware. ``custom`` designates that integer values 0-255 may be written
+as well.  This attribute is read-only.
 
 ``energy_performance_preference``
 
 The current energy performance preference can be read from this attribute.
 and user can change current preference according to energy or performance needs
-Please get all support profiles list from
-``energy_performance_available_preferences`` attribute, all the profiles are
-integer values defined between 0 to 255 when EPP feature is enabled by platform
-firmware, but if the dynamic EPP feature is enabled, driver will block writes.
+Coarse named profiles are available in the attribute
+``energy_performance_available_preferences``.
+Users can also write individual integer values between 0 to 255.
+When EPP feature is enabled by platform firmware but if the dynamic EPP feature is
+enabled, driver will ignore the written value. Lower epp values shift the bias
+towards improved performance while a higher epp value shifts the bias towards
+power-savings. The exact impact can change from one platform to the other.
+If a valid integer was last written, then a number will be returned on future reads.
+If a valid string was last written then a string will be returned on future reads.
 This attribute is read-write.
 
 ``boost``
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 93cda05ffa855..11fc992ea2da1 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -109,6 +109,7 @@ static struct quirk_entry *quirks;
  *	2		balance_performance
  *	3		balance_power
  *	4		power
+ *	5		custom (for raw EPP values)
  */
 enum energy_perf_value_index {
 	EPP_INDEX_DEFAULT = 0,
@@ -116,6 +117,7 @@ enum energy_perf_value_index {
 	EPP_INDEX_BALANCE_PERFORMANCE,
 	EPP_INDEX_BALANCE_POWERSAVE,
 	EPP_INDEX_POWERSAVE,
+	EPP_INDEX_CUSTOM,
 	EPP_INDEX_MAX,
 };
 
@@ -125,6 +127,7 @@ static const char * const energy_perf_strings[] = {
 	[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
 	[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
 	[EPP_INDEX_POWERSAVE] = "power",
+	[EPP_INDEX_CUSTOM] = "custom",
 };
 static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
 
@@ -135,7 +138,7 @@ static unsigned int epp_values[] = {
 	[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
 	[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
 };
-static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX);
+static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1);
 
 typedef int (*cppc_mode_transition_fn)(int);
 
@@ -1413,6 +1416,7 @@ static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	ssize_t ret;
+	bool raw_epp = false;
 	u8 epp;
 
 	if (cpudata->dynamic_epp) {
@@ -1420,14 +1424,21 @@ static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy
 		return -EBUSY;
 	}
 
-	ret = sysfs_match_string(energy_perf_strings, buf);
-	if (ret < 0)
-		return -EINVAL;
-
-	if (ret)
-		epp = epp_values[ret];
-	else
-		epp = amd_pstate_get_balanced_epp(policy);
+	/*
+	 * if the value matches a number, use that, otherwise see if
+	 * matches an index in the energy_perf_strings array
+	 */
+	ret = kstrtou8(buf, 0, &epp);
+	raw_epp = !ret;
+	if (ret) {
+		ret = sysfs_match_string(energy_perf_strings, buf);
+		if (ret < 0 || ret == EPP_INDEX_CUSTOM)
+			return -EINVAL;
+		if (ret)
+			epp = epp_values[ret];
+		else
+			epp = amd_pstate_get_balanced_epp(policy);
+	}
 
 	if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
 		pr_debug("EPP cannot be set under performance policy\n");
@@ -1438,7 +1449,9 @@ static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy
 	if (ret)
 		return ret;
 
-	return ret ? ret : count;
+	cpudata->raw_epp = raw_epp;
+
+	return count;
 }
 
 static ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
@@ -1448,6 +1461,9 @@ static ssize_t show_energy_performance_preference(struct cpufreq_policy *policy,
 
 	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
 
+	if (cpudata->raw_epp)
+		return sysfs_emit(buf, "%u\n", epp);
+
 	switch (epp) {
 	case AMD_CPPC_EPP_PERFORMANCE:
 		preference = EPP_INDEX_PERFORMANCE;
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index a7e52f79a8029..f7461d1b6bf3c 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -127,6 +127,7 @@ struct amd_cpudata {
 	u8	epp_default_ac;
 	u8	epp_default_dc;
 	bool	dynamic_epp;
+	bool	raw_epp;
 	struct notifier_block power_nb;
 
 	/* platform profile */
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 5/5] cpufreq/amd-pstate-ut: Add a unit test for raw EPP
From: Mario Limonciello (AMD) @ 2026-03-29 20:38 UTC (permalink / raw)
  To: Gautham R . Shenoy
  Cc: Perry Yuan, open list:X86 ARCHITECTURE (32-BIT AND 64-BIT),
	open list:CPU FREQUENCY SCALING FRAMEWORK,
	Mario Limonciello (AMD)
In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org>

Ensure that all supported raw EPP values work properly.

Export the driver helpers used by the test module so the test can drive
raw EPP writes and temporarily disable dynamic EPP while it runs.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
v5->v6:
 * Also verify that string EPP writes read back as strings
 * Save and restore the original driver mode and dynamic EPP state
 * Export the helper interfaces used by the unit test module
---
 drivers/cpufreq/amd-pstate-ut.c | 109 ++++++++++++++++++++++++++++++++
 drivers/cpufreq/amd-pstate.c    |  11 ++--
 drivers/cpufreq/amd-pstate.h    |   4 ++
 3 files changed, 120 insertions(+), 4 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
index 1f62ab6438b4e..aa8a464fab47a 100644
--- a/drivers/cpufreq/amd-pstate-ut.c
+++ b/drivers/cpufreq/amd-pstate-ut.c
@@ -28,6 +28,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/cleanup.h>
 
@@ -41,6 +42,7 @@ static char *test_list;
 module_param(test_list, charp, 0444);
 MODULE_PARM_DESC(test_list,
 	"Comma-delimited list of tests to run (empty means run all tests)");
+DEFINE_FREE(cleanup_page, void *, if (_T) free_page((unsigned long)_T))
 
 struct amd_pstate_ut_struct {
 	const char *name;
@@ -54,6 +56,7 @@ static int amd_pstate_ut_acpi_cpc_valid(u32 index);
 static int amd_pstate_ut_check_enabled(u32 index);
 static int amd_pstate_ut_check_perf(u32 index);
 static int amd_pstate_ut_check_freq(u32 index);
+static int amd_pstate_ut_epp(u32 index);
 static int amd_pstate_ut_check_driver(u32 index);
 static int amd_pstate_ut_check_freq_attrs(u32 index);
 
@@ -62,6 +65,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
 	{"amd_pstate_ut_check_enabled",     amd_pstate_ut_check_enabled    },
 	{"amd_pstate_ut_check_perf",        amd_pstate_ut_check_perf       },
 	{"amd_pstate_ut_check_freq",        amd_pstate_ut_check_freq       },
+	{"amd_pstate_ut_epp",               amd_pstate_ut_epp              },
 	{"amd_pstate_ut_check_driver",      amd_pstate_ut_check_driver     },
 	{"amd_pstate_ut_check_freq_attrs",  amd_pstate_ut_check_freq_attrs },
 };
@@ -268,6 +272,111 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
 	return amd_pstate_update_status(mode_str, strlen(mode_str));
 }
 
+static int amd_pstate_ut_epp(u32 index)
+{
+	struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
+	char *buf __free(cleanup_page) = NULL;
+	static const char * const epp_strings[] = {
+		"performance",
+		"balance_performance",
+		"balance_power",
+		"power",
+	};
+	struct amd_cpudata *cpudata;
+	enum amd_pstate_mode orig_mode;
+	bool orig_dynamic_epp;
+	int ret, cpu = 0;
+	int i;
+	u16 epp;
+
+	policy = cpufreq_cpu_get(cpu);
+	if (!policy)
+		return -ENODEV;
+
+	cpudata = policy->driver_data;
+	orig_mode = amd_pstate_get_status();
+	orig_dynamic_epp = cpudata->dynamic_epp;
+
+	/* disable dynamic EPP before running test */
+	if (cpudata->dynamic_epp) {
+		pr_debug("Dynamic EPP is enabled, disabling it\n");
+		amd_pstate_clear_dynamic_epp(policy);
+	}
+
+	buf = (char *)__get_free_page(GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = amd_pstate_set_mode(AMD_PSTATE_ACTIVE);
+	if (ret)
+		goto out;
+
+	for (epp = 0; epp <= U8_MAX; epp++) {
+		u8 val;
+
+		/* write all EPP values */
+		memset(buf, 0, PAGE_SIZE);
+		snprintf(buf, PAGE_SIZE, "%d", epp);
+		ret = store_energy_performance_preference(policy, buf, strlen(buf));
+		if (ret < 0)
+			goto out;
+
+		/* check if the EPP value reads back correctly for raw numbers */
+		memset(buf, 0, PAGE_SIZE);
+		ret = show_energy_performance_preference(policy, buf);
+		if (ret < 0)
+			goto out;
+		strreplace(buf, '\n', '\0');
+		ret = kstrtou8(buf, 0, &val);
+		if (!ret && epp != val) {
+			pr_err("Raw EPP value mismatch: %d != %d\n", epp, val);
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
+		memset(buf, 0, PAGE_SIZE);
+		snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
+		ret = store_energy_performance_preference(policy, buf, strlen(buf));
+		if (ret < 0)
+			goto out;
+
+		memset(buf, 0, PAGE_SIZE);
+		ret = show_energy_performance_preference(policy, buf);
+		if (ret < 0)
+			goto out;
+		strreplace(buf, '\n', '\0');
+
+		if (strcmp(buf, epp_strings[i])) {
+			pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	if (orig_dynamic_epp) {
+		int ret2;
+
+		ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE);
+		if (!ret && ret2)
+			ret = ret2;
+	}
+
+	if (orig_mode != amd_pstate_get_status()) {
+		int ret2;
+
+		ret2 = amd_pstate_set_mode(orig_mode);
+		if (!ret && ret2)
+			ret = ret2;
+	}
+
+	return ret;
+}
+
 static int amd_pstate_ut_check_driver(u32 index)
 {
 	enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 11fc992ea2da1..e246a1477cf78 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1261,7 +1261,7 @@ static const struct platform_profile_ops amd_pstate_profile_ops = {
 	.profile_get = amd_pstate_profile_get,
 };
 
-static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
+void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 
@@ -1274,6 +1274,7 @@ static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
 	kfree(cpudata->profile_name);
 	cpudata->dynamic_epp = false;
 }
+EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp);
 
 static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 {
@@ -1411,8 +1412,8 @@ static ssize_t show_energy_performance_available_preferences(
 	return offset;
 }
 
-static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
-					   const char *buf, size_t count)
+ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
+				    const char *buf, size_t count)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	ssize_t ret;
@@ -1453,8 +1454,9 @@ static ssize_t store_energy_performance_preference(struct cpufreq_policy *policy
 
 	return count;
 }
+EXPORT_SYMBOL_GPL(store_energy_performance_preference);
 
-static ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
+ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	u8 preference, epp;
@@ -1483,6 +1485,7 @@ static ssize_t show_energy_performance_preference(struct cpufreq_policy *policy,
 
 	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
 }
+EXPORT_SYMBOL_GPL(show_energy_performance_preference);
 
 static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
 					   const char *buf, size_t count)
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index f7461d1b6bf3c..e4722e54387b0 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -150,6 +150,10 @@ enum amd_pstate_mode {
 const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode);
 int amd_pstate_get_status(void);
 int amd_pstate_update_status(const char *buf, size_t size);
+ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
+				    const char *buf, size_t count);
+ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf);
+void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy);
 
 struct freq_attr;
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v2 0/2] dt-bindings: power: reset: cortina: Convert to DT schema and rename node
From: Khushal Chitturi @ 2026-03-29 20:51 UTC (permalink / raw)
  To: sre, robh, krzk+dt, conor+dt, ulli.kroll, linusw
  Cc: daniel.baluta, simona.toaca, d-gole, m-chawdhry, linux-pm,
	devicetree, linux-arm-kernel, linux-kernel, Khushal Chitturi

Convert the Cortina Systems Gemini Poweroff Controller bindings to
DT schema and update corresponding dtsi file with new node name

---
Khushal Chitturi (2):
  dt-bindings: power: reset: cortina,gemini-power-controller: convert to
    DT schema
  ARM: dts: gemini: Rename power controller node to gemini-poweroff

 .../cortina,gemini-power-controller.yaml      | 42 +++++++++++++++++++
 .../bindings/power/reset/gemini-poweroff.txt  | 17 --------
 arch/arm/boot/dts/gemini/gemini.dtsi          |  2 +-
 3 files changed, 43 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml
 delete mode 100644 Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt

-- 
2.53.0


^ permalink raw reply

* [PATCH v2 1/2] dt-bindings: power: reset: cortina,gemini-power-controller: convert to DT schema
From: Khushal Chitturi @ 2026-03-29 20:51 UTC (permalink / raw)
  To: sre, robh, krzk+dt, conor+dt, ulli.kroll, linusw
  Cc: daniel.baluta, simona.toaca, d-gole, m-chawdhry, linux-pm,
	devicetree, linux-arm-kernel, linux-kernel, Khushal Chitturi
In-Reply-To: <20260329205151.15161-1-khushalchitturi@gmail.com>

Convert the Cortina Systems Gemini Poweroff Controller bindings to
DT schema.

Signed-off-by: Khushal Chitturi <khushalchitturi@gmail.com>
---
Changelog:
v1 -> v2:
- Renamed the node from power-controller to gemini-poweroff to resolve dtschema warnings.

Note:
* This patch series is part of the GSoC2026 application process for device tree bindings conversions
* https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings

 .../cortina,gemini-power-controller.yaml      | 42 +++++++++++++++++++
 .../bindings/power/reset/gemini-poweroff.txt  | 17 --------
 2 files changed, 42 insertions(+), 17 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml
 delete mode 100644 Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt

diff --git a/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml b/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml
new file mode 100644
index 000000000000..8fbe7e952b25
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/reset/cortina,gemini-power-controller.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/reset/cortina,gemini-power-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cortina Systems Gemini Poweroff Controller
+
+maintainers:
+  - Linus Walleij <linusw@kernel.org>
+
+description: |
+  The Gemini power controller is a dedicated IP block in the Cortina Gemini SoC that
+  controls system power-down operations.
+
+properties:
+  compatible:
+    const: cortina,gemini-power-controller
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    gemini-poweroff@4b000000 {
+      compatible = "cortina,gemini-power-controller";
+      reg = <0x4b000000 0x100>;
+      interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+    };
+...
diff --git a/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt b/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt
deleted file mode 100644
index 7fec3e100214..000000000000
--- a/Documentation/devicetree/bindings/power/reset/gemini-poweroff.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-* Device-Tree bindings for Cortina Systems Gemini Poweroff
-
-This is a special IP block in the Cortina Gemini SoC that only
-deals with different ways to power the system down.
-
-Required properties:
-- compatible: should be "cortina,gemini-power-controller"
-- reg: should contain the physical memory base and size
-- interrupts: should contain the power management interrupt
-
-Example:
-
-power-controller@4b000000 {
-	compatible = "cortina,gemini-power-controller";
-	reg = <0x4b000000 0x100>;
-	interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
-};
-- 
2.53.0


^ permalink raw reply related


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