mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
cpufreq/amd-pstate: Add dynamic energy performance preference
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 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.
Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
This commit is contained in:
parent
a362ae6e7e
commit
e30ca6dd53
|
|
@ -325,7 +325,7 @@ and user can change current preference according to energy or performance needs
|
||||||
Please get all support profiles list from
|
Please get all support profiles list from
|
||||||
``energy_performance_available_preferences`` attribute, all the profiles are
|
``energy_performance_available_preferences`` attribute, all the profiles are
|
||||||
integer values defined between 0 to 255 when EPP feature is enabled by platform
|
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.
|
This attribute is read-write.
|
||||||
|
|
||||||
``boost``
|
``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
|
Other performance and frequency values can be read back from
|
||||||
``/sys/devices/system/cpu/cpuX/acpi_cppc/``, see :ref:`cppc_sysfs`.
|
``/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 depends on 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``
|
``amd-pstate`` vs ``acpi-cpufreq``
|
||||||
======================================
|
======================================
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,18 @@ config X86_AMD_PSTATE_DEFAULT_MODE
|
||||||
For details, take a look at:
|
For details, take a look at:
|
||||||
<file:Documentation/admin-guide/pm/amd-pstate.rst>.
|
<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
|
config X86_AMD_PSTATE_UT
|
||||||
tristate "selftest for AMD Processor P-State driver"
|
tristate "selftest for AMD Processor P-State driver"
|
||||||
depends on X86 && ACPI_PROCESSOR
|
depends on X86 && ACPI_PROCESSOR
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/power_supply.h>
|
||||||
#include <linux/static_call.h>
|
#include <linux/static_call.h>
|
||||||
#include <linux/topology.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 struct cpufreq_driver amd_pstate_epp_driver;
|
||||||
static int cppc_state = AMD_PSTATE_UNDEFINED;
|
static int cppc_state = AMD_PSTATE_UNDEFINED;
|
||||||
static bool amd_pstate_prefcore = true;
|
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;
|
static struct quirk_entry *quirks;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1155,6 +1161,73 @@ static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
|
||||||
kfree(cpudata);
|
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;
|
||||||
|
|
||||||
|
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 */
|
/* Sysfs attributes */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1244,14 +1317,19 @@ static ssize_t store_energy_performance_preference(
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
u8 epp;
|
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);
|
ret = sysfs_match_string(energy_perf_strings, buf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!ret)
|
if (ret)
|
||||||
epp = cpudata->epp_default;
|
|
||||||
else
|
|
||||||
epp = epp_values[ret];
|
epp = epp_values[ret];
|
||||||
|
else
|
||||||
|
epp = amd_pstate_get_balanced_epp(policy);
|
||||||
|
|
||||||
if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
|
if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
|
||||||
pr_debug("EPP cannot be set under performance policy\n");
|
pr_debug("EPP cannot be set under performance policy\n");
|
||||||
|
|
@ -1259,6 +1337,8 @@ static ssize_t store_energy_performance_preference(
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = amd_pstate_set_epp(policy, epp);
|
ret = amd_pstate_set_epp(policy, epp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return ret ? ret : count;
|
return ret ? ret : count;
|
||||||
}
|
}
|
||||||
|
|
@ -1620,12 +1700,42 @@ static ssize_t prefcore_show(struct device *dev,
|
||||||
return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
|
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);
|
||||||
|
if (ret)
|
||||||
|
dynamic_epp = false;
|
||||||
|
|
||||||
|
return ret ? ret : count;
|
||||||
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR_RW(status);
|
static DEVICE_ATTR_RW(status);
|
||||||
static DEVICE_ATTR_RO(prefcore);
|
static DEVICE_ATTR_RO(prefcore);
|
||||||
|
static DEVICE_ATTR_RW(dynamic_epp);
|
||||||
|
|
||||||
static struct attribute *pstate_global_attributes[] = {
|
static struct attribute *pstate_global_attributes[] = {
|
||||||
&dev_attr_status.attr,
|
&dev_attr_status.attr,
|
||||||
&dev_attr_prefcore.attr,
|
&dev_attr_prefcore.attr,
|
||||||
|
&dev_attr_dynamic_epp.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1715,13 +1825,17 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
|
||||||
if (amd_pstate_acpi_pm_profile_server() ||
|
if (amd_pstate_acpi_pm_profile_server() ||
|
||||||
amd_pstate_acpi_pm_profile_undefined()) {
|
amd_pstate_acpi_pm_profile_undefined()) {
|
||||||
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
|
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 {
|
} else {
|
||||||
policy->policy = CPUFREQ_POLICY_POWERSAVE;
|
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)
|
if (ret)
|
||||||
goto free_cpudata1;
|
goto free_cpudata1;
|
||||||
|
|
||||||
|
|
@ -1753,6 +1867,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_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
|
||||||
amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
|
amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
|
||||||
|
|
||||||
|
if (cpudata->dynamic_epp)
|
||||||
|
amd_pstate_clear_dynamic_epp(policy);
|
||||||
kfree(cpudata);
|
kfree(cpudata);
|
||||||
policy->driver_data = NULL;
|
policy->driver_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,11 @@ struct amd_aperf_mperf {
|
||||||
* AMD P-State driver supports preferred core featue.
|
* AMD P-State driver supports preferred core featue.
|
||||||
* @epp_cached: Cached CPPC energy-performance preference value
|
* @epp_cached: Cached CPPC energy-performance preference value
|
||||||
* @policy: Cpufreq policy 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
|
* 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.
|
* represents all the attributes and goals that AMD P-State requests at runtime.
|
||||||
|
|
@ -118,7 +123,10 @@ struct amd_cpudata {
|
||||||
/* EPP feature related attributes*/
|
/* EPP feature related attributes*/
|
||||||
u32 policy;
|
u32 policy;
|
||||||
bool suspended;
|
bool suspended;
|
||||||
u8 epp_default;
|
u8 epp_default_ac;
|
||||||
|
u8 epp_default_dc;
|
||||||
|
bool dynamic_epp;
|
||||||
|
struct notifier_block power_nb;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user