From nobody Thu Apr 2 10:44:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73CE5387354; Sun, 29 Mar 2026 20:38:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816705; cv=none; b=FBQcOoFdRerDDVhgmJwXx3KkXvZzAdcwdrIU8XhmCPA6Ld8T23+jHWsvKyKXQAMNmLKrvc7zUqrLBry5UN9193YiLScqYKJW0Uv0+9IxRp1mOFdCvmL/OYxI73gy51LbTTkmmF3jwMqui0sU6UexIScfMrv9cIIqjyiIOBH2Xpk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816705; c=relaxed/simple; bh=GCDcQLs0jsdAuh+3LifhC5fa5TSYaqzut23mEb80PrQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XfUa52NSkDfbFLuQC6XIoMMvmhcYvMyQKYIQ4nfwNqTbH1bi+/FBozFGDLTXHzYqxjVMkMng7ZE1D8QMBwQ4G2DlINLRO1Evimb6D952OdepREFsKsbJ8DW6yQSe2IelaMXOQKbFbLghkU/3ZXo6R6fcUWcC9ktwS0P2TkxO7yg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NjAC4INF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NjAC4INF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9E261C2BCB1; Sun, 29 Mar 2026 20:38:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774816705; bh=GCDcQLs0jsdAuh+3LifhC5fa5TSYaqzut23mEb80PrQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NjAC4INFHJbkX5BpEY970zkR9OPJUdfYK0c2Au5sj2X41ydmpei2QGyGIDdxuAc2e eh/E7TfvaTEAVKKgPyJ22fjKjsoNtIVuz4aSzPAWHvsWRY3qWLpprN2hgKUDn1h0r7 JQux6tfXxUxGZIyxts5mHJBXwOGuNvGlOeonTepTEi+4jQiNPnX3e1qStc1QxV5l9c gzIFQ1Yly9jtcHIaKtbLxO+TTu5xJsu/zUC5GFtlCuEp4523AdR/Lsr9NpB50vloD+ vWBnjCO+Uh+4cAfCcOi1LAC0NMrImlcnzrq6O0yN7RImReapL9leapu/WXW3mtz0wJ HuKk1sGKy87Bg== From: "Mario Limonciello (AMD)" To: "Gautham R . Shenoy" Cc: Perry Yuan , linux-kernel@vger.kernel.org (open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)), linux-pm@vger.kernel.org (open list:CPU FREQUENCY SCALING FRAMEWORK), "Mario Limonciello (AMD)" Subject: [PATCH v6 1/5] cpufreq/amd-pstate: Add dynamic energy performance preference Date: Sun, 29 Mar 2026 15:38:07 -0500 Message-ID: <20260329203811.2590633-2-superm1@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org> References: <20260329203811.2590633-1-superm1@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) --- 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/ad= min-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 ene= rgy or performance needs Please get all support profiles list from ``energy_performance_available_preferences`` attribute, all the profiles a= re integer values defined between 0 to 255 when EPP feature is enabled by pla= tform -firmware, if EPP feature is disabled, driver will ignore the written value +firmware, but if the dynamic EPP feature is enabled, driver will block wri= tes. This attribute is read-write. =20 ``boost`` @@ -347,6 +347,22 @@ boost or `1` to enable it, for the respective CPU usin= g the sysfs path Other performance and frequency values can be read back from ``/sys/devices/system/cpu/cpuX/acpi_cppc/``, see :ref:`cppc_sysfs`. =20 +Dynamic energy performance profile +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +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/dyn= amic_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 pr= ofile +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. =20 ``amd-pstate`` vs ``acpi-cpufreq`` =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 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: . =20 +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 #include #include +#include #include #include =20 @@ -86,6 +87,11 @@ static struct cpufreq_driver amd_pstate_driver; static struct cpufreq_driver amd_pstate_epp_driver; static int cppc_state =3D AMD_PSTATE_UNDEFINED; static bool amd_pstate_prefcore =3D true; +#ifdef CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP +static bool dynamic_epp =3D CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP; +#else +static bool dynamic_epp; +#endif static struct quirk_entry *quirks; =20 /* @@ -1155,6 +1161,74 @@ static void amd_pstate_cpu_exit(struct cpufreq_polic= y *policy) kfree(cpudata); } =20 +static int amd_pstate_get_balanced_epp(struct cpufreq_policy *policy) +{ + struct amd_cpudata *cpudata =3D 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 =3D container_of(nb, struct amd_cpudata, powe= r_nb); + struct cpufreq_policy *policy __free(put_cpufreq_policy) =3D cpufreq_cpu_= get(cpudata->cpu); + u8 epp; + int ret; + + if (event !=3D PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + epp =3D amd_pstate_get_balanced_epp(policy); + + ret =3D 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 =3D policy->driver_data; + + if (cpudata->power_nb.notifier_call) + power_supply_unreg_notifier(&cpudata->power_nb); + cpudata->dynamic_epp =3D false; +} + +static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) +{ + struct amd_cpudata *cpudata =3D policy->driver_data; + int ret; + u8 epp; + + policy->policy =3D CPUFREQ_POLICY_PERFORMANCE; + epp =3D amd_pstate_get_balanced_epp(policy); + ret =3D amd_pstate_set_epp(policy, epp); + if (ret) + return ret; + + /* only enable notifier if things will actually change */ + if (cpudata->epp_default_ac !=3D cpudata->epp_default_dc) { + cpudata->power_nb.notifier_call =3D amd_pstate_power_supply_notifier; + ret =3D power_supply_reg_notifier(&cpudata->power_nb); + if (ret) + goto cleanup; + } + + cpudata->dynamic_epp =3D true; + + return 0; + +cleanup: + amd_pstate_clear_dynamic_epp(policy); + + return ret; +} + /* Sysfs attributes */ =20 /* @@ -1244,14 +1318,19 @@ static ssize_t store_energy_performance_preference( ssize_t ret; u8 epp; =20 + if (cpudata->dynamic_epp) { + pr_debug("EPP cannot be set when dynamic EPP is enabled\n"); + return -EBUSY; + } + ret =3D sysfs_match_string(energy_perf_strings, buf); if (ret < 0) return -EINVAL; =20 - if (!ret) - epp =3D cpudata->epp_default; - else + if (ret) epp =3D epp_values[ret]; + else + epp =3D amd_pstate_get_balanced_epp(policy); =20 if (epp > 0 && policy->policy =3D=3D CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); @@ -1259,6 +1338,8 @@ static ssize_t store_energy_performance_preference( } =20 ret =3D amd_pstate_set_epp(policy, epp); + if (ret) + return ret; =20 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)); } =20 +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 =3D kstrtobool(buf, &enabled); + if (ret) + return ret; + + if (dynamic_epp =3D=3D enabled) + return -EINVAL; + + /* reinitialize with desired dynamic EPP value */ + dynamic_epp =3D enabled; + ret =3D 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); =20 static struct attribute *pstate_global_attributes[] =3D { &dev_attr_status.attr, &dev_attr_prefcore.attr, + &dev_attr_dynamic_epp.attr, NULL }; =20 @@ -1715,22 +1824,20 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_p= olicy *policy) if (amd_pstate_acpi_pm_profile_server() || amd_pstate_acpi_pm_profile_undefined()) { policy->policy =3D CPUFREQ_POLICY_PERFORMANCE; - cpudata->epp_default =3D amd_pstate_get_epp(cpudata); + cpudata->epp_default_ac =3D cpudata->epp_default_dc =3D amd_pstate_get_e= pp(cpudata); } else { policy->policy =3D CPUFREQ_POLICY_POWERSAVE; - cpudata->epp_default =3D AMD_CPPC_EPP_BALANCE_PERFORMANCE; + cpudata->epp_default_ac =3D AMD_CPPC_EPP_PERFORMANCE; + cpudata->epp_default_dc =3D AMD_CPPC_EPP_BALANCE_PERFORMANCE; } =20 - ret =3D amd_pstate_set_epp(policy, cpudata->epp_default); + if (dynamic_epp) + ret =3D amd_pstate_set_dynamic_epp(policy); + else + ret =3D amd_pstate_set_epp(policy, amd_pstate_get_balanced_epp(policy)); if (ret) goto free_cpudata1; =20 - ret =3D 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 =3D NULL; =20 return 0; @@ -1753,6 +1860,8 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_po= licy *policy) amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false); amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf); =20 + if (cpudata->dynamic_epp) + amd_pstate_clear_dynamic_epp(policy); kfree(cpudata); policy->driver_data =3D NULL; } @@ -1790,6 +1899,10 @@ static int amd_pstate_epp_set_policy(struct cpufreq_= policy *policy) if (!policy->cpuinfo.max_freq) return -ENODEV; =20 + /* policy can't be changed to powersave policy while dynamic epp is enabl= ed */ + if (policy->policy =3D=3D CPUFREQ_POLICY_POWERSAVE && cpudata->dynamic_ep= p) + return -EBUSY; + cpudata->policy =3D policy->policy; =20 ret =3D 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 ru= ntime. @@ -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; }; =20 /* --=20 2.43.0 From nobody Thu Apr 2 10:44:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 433ED388E62; Sun, 29 Mar 2026 20:38:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816706; cv=none; b=NqtkccGu7sAK2a1n9GUJk319ehhq62YJ2JGVSirS6ikjQU0ON9eV/1YNiSTyJ+Tsb0IV0dJexMncvjujNR5BK547yfcej7y+zZC3qLScbymqDETnMxd5D8lFPrxfbA3gz2aJ/oxbIeyWCq0VtkSfKy4vj4NUn3Ek/VBoXJDr4Fs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816706; c=relaxed/simple; bh=3LLOMCgcg9m85y4GEHxMexsyWkSoIqB5TrOTaxm4ln0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rE1PvaMdFuA2/XqnDISOPkyGBZedMgKn+RmIiRvOdQ5EAE4xiVWqNNpVsvq+WmdeXePwycU571L9ND7f0S0LJdUO9vMVjHe0/P6NqFpeYGnDJjw7RK4MdWzVEXoWGg5EDkgrZe2306+Tg4RPadaI6LnRH9BiXu1uOfPXjjt/vkg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eA/xR+x0; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="eA/xR+x0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6C691C19423; Sun, 29 Mar 2026 20:38:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774816706; bh=3LLOMCgcg9m85y4GEHxMexsyWkSoIqB5TrOTaxm4ln0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eA/xR+x0UWadYSw5tAH2wkgPAAoKuvj5GENu7+W8bnAFrKZN/xDKRKII1OOAwS+Ln p0IAbu8oaPUmJ+i0H8g/q6foBuoSTRI6du63FxDHL+Lcnfrncu7RVOxExGT+isw6iU d4qczHFEk63t8ePFFCyJFIh0S+HBMbfHgETNOlsSp2TZlT81LrZDgD9t1UYtTFgKly BZp8Qf4fWtsqCUcFx2p1E/93BUOZgW8fXauILrNdkIZf+T7PLocNUBs6dQs7v84QJ9 XiNUnhVu02FeBS8Jd7N545DHZLiv/QHWAit2YsYcJ7sLUVz/+5E6ZQROQ79F7+gWAK Y42Ox1r674vLw== From: "Mario Limonciello (AMD)" To: "Gautham R . Shenoy" Cc: Perry Yuan , linux-kernel@vger.kernel.org (open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)), linux-pm@vger.kernel.org (open list:CPU FREQUENCY SCALING FRAMEWORK), "Mario Limonciello (AMD)" Subject: [PATCH v6 2/5] cpufreq/amd-pstate: add kernel command line to override dynamic epp Date: Sun, 29 Mar 2026 15:38:08 -0500 Message-ID: <20260329203811.2590633-3-superm1@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org> References: <20260329203811.2590633-1-superm1@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add `amd_dynamic_epp=3Denable` and `amd_dynamic_epp=3Ddisable` to override the kernel configuration option `CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP` locally. Signed-off-by: Mario Limonciello (AMD) Reviewed-by: Gautham R. Shenoy --- 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/Documentatio= n/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. =20 + amd_dynamic_epp=3D + [X86] + disable + Disable amd-pstate dynamic EPP. + enable + Enable amd-pstate dynamic EPP. + amijoy.map=3D [HW,JOY] Amiga joystick support Map of devices attached to JOY0DAT and JOY1DAT Format: , diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/ad= min-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=3Ddisable``. =20 +``amd_dynamic_epp`` + +When AMD pstate is in auto mode, dynamic EPP will control whether the kern= el +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=3Denable`` or disabled with ``amd_dynamic_epp=3Ddisable`= `. + User Space Interface in ``sysfs`` - General =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 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; } =20 +static int __init amd_dynamic_epp_param(char *str) +{ + if (!strcmp(str, "disable")) + dynamic_epp =3D false; + if (!strcmp(str, "enable")) + dynamic_epp =3D 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); =20 MODULE_AUTHOR("Huang Rui "); MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver"); --=20 2.43.0 From nobody Thu Apr 2 10:44:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DECCA389479; Sun, 29 Mar 2026 20:38:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816706; cv=none; b=dyl48IpCgiA7GWbQnkjolNCJmuDB/JwHZR7OMHAmzcAsac5ADbx5Ud9Q0IhUxS0NL9Wu/nJxUMIYenTXkwIlB50UmA2Iq+nzepXKE35krTYwTpvujIt1B5Fn+W2O6ylYhR2Ad0TstfyFAHLESoYSn9KjPT49+Elzze1JYkE+4w4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816706; c=relaxed/simple; bh=AGsSTM+llb6mv1B4bpZ//DjsWmnGIovZaLNlN1yCTsE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IAJYmqIO2S2SXl6cyFYSFNheveSvMJ/gr3ubrCUhtOBaNz6TnKBW+TPbS6cVEY5cNVbVhqgYt9DFjRSIw2GVrb5VOcMHcATousFLBDCS41eGRkVUlCG/uIrMETGVqFZgBithsTrJ+QYD0+ORXbFB9lVDTAII8fYcRnbU9M87COw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XVOZRi5T; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="XVOZRi5T" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3ABE8C116C6; Sun, 29 Mar 2026 20:38:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774816706; bh=AGsSTM+llb6mv1B4bpZ//DjsWmnGIovZaLNlN1yCTsE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XVOZRi5TWQUN8BLeawHCV7ytkrr9FGdzclyhPZBbxbT28KwnCaWbhGXW1xl3pf49f RtCCJhtEWLwGKVEYbeQPhi1u7CV8O44LU8+nR4jQGGt4Aahl6McPX5L/A9D62iTZGc UGUIBWqMu6M/mhTWJC43jPDoXWmtOS8WyzYBrXPyZkQlKPUD8/5JeBWvFK0fNy9ZhR lDeJIlwY/CeY5gPnQ7qRxsdGuAj4sq5RR+Xe7T2jFj2JmZkxk12n+1wNbG/o0wDqju qcONUru1ACc+OttEpl6DRf9rrtSasAw9oc9SX+uau2CEn7zuyEiameKaCR7nfhZSTc jJPWzw8N+f10A== From: "Mario Limonciello (AMD)" To: "Gautham R . Shenoy" Cc: Perry Yuan , linux-kernel@vger.kernel.org (open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)), linux-pm@vger.kernel.org (open list:CPU FREQUENCY SCALING FRAMEWORK), "Mario Limonciello (AMD)" Subject: [PATCH v6 3/5] cpufreq/amd-pstate: Add support for platform profile class Date: Sun, 29 Mar 2026 15:38:09 -0500 Message-ID: <20260329203811.2590633-4-superm1@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org> References: <20260329203811.2590633-1-superm1@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) Reviewed-by: Gautham R. Shenoy --- 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/ad= min-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 ke= rnel config option at runtime by the sysfs file ``/sys/devices/system/cpu/cpufreq/policyX/dyn= amic_epp``. =20 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 pr= ofile based on the power source and will not react to user desired power state. =20 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 n= otifier_block *nb, if (event !=3D PSY_EVENT_PROP_CHANGED) return NOTIFY_OK; =20 + /* dynamic actions are only applied while platform profile is in balanced= */ + if (cpudata->current_profile !=3D PLATFORM_PROFILE_BALANCED) + return 0; + epp =3D amd_pstate_get_balanced_epp(policy); =20 ret =3D amd_pstate_set_epp(policy, epp); @@ -1190,12 +1194,81 @@ static int amd_pstate_power_supply_notifier(struct = notifier_block *nb, =20 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 =3D dev_get_drvdata(dev); + + *profile =3D cpudata->current_profile; + + return 0; +} + +static int amd_pstate_profile_set(struct device *dev, + enum platform_profile_option profile) +{ + struct amd_cpudata *cpudata =3D dev_get_drvdata(dev); + struct cpufreq_policy *policy __free(put_cpufreq_policy) =3D cpufreq_cpu_= get(cpudata->cpu); + int ret; + + switch (profile) { + case PLATFORM_PROFILE_LOW_POWER: + if (cpudata->policy !=3D CPUFREQ_POLICY_POWERSAVE) + cpudata->policy =3D CPUFREQ_POLICY_POWERSAVE; + ret =3D amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); + if (ret) + return ret; + break; + case PLATFORM_PROFILE_BALANCED: + if (cpudata->policy !=3D CPUFREQ_POLICY_POWERSAVE) + cpudata->policy =3D CPUFREQ_POLICY_POWERSAVE; + ret =3D amd_pstate_set_epp(policy, + amd_pstate_get_balanced_epp(policy)); + if (ret) + return ret; + break; + case PLATFORM_PROFILE_PERFORMANCE: + ret =3D 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 =3D profile; + + return 0; +} + +static const struct platform_profile_ops amd_pstate_profile_ops =3D { + .probe =3D amd_pstate_profile_probe, + .profile_set =3D amd_pstate_profile_set, + .profile_get =3D amd_pstate_profile_get, +}; + static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy) { struct amd_cpudata *cpudata =3D policy->driver_data; =20 if (cpudata->power_nb.notifier_call) power_supply_unreg_notifier(&cpudata->power_nb); + if (cpudata->ppdev) { + platform_profile_remove(cpudata->ppdev); + cpudata->ppdev =3D NULL; + } + kfree(cpudata->profile_name); cpudata->dynamic_epp =3D false; } =20 @@ -1206,11 +1279,35 @@ static int amd_pstate_set_dynamic_epp(struct cpufre= q_policy *policy) u8 epp; =20 policy->policy =3D CPUFREQ_POLICY_PERFORMANCE; - epp =3D amd_pstate_get_balanced_epp(policy); + switch (cpudata->current_profile) { + case PLATFORM_PROFILE_PERFORMANCE: + epp =3D AMD_CPPC_EPP_PERFORMANCE; + break; + case PLATFORM_PROFILE_LOW_POWER: + epp =3D AMD_CPPC_EPP_POWERSAVE; + break; + case PLATFORM_PROFILE_BALANCED: + epp =3D amd_pstate_get_balanced_epp(policy); + break; + default: + pr_err("Unknown Platform Profile %d\n", cpudata->current_profile); + return -EOPNOTSUPP; + } ret =3D amd_pstate_set_epp(policy, epp); if (ret) return ret; =20 + cpudata->profile_name =3D kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", c= pudata->cpu); + + cpudata->ppdev =3D platform_profile_register(get_cpu_device(policy->cpu), + cpudata->profile_name, + policy->driver_data, + &amd_pstate_profile_ops); + if (IS_ERR(cpudata->ppdev)) { + ret =3D PTR_ERR(cpudata->ppdev); + goto cleanup; + } + /* only enable notifier if things will actually change */ if (cpudata->epp_default_ac !=3D cpudata->epp_default_dc) { cpudata->power_nb.notifier_call =3D amd_pstate_power_supply_notifier; @@ -1311,8 +1408,8 @@ static ssize_t show_energy_performance_available_pref= erences( return offset; } =20 -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 =3D policy->driver_data; ssize_t ret; @@ -1332,7 +1429,7 @@ static ssize_t store_energy_performance_preference( else epp =3D amd_pstate_get_balanced_epp(policy); =20 - if (epp > 0 && policy->policy =3D=3D CPUFREQ_POLICY_PERFORMANCE) { + if (epp > 0 && cpudata->policy =3D=3D 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; } =20 -static ssize_t show_energy_performance_preference( - struct cpufreq_policy *policy, char *buf) +static ssize_t show_energy_performance_preference(struct cpufreq_policy *p= olicy, char *buf) { struct amd_cpudata *cpudata =3D policy->driver_data; u8 preference, epp; @@ -1825,10 +1921,12 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_p= olicy *policy) amd_pstate_acpi_pm_profile_undefined()) { policy->policy =3D CPUFREQ_POLICY_PERFORMANCE; cpudata->epp_default_ac =3D cpudata->epp_default_dc =3D amd_pstate_get_e= pp(cpudata); + cpudata->current_profile =3D PLATFORM_PROFILE_PERFORMANCE; } else { policy->policy =3D CPUFREQ_POLICY_POWERSAVE; cpudata->epp_default_ac =3D AMD_CPPC_EPP_PERFORMANCE; cpudata->epp_default_dc =3D AMD_CPPC_EPP_BALANCE_PERFORMANCE; + cpudata->current_profile =3D PLATFORM_PROFILE_BALANCED; } =20 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 =20 #include +#include =20 /********************************************************************* * 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; }; =20 /* --=20 2.43.0 From nobody Thu Apr 2 10:44:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE84F38A706; Sun, 29 Mar 2026 20:38:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816707; cv=none; b=Rh8cu3AJy9o9ga+NnR8l6zxz/QUoH0lpbMw3EXH3Yisa1Zl5GPvGxWMJgyzEuJVidBbnKVU9tnpRiOxQn+30Xb7Q+tKGgTQugUztsF51u+5NNyYqCdvKoWSuHKSS4xWcz2jSdu40oq/4P1csxi5OgXM85mT9idiJrvhOi4Dr46w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816707; c=relaxed/simple; bh=ThuZB87gnJHY06bv+wwV6xDhV0Cusiyc38dwq5LKW9Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JkQ6nK76AsJXUhNL9RnWrZWujuP4veGEppRmrbgPYytYt0uHUO1Hug2vjD7DgLMGEnNjyArZ4HMZXtcJyS302Tw1UvBhnd4ws6Ca8OhOHuoG5kIczeRsllBZZwkGeBRFBjvpPncu+q6aWiBCyoDbmFJY9tuR9JVhvzx/yAVnJwQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=r2iBRzXh; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r2iBRzXh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0B7A7C116C6; Sun, 29 Mar 2026 20:38:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774816707; bh=ThuZB87gnJHY06bv+wwV6xDhV0Cusiyc38dwq5LKW9Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=r2iBRzXhdNIbaNtagYXFMoaRQKNRHr5XdofueSHBwVAAywq5NdIFTFj2l01ifIrcf AQs4cPK6uPfzSmk6QnH+fbZmmv1wTKdAbPSt2fM/qABvL8bzL39YvV68yBLUDWX18U mu5z7ou31FJV7ifcpzU0M8FJDeTHo6AkSrRH3dSYLDTT3KtMhsF4fB+/WrmhFFWanM 2qryA0mx8Q+w1pfSU6akRw9yv9CvMrd/Wy30wuj+3xeyztGAG+PyfWvM9WVN4Xpa69 bF8ol7YTEeR/zNPAYvGAFFXOGOVkStvVKY+Om/GoLIeroqh4jGTFrB2+X0I+UOLIk7 VPIGxuXmmepOA== From: "Mario Limonciello (AMD)" To: "Gautham R . Shenoy" Cc: Perry Yuan , linux-kernel@vger.kernel.org (open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)), linux-pm@vger.kernel.org (open list:CPU FREQUENCY SCALING FRAMEWORK), "Mario Limonciello (AMD)" Subject: [PATCH v6 4/5] cpufreq/amd-pstate: Add support for raw EPP writes Date: Sun, 29 Mar 2026 15:38:10 -0500 Message-ID: <20260329203811.2590633-5-superm1@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org> References: <20260329203811.2590633-1-superm1@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) Reviewed-by: Gautham R. Shenoy --- 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/ad= min-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 coul= d 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. =20 ``energy_performance_preference`` =20 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 a= re -integer values defined between 0 to 255 when EPP feature is enabled by pla= tform -firmware, but if the dynamic EPP feature is enabled, driver will block wri= tes. +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 fe= ature 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 towa= rds +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 fut= ure reads. +If a valid string was last written then a string will be returned on futur= e reads. This attribute is read-write. =20 ``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 =3D 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, }; =20 @@ -125,6 +127,7 @@ static const char * const energy_perf_strings[] =3D { [EPP_INDEX_BALANCE_PERFORMANCE] =3D "balance_performance", [EPP_INDEX_BALANCE_POWERSAVE] =3D "balance_power", [EPP_INDEX_POWERSAVE] =3D "power", + [EPP_INDEX_CUSTOM] =3D "custom", }; static_assert(ARRAY_SIZE(energy_perf_strings) =3D=3D EPP_INDEX_MAX); =20 @@ -135,7 +138,7 @@ static unsigned int epp_values[] =3D { [EPP_INDEX_BALANCE_POWERSAVE] =3D AMD_CPPC_EPP_BALANCE_POWERSAVE, [EPP_INDEX_POWERSAVE] =3D AMD_CPPC_EPP_POWERSAVE, }; -static_assert(ARRAY_SIZE(epp_values) =3D=3D EPP_INDEX_MAX); +static_assert(ARRAY_SIZE(epp_values) =3D=3D EPP_INDEX_MAX - 1); =20 typedef int (*cppc_mode_transition_fn)(int); =20 @@ -1413,6 +1416,7 @@ static ssize_t store_energy_performance_preference(st= ruct cpufreq_policy *policy { struct amd_cpudata *cpudata =3D policy->driver_data; ssize_t ret; + bool raw_epp =3D false; u8 epp; =20 if (cpudata->dynamic_epp) { @@ -1420,14 +1424,21 @@ static ssize_t store_energy_performance_preference(= struct cpufreq_policy *policy return -EBUSY; } =20 - ret =3D sysfs_match_string(energy_perf_strings, buf); - if (ret < 0) - return -EINVAL; - - if (ret) - epp =3D epp_values[ret]; - else - epp =3D 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 =3D kstrtou8(buf, 0, &epp); + raw_epp =3D !ret; + if (ret) { + ret =3D sysfs_match_string(energy_perf_strings, buf); + if (ret < 0 || ret =3D=3D EPP_INDEX_CUSTOM) + return -EINVAL; + if (ret) + epp =3D epp_values[ret]; + else + epp =3D amd_pstate_get_balanced_epp(policy); + } =20 if (epp > 0 && cpudata->policy =3D=3D CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); @@ -1438,7 +1449,9 @@ static ssize_t store_energy_performance_preference(st= ruct cpufreq_policy *policy if (ret) return ret; =20 - return ret ? ret : count; + cpudata->raw_epp =3D raw_epp; + + return count; } =20 static ssize_t show_energy_performance_preference(struct cpufreq_policy *p= olicy, char *buf) @@ -1448,6 +1461,9 @@ static ssize_t show_energy_performance_preference(str= uct cpufreq_policy *policy, =20 epp =3D FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached); =20 + if (cpudata->raw_epp) + return sysfs_emit(buf, "%u\n", epp); + switch (epp) { case AMD_CPPC_EPP_PERFORMANCE: preference =3D 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; =20 /* platform profile */ --=20 2.43.0 From nobody Thu Apr 2 10:44:29 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A01CF388E7B; Sun, 29 Mar 2026 20:38:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816708; cv=none; b=YFh7TC6NJjJvcI2a5FOK8ciPn+ntu9BDtR271dXOX8/8xZxx/WnIYgq+iv/QU7/xhJpJKwEG2zNkyLoytjBjwrFahF+QMuSgGibU61BEGmQ9YI3M25d6EP34ZyT8DdyakxFlKUwE8l5D77zBDnTZDLt942HjvJn2NHtLFzsb/O4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774816708; c=relaxed/simple; bh=rnV0WqfdBI6WTFndB/QrgIZUFNrsc3EjvCCMJsqAQcs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W+IDrJ5ZthO+3UWi5HfPcTEiRAK1dYKEA9tod+eY/5+sQBuhcgZCq9HG4ihyLMlRaJY1PSFoezI8CTOt5+4o5u6omhNpFBi4cUtBJuA1pMGAkKJFu7GSh0geRL+GpGYa29BbnhMmwNR9IQUBozglM1Ci5oqjhfycl+81JjH/ijk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=i56KkGQX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="i56KkGQX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D57ECC2BCB2; Sun, 29 Mar 2026 20:38:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774816708; bh=rnV0WqfdBI6WTFndB/QrgIZUFNrsc3EjvCCMJsqAQcs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i56KkGQXB/2ZR2aAkU+dY3By3ygLm5nLNG8MpoxScYLutulJmKZtijXzT7zdrvrW5 N1MpPLFvmb4z187BIRE52+7CIUCYiVtvKkwBYOjTlurK5HxCBALCLVRgi0tg802uBL xJmuEnDdf5gCxcV35hQU6oU4g/xZMmSr7E2W3REBn3xL/RKwpMAbnMSfom9zUO3aRF T8ZoLfXuoss0GL6H83JRO/pkn1daIIgjJVmTWFOwkQCF+RaTXHjUI6iLsD+0ugFNwl 2M4bg2+MrUwqgOXpSjXqALVRZcRdPP7e8r2GS4Zq9vcOQQSJzEOjjtTaihMdI4RVZj Epae57DUr6dzg== From: "Mario Limonciello (AMD)" To: "Gautham R . Shenoy" Cc: Perry Yuan , linux-kernel@vger.kernel.org (open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)), linux-pm@vger.kernel.org (open list:CPU FREQUENCY SCALING FRAMEWORK), "Mario Limonciello (AMD)" Subject: [PATCH v6 5/5] cpufreq/amd-pstate-ut: Add a unit test for raw EPP Date: Sun, 29 Mar 2026 15:38:11 -0500 Message-ID: <20260329203811.2590633-6-superm1@kernel.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329203811.2590633-1-superm1@kernel.org> References: <20260329203811.2590633-1-superm1@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) Reviewed-by: Gautham R. Shenoy --- 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-u= t.c index 1f62ab6438b4e..aa8a464fab47a 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include =20 @@ -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)) =20 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); =20 @@ -62,6 +65,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = =3D { {"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 m= ode) return amd_pstate_update_status(mode_str, strlen(mode_str)); } =20 +static int amd_pstate_ut_epp(u32 index) +{ + struct cpufreq_policy *policy __free(put_cpufreq_policy) =3D NULL; + char *buf __free(cleanup_page) =3D NULL; + static const char * const epp_strings[] =3D { + "performance", + "balance_performance", + "balance_power", + "power", + }; + struct amd_cpudata *cpudata; + enum amd_pstate_mode orig_mode; + bool orig_dynamic_epp; + int ret, cpu =3D 0; + int i; + u16 epp; + + policy =3D cpufreq_cpu_get(cpu); + if (!policy) + return -ENODEV; + + cpudata =3D policy->driver_data; + orig_mode =3D amd_pstate_get_status(); + orig_dynamic_epp =3D 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 =3D (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret =3D amd_pstate_set_mode(AMD_PSTATE_ACTIVE); + if (ret) + goto out; + + for (epp =3D 0; epp <=3D U8_MAX; epp++) { + u8 val; + + /* write all EPP values */ + memset(buf, 0, PAGE_SIZE); + snprintf(buf, PAGE_SIZE, "%d", epp); + ret =3D 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 =3D show_energy_performance_preference(policy, buf); + if (ret < 0) + goto out; + strreplace(buf, '\n', '\0'); + ret =3D kstrtou8(buf, 0, &val); + if (!ret && epp !=3D val) { + pr_err("Raw EPP value mismatch: %d !=3D %d\n", epp, val); + ret =3D -EINVAL; + goto out; + } + } + + for (i =3D 0; i < ARRAY_SIZE(epp_strings); i++) { + memset(buf, 0, PAGE_SIZE); + snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]); + ret =3D store_energy_performance_preference(policy, buf, strlen(buf)); + if (ret < 0) + goto out; + + memset(buf, 0, PAGE_SIZE); + ret =3D 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 !=3D %s\n", buf, epp_strings[i]); + ret =3D -EINVAL; + goto out; + } + } + + ret =3D 0; + +out: + if (orig_dynamic_epp) { + int ret2; + + ret2 =3D amd_pstate_set_mode(AMD_PSTATE_DISABLE); + if (!ret && ret2) + ret =3D ret2; + } + + if (orig_mode !=3D amd_pstate_get_status()) { + int ret2; + + ret2 =3D amd_pstate_set_mode(orig_mode); + if (!ret && ret2) + ret =3D ret2; + } + + return ret; +} + static int amd_pstate_ut_check_driver(u32 index) { enum amd_pstate_mode mode1, mode2 =3D 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_p= rofile_ops =3D { .profile_get =3D amd_pstate_profile_get, }; =20 -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 =3D policy->driver_data; =20 @@ -1274,6 +1274,7 @@ static void amd_pstate_clear_dynamic_epp(struct cpufr= eq_policy *policy) kfree(cpudata->profile_name); cpudata->dynamic_epp =3D false; } +EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp); =20 static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) { @@ -1411,8 +1412,8 @@ static ssize_t show_energy_performance_available_pref= erences( return offset; } =20 -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 =3D policy->driver_data; ssize_t ret; @@ -1453,8 +1454,9 @@ static ssize_t store_energy_performance_preference(st= ruct cpufreq_policy *policy =20 return count; } +EXPORT_SYMBOL_GPL(store_energy_performance_preference); =20 -static ssize_t show_energy_performance_preference(struct cpufreq_policy *p= olicy, char *buf) +ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, = char *buf) { struct amd_cpudata *cpudata =3D policy->driver_data; u8 preference, epp; @@ -1483,6 +1485,7 @@ static ssize_t show_energy_performance_preference(str= uct cpufreq_policy *policy, =20 return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); } +EXPORT_SYMBOL_GPL(show_energy_performance_preference); =20 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); =20 struct freq_attr; =20 --=20 2.43.0