From: Mario Limonciello <superm1@kernel.org>
To: "Gautham R . Shenoy" <gautham.shenoy@amd.com>,
Perry Yuan <perry.yuan@amd.com>
Cc: Dhananjay Ugwekar <Dhananjay.Ugwekar@amd.com>,
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 <mario.limonciello@amd.com>
Subject: [PATCH 1/5] cpufreq/amd-pstate: Add dynamic energy performance preference
Date: Fri, 14 Feb 2025 18:54:44 -0600 [thread overview]
Message-ID: <20250215005448.1216646-2-superm1@kernel.org> (raw)
In-Reply-To: <20250215005448.1216646-1-superm1@kernel.org>
From: Mario Limonciello <mario.limonciello@amd.com>
Dynamic energy performance preference will change 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.
For non-server systems:
* the default EPP for AC mode is `balance_performance`.
* the default EPP for DC mode is `balance_power`.
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
---
Documentation/admin-guide/pm/amd-pstate.rst | 18 ++-
drivers/cpufreq/Kconfig.x86 | 12 ++
drivers/cpufreq/amd-pstate.c | 129 ++++++++++++++++++--
drivers/cpufreq/amd-pstate.h | 5 +-
4 files changed, 155 insertions(+), 9 deletions(-)
diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index 412423c54f258..2e076650dc77c 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -289,7 +289,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``
@@ -311,6 +311,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 97c2d4f15d76e..bfe957b653b05 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 y if X86_AMD_PSTATE
+ help
+ Allow the kenrel 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 aafd765c43f30..95cf8ac29b08c 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,7 @@ 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;
+static bool dynamic_epp = CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP;
static struct quirk_entry *quirks;
/*
@@ -1066,6 +1068,73 @@ static int amd_pstate_cpu_resume(struct cpufreq_policy *policy)
return amd_pstate_cppc_enable(policy);
}
+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;
+
+ 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) {
+ ret = power_supply_reg_notifier(&cpudata->power_nb);
+ if (ret)
+ goto cleanup;
+ cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier;
+ }
+
+ cpudata->dynamic_epp = true;
+
+ return 0;
+
+cleanup:
+ amd_pstate_clear_dynamic_epp(policy);
+
+ return ret;
+}
+
/* Sysfs attributes */
/*
@@ -1162,6 +1231,11 @@ 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 = sscanf(buf, "%20s", str_preference);
if (ret != 1)
return -EINVAL;
@@ -1170,10 +1244,10 @@ static ssize_t store_energy_performance_preference(
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");
@@ -1193,6 +1267,8 @@ static ssize_t store_energy_performance_preference(
}
ret = amd_pstate_set_epp(policy, epp);
+ if (ret)
+ return ret;
return ret ? ret : count;
}
@@ -1392,6 +1468,32 @@ 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;
+}
+
cpufreq_freq_attr_ro(amd_pstate_max_freq);
cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
@@ -1402,6 +1504,7 @@ cpufreq_freq_attr_rw(energy_performance_preference);
cpufreq_freq_attr_ro(energy_performance_available_preferences);
static DEVICE_ATTR_RW(status);
static DEVICE_ATTR_RO(prefcore);
+static DEVICE_ATTR_RW(dynamic_epp);
static struct freq_attr *amd_pstate_attr[] = {
&amd_pstate_max_freq,
@@ -1426,6 +1529,7 @@ static struct freq_attr *amd_pstate_epp_attr[] = {
static struct attribute *pstate_global_attributes[] = {
&dev_attr_status.attr,
&dev_attr_prefcore.attr,
+ &dev_attr_dynamic_epp.attr,
NULL
};
@@ -1518,10 +1622,11 @@ 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;
}
if (cpu_feature_enabled(X86_FEATURE_CPPC)) {
@@ -1530,9 +1635,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
return ret;
WRITE_ONCE(cpudata->cppc_req_cached, value);
}
- 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)
- return ret;
+ goto free_cpudata1;
current_pstate_driver->adjust_perf = NULL;
@@ -1549,6 +1658,8 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
struct amd_cpudata *cpudata = policy->driver_data;
if (cpudata) {
+ if (cpudata->dynamic_epp)
+ amd_pstate_clear_dynamic_epp(policy);
kfree(cpudata);
policy->driver_data = NULL;
}
@@ -1584,6 +1695,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 performance policy while dynamic epp is enabled */
+ if (policy->policy == CPUFREQ_POLICY_PERFORMANCE && cpudata->dynamic_epp)
+ return -EBUSY;
+
cpudata->policy = policy->policy;
ret = amd_pstate_epp_update_limit(policy);
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index 13918853f0a82..a3b70c635a7d7 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -102,7 +102,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
next prev parent reply other threads:[~2025-02-15 0:54 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-15 0:54 [PATCH 0/5] amd-pstate Dynamic EPP and raw EPP Mario Limonciello
2025-02-15 0:54 ` Mario Limonciello [this message]
2025-02-15 0:54 ` [PATCH 2/5] cpufreq/amd-pstate: add kernel command line to override dynamic epp Mario Limonciello
2025-02-15 0:54 ` [PATCH 3/5] cpufreq/amd-pstate: Add support for platform profile class Mario Limonciello
2025-02-15 0:54 ` [PATCH 4/5] cpufreq/amd-pstate: Add support for raw EPP writes Mario Limonciello
2025-02-15 0:54 ` [PATCH 5/5] cpufreq/amd-pstate-ut: Add a unit test for raw EPP Mario Limonciello
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250215005448.1216646-2-superm1@kernel.org \
--to=superm1@kernel.org \
--cc=Dhananjay.Ugwekar@amd.com \
--cc=gautham.shenoy@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=perry.yuan@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.