Merge branch 'pm-cpufreq'

Merge cpufreq updates for 7.4:

 - Minor fixes in various cpufreq drivers (Alexander Koskovich, Daniel
   Schwierzeck, Frank Zhang, Haotian Zhang, Kathiravan Thirumoorthy,
   Sumeet Pawnikar, and Xueqin Luo)

 - Fix wrong behaviors on multi-node servers in the Loongson cpufreq
   driver (Huacai Chen)

 - Reject NULL from cpufreq_cpu_get() in the Rust code (Mehmet Koseoglu)

 - Add suspend OPP to mediatek cpufreq driver and DT (Ryan Brue)

 - Fixups for a few bugs identified by Sashiko in the amd-pstate cpufreq
   driver (Mario Limoncielloi, Wentao Guan)

 - Initial support for maximum frequency capacity handling on Zen6
   client in amd-pstate (Mario Limonciello)

 - Fix for amd-pstate-ut with 'Requested CPU Min frequency' BIOS option
   (K Prateek Nayak)

 - New Zen6 tuning values for EPP in amd-pstate (Mario Limonciello,
   Vishal Badole)

 - amd-pstate documentation fix (Karl Mehltretter)

 - Restrict deferred downscaling to low load for shared policies in the
   conservative cpufreq governor to avoid undesirable CPU frequency
   reduction in some cases (Shengming Hu)

 - Serialize sugov_start() against rate limit updates in the schedutil
   cpufreq governor to avoid using a stale rate limit value (Hui Su)

* pm-cpufreq: (34 commits)
  cpufreq: loongson3: Replace IOCSR read/write with MMIO ones
  cpufreq: loongson3: Use global physical CPU ID in get/target callbacks
  cpufreq: loongson3: Replace per-package mutex with per-node
  cpufreq: loongson3: Adjust the width of id and val in smc_message
  cpufreq: loongson3: Make this driver depend on MACH_LOONGSON64
  cpufreq: schedutil: Serialize sugov_start() against rate limit updates
  arm64: dts: mediatek: mt8173: Mark the lowest CPU OPPs for suspend
  cpufreq: mediatek: Enter system sleep at the suspend OPP
  cpufreq: qcom-cpufreq-hw: fix device node refcount leak in phandle parsing
  cpufreq: qcom-nvmem: Add IPQ9650 support
  cpufreq: Add Milos to cpufreq-dt-platdev blocklist
  cpufreq: Use %pe to print error pointers symbolically
  rust: rcpufreq_dt: add module alias
  cpufreq: sti: avoid NULL dereference in dev_err()
  cpufreq: tegra194: fix double-pointer error in get_cpu_ndiv
  cpufreq: sparc-us2e: fix frequency table index copy-paste error
  rust: cpufreq: reject NULL from cpufreq_cpu_get()
  cpufreq: amd-pstate-ut: Fix max_freq and EPP test failures on Zen6
  cpufreq: amd-pstate: Propagate cppc_set_auto_sel() errors on mode change
  cpufreq: amd-pstate: Restore previous mode when changing driver mode fails
  ...
This commit is contained in:
Rafael J. Wysocki 2026-10-19 14:20:58 +02:00
commit eece15c579
29 changed files with 607 additions and 260 deletions

View File

@ -501,13 +501,6 @@ 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>

View File

@ -481,12 +481,6 @@ 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 disabled. It can be enabled
with the kernel parameter ``amd_dynamic_epp=enable``.
User Space Interface in ``sysfs`` - General
===========================================

View File

@ -56,6 +56,7 @@ cluster0_opp: opp-table-0 {
opp-507000000 {
opp-hz = /bits/ 64 <507000000>;
opp-microvolt = <859000>;
opp-suspend;
};
opp-702000000 {
opp-hz = /bits/ 64 <702000000>;
@ -93,6 +94,7 @@ cluster1_opp: opp-table-1 {
opp-507000000 {
opp-hz = /bits/ 64 <507000000>;
opp-microvolt = <828000>;
opp-suspend;
};
opp-702000000 {
opp-hz = /bits/ 64 <702000000>;

View File

@ -11,6 +11,7 @@
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/topology.h>
#include <asm/cpu_device_id.h>
#define CPPC_HIGHEST_PERF_PERFORMANCE 196
#define CPPC_HIGHEST_PERF_PREFCORE 166
@ -95,31 +96,18 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
static void amd_set_max_freq_ratio(void)
{
struct cppc_perf_caps perf_caps;
u64 numerator, nominal_perf;
u64 numerator, denominator;
u64 perf_ratio;
int rc;
rc = cppc_get_perf_caps(0, &perf_caps);
rc = amd_get_boost_ratio(0, &numerator, &denominator);
if (rc) {
pr_debug("Could not retrieve perf counters (%d)\n", rc);
return;
}
rc = amd_get_boost_ratio_numerator(0, &numerator);
if (rc) {
pr_debug("Could not retrieve highest performance (%d)\n", rc);
return;
}
nominal_perf = perf_caps.nominal_perf;
if (!nominal_perf) {
pr_debug("Could not retrieve nominal performance\n");
pr_debug("Could not retrieve boost ratio (%d)\n", rc);
return;
}
/* midpoint between max_boost and max_P */
perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, nominal_perf) + SCHED_CAPACITY_SCALE) >> 1;
perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, denominator) + SCHED_CAPACITY_SCALE) >> 1;
freq_invariance_set_perf_ratio(perf_ratio, false);
}
@ -239,35 +227,30 @@ int amd_detect_prefcore(bool *detected)
EXPORT_SYMBOL_GPL(amd_detect_prefcore);
/**
* amd_get_boost_ratio_numerator: Get the numerator to use for boost ratio calculation
* @cpu: CPU to get numerator for.
* @numerator: Output variable for numerator.
* amd_get_effective_highest_perf: Get the effective highest performance value
* @cpu: CPU to get highest performance for.
*
* Determine the numerator to use for calculating the boost ratio on
* a CPU. On systems that support preferred cores, this will be a hardcoded
* value. On other systems this will the highest performance register value.
* Get the effective highest performance value for a CPU, accounting for
* preferred cores and heterogeneous topologies. On systems with preferred
* cores, this may be a hardcoded value. On heterogeneous systems, this
* may be a per-CPU value. On other systems, this is the shared highest
* performance value.
*
* If booting the system with amd-pstate enabled but preferred cores disabled then
* the correct boost numerator will be returned to match hardware capabilities
* even if the preferred cores scheduling hints are not enabled.
*
* Return: 0 for success, negative error code otherwise.
* Return: Effective highest performance value, or negative error code.
*/
int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
int amd_get_effective_highest_perf(unsigned int cpu)
{
bool prefcore;
int ret;
u32 tmp;
ret = amd_detect_prefcore(&prefcore);
if (ret)
if (ret < 0)
return ret;
/* without preferred cores, return the highest perf register value */
if (!prefcore) {
*numerator = boost_numerator;
return 0;
}
if (!prefcore)
return boost_numerator;
/*
* For AMD CPUs with Family ID 19H and Model ID range 0x70 to 0x7f,
@ -277,8 +260,7 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
if (cpu_feature_enabled(X86_FEATURE_ZEN4)) {
switch (boot_cpu_data.x86_model) {
case 0x70 ... 0x7f:
*numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
return 0;
return CPPC_HIGHEST_PERF_PERFORMANCE;
default:
break;
}
@ -286,28 +268,100 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
/* detect if running on heterogeneous design */
if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) {
switch (cpu_data(cpu).topo.cpu_type) {
case TOPO_CPU_TYPE_UNKNOWN:
case TOPO_CPU_TYPE_ANY:
pr_warn("Undefined core type found for cpu %d\n", cpu);
break;
case TOPO_CPU_TYPE_PERFORMANCE:
/* use the max scale for performance cores */
*numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
return 0;
case TOPO_CPU_TYPE_LOW_POWER:
case TOPO_CPU_TYPE_EFFICIENCY:
/* use the highest perf value for efficiency and low-power cores */
ret = amd_get_highest_perf(cpu, &tmp);
if (ret)
return ret;
*numerator = tmp;
return 0;
}
if (cpu_feature_enabled(X86_FEATURE_ZEN5) &&
cpu_data(cpu).topo.cpu_type == TOPO_CPU_TYPE_PERFORMANCE)
return CPPC_HIGHEST_PERF_PERFORMANCE;
/* Zen 5 efficiency, and Zen 6+ */
ret = amd_get_highest_perf(cpu, &tmp);
if (ret < 0)
return ret;
return tmp;
}
*numerator = CPPC_HIGHEST_PERF_PREFCORE;
return CPPC_HIGHEST_PERF_PREFCORE;
}
EXPORT_SYMBOL_GPL(amd_get_effective_highest_perf);
struct amd_max_freq {
u32 performance;
u32 efficiency;
u32 low_power;
};
static const struct amd_max_freq max_freq_zen6_client = {
.performance = 5025,
.efficiency = 3524,
.low_power = 2399,
};
int amd_get_max_frequency(unsigned int cpu)
{
enum x86_topology_cpu_type type = cpu_data(cpu).topo.cpu_type;
const struct amd_max_freq *max_freq = NULL;
if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
max_freq = &max_freq_zen6_client;
if (!max_freq)
return 0;
switch (type) {
case TOPO_CPU_TYPE_PERFORMANCE:
return max_freq->performance;
case TOPO_CPU_TYPE_EFFICIENCY:
return max_freq->efficiency;
case TOPO_CPU_TYPE_LOW_POWER:
return max_freq->low_power;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);
EXPORT_SYMBOL_GPL(amd_get_max_frequency);
/**
* amd_get_boost_ratio: Get numerator and denominator for boost ratio
* @cpu: CPU to get the boost ratio for.
* @numerator: Output variable for numerator.
* @denominator: Output variable for denominator.
*
* Get the numerator and denominator for calculating the boost ratio.
*
* Return: 0 for success, negative error code otherwise.
*/
int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
{
struct cppc_perf_caps perf_caps;
int max_frequency;
int ret;
ret = cppc_get_perf_caps(cpu, &perf_caps);
if (ret)
return ret;
max_frequency = amd_get_max_frequency(cpu);
/* Use frequency values if available */
if (max_frequency && perf_caps.nominal_freq) {
*numerator = max_frequency;
*denominator = perf_caps.nominal_freq;
return 0;
}
/* Fall back to performance values */
ret = amd_get_effective_highest_perf(cpu);
if (ret < 0)
return ret;
*numerator = ret;
*denominator = perf_caps.nominal_perf;
if (!*denominator)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(amd_get_boost_ratio);

View File

@ -288,6 +288,7 @@ endif
if LOONGARCH
config LOONGSON3_CPUFREQ
tristate "Loongson3 CPUFreq Driver"
depends on MACH_LOONGSON64
help
This option adds a CPUFreq driver for Loongson processors which
support software configurable cpu frequency.

View File

@ -626,7 +626,7 @@ static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c)
static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
{
struct cppc_perf_caps perf_caps;
u64 highest_perf, nominal_perf;
u64 numerator, denominator;
int ret;
if (acpi_pstate_strict)
@ -640,32 +640,31 @@ static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
}
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
ret = amd_get_boost_ratio_numerator(cpu, &highest_perf);
ret = amd_get_boost_ratio(cpu, &numerator, &denominator);
if (ret) {
pr_debug("CPU%d: Unable to get boost ratio numerator (%d)\n",
pr_debug("CPU%d: Unable to get boost ratio (%d)\n",
cpu, ret);
return 0;
}
} else {
highest_perf = perf_caps.highest_perf;
numerator = perf_caps.highest_perf;
denominator = perf_caps.nominal_perf;
}
nominal_perf = perf_caps.nominal_perf;
if (nominal_freq)
*nominal_freq = perf_caps.nominal_freq * 1000;
if (!highest_perf || !nominal_perf) {
if (!numerator || !denominator) {
pr_debug("CPU%d: highest or nominal performance missing\n", cpu);
return 0;
}
if (highest_perf < nominal_perf) {
if (numerator < denominator) {
pr_debug("CPU%d: nominal performance above highest\n", cpu);
return 0;
}
return div_u64(highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
return div_u64(numerator << SCHED_CAPACITY_SHIFT, denominator);
}
#else

View File

@ -64,7 +64,7 @@ static int airoha_cpufreq_probe(struct platform_device *pdev)
/* Attach PM for OPP */
ret = dev_pm_domain_attach_list(cpu_dev, &attach_data,
&priv->pd_list);
if (ret)
if (ret < 0)
goto clear_opp_config;
cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);

View File

@ -216,7 +216,14 @@ static int amd_pstate_ut_check_perf(u32 index)
/*
* Check if frequency values are reasonable.
* max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
* nominal_freq > lowest_nonlinear_freq >= min_freq > 0
* max_freq >= lowest_nonlinear_freq
*
* On most parts the boost frequency is the highest, i.e.
* max_freq >= nominal_freq. On heterogeneous designs (e.g. Zen6) a
* low-power core can have a maximum frequency below the shared nominal
* reference frequency, so only require the boost frequency to sit above
* the lowest nonlinear frequency here.
* check max freq when set support boost mode.
*/
static int amd_pstate_ut_check_freq(u32 index)
@ -226,23 +233,44 @@ static int amd_pstate_ut_check_freq(u32 index)
for_each_online_cpu(cpu) {
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
struct amd_cpudata *cpudata;
union perf_cached perf;
policy = cpufreq_cpu_get(cpu);
if (!policy)
continue;
cpudata = policy->driver_data;
if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
cpudata = policy->driver_data;
perf = READ_ONCE(cpudata->perf);
if (!((policy->cpuinfo.max_freq >= cpudata->lowest_nonlinear_freq) &&
(cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
(cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
(policy->cpuinfo.min_freq > 0))) {
pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
pr_err("%s cpu%d max=%d, nominal=%d, lowest_nonlinear=%d, min=%d, the formula is incorrect!\n",
__func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
return -EINVAL;
}
if (cpudata->lowest_nonlinear_freq != policy->min) {
if (perf.bios_min_perf) {
u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq,
perf.bios_min_perf);
/*
* User set bios_min_freq cannot be trusted to
* be within the driver limits. Clamp it similar
* to cpufreq_verify_within_cpu_limits().
*/
bios_min_freq = clamp_t(u32, bios_min_freq,
policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
if (bios_min_freq != policy->min) {
pr_err("%s cpu%d bios_min_freq=%d policy_min=%d, they should be equal!\n",
__func__, cpu, bios_min_freq, policy->min);
return -EINVAL;
}
} else if (cpudata->lowest_nonlinear_freq != policy->min) {
pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
__func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
return -EINVAL;
@ -276,12 +304,12 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
static int amd_pstate_ut_epp(u32 index)
{
static const char * const epp_strings[] = {
"dynamic",
"power",
"balance_power",
"balance_performance",
"performance",
enum energy_perf_value_index epp_modes[] = {
EPP_INDEX_DYNAMIC,
EPP_INDEX_POWERSAVE,
EPP_INDEX_BALANCE_POWERSAVE,
EPP_INDEX_BALANCE_PERFORMANCE,
EPP_INDEX_PERFORMANCE,
};
char *buf __free(cleanup_page) = NULL;
struct cpufreq_policy *policy = NULL;
@ -350,9 +378,9 @@ static int amd_pstate_ut_epp(u32 index)
}
}
for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
for (i = 0; i < ARRAY_SIZE(epp_modes); i++) {
memset(buf, 0, PAGE_SIZE);
snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
snprintf(buf, PAGE_SIZE, "%s", energy_perf_strings[epp_modes[i]]);
ret = store_energy_performance_preference(policy, buf, strlen(buf));
if (ret < 0)
goto out;
@ -368,8 +396,24 @@ static int amd_pstate_ut_epp(u32 index)
*/
strreplace(buf, '(', '\0');
if (strcmp(buf, epp_strings[i])) {
pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
if (strcmp(buf, energy_perf_strings[epp_modes[i]])) {
u8 epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
/*
* The preference read back may legitimately differ from the one
* written: some platforms (e.g. Zen6) program the same raw EPP
* value for more than one named preference, so show() reports the
* first name that matches that value.
*
* If strings mismatch, confirm the CPU is programmed with the correct
* EPP value for the mode. This verifies hardware programming while
* tolerating such aliasing in driver.
*/
if (epp == amd_pstate_cpu_epp_values(cpudata->cpu_type)[epp_modes[i]])
continue;
pr_err("String EPP value mismatch: %s != %s\n",
buf, energy_perf_strings[epp_modes[i]]);
ret = -EINVAL;
goto out;
}
@ -385,11 +429,12 @@ static int amd_pstate_ut_epp(u32 index)
* restore it here before dropping policy reference.
*/
if (orig_dynamic_epp) {
const char * const dynamic_string = energy_perf_strings[epp_modes[0]];
int ret2;
ret2 = store_energy_performance_preference(policy,
epp_strings[0],
strlen(epp_strings[0]));
dynamic_string,
strlen(dynamic_string));
if (!ret && (ret2 < 0))
ret = ret2;
}

View File

@ -44,7 +44,6 @@
#include <acpi/cppc_acpi.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <asm/cpu_device_id.h>
@ -55,10 +54,10 @@
#define AMD_PSTATE_TRANSITION_DELAY 1000
#define AMD_PSTATE_FAST_CPPC_TRANSITION_DELAY 600
#define AMD_CPPC_EPP_PERFORMANCE 0x00
#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80
#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF
#define AMD_CPPC_EPP_POWERSAVE 0xFF
#define AMD_CPPC_EPP_LEGACY_PERFORMANCE 0x00
#define AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE 0x80
#define AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE 0xBF
#define AMD_CPPC_EPP_LEGACY_POWERSAVE 0xFF
static const char * const amd_pstate_mode_string[] = {
[AMD_PSTATE_UNDEFINED] = "undefined",
@ -90,53 +89,112 @@ static bool amd_pstate_prefcore = true;
static struct quirk_entry *quirks;
/*
* AMD Energy Preference Performance (EPP)
* The EPP is used in the CCLK DPM controller to drive
* the frequency that a core is going to operate during
* short periods of activity. EPP values will be utilized for
* different OS profiles (balanced, performance, power savings)
* display strings corresponding to EPP index in the
* energy_perf_strings[]
* index String
*-------------------------------------
* 0 default
* 1 performance
* 2 balance_performance
* 3 balance_power
* 4 power
* 5 custom (for raw EPP values)
* 6 dynamic (platform profile driven selection)
* The numeric EPP value programmed for each named preference. First dimension
* is CPU type (TOPO_CPU_TYPE_ANY for non-hybrid, TOPO_CPU_TYPE_PERFORMANCE/
* EFFICIENCY/LOW_POWER for hybrid). The initializer holds the legacy values
* used as the fallback on any platform not listed in amd_pstate_epp_soc_ids[];
* amd_pstate_init_epp_values() overwrites slots at boot when the running SoC
* has a per-SoC (and potentially per-CPU-type) override.
*/
enum energy_perf_value_index {
EPP_INDEX_DEFAULT = 0,
EPP_INDEX_PERFORMANCE,
EPP_INDEX_BALANCE_PERFORMANCE,
EPP_INDEX_BALANCE_POWERSAVE,
EPP_INDEX_POWERSAVE,
EPP_INDEX_CUSTOM,
EPP_INDEX_DYNAMIC,
EPP_INDEX_MAX,
static u8 epp_values[][EPP_INDEX_MAX] = {
/*
* Initialize all CPU types to legacy defaults.
* amd_pstate_init_epp_values() will fix these up
* based on the platform during boot.
*/
[TOPO_CPU_TYPE_ANY ... TOPO_CPU_TYPE_LOW_POWER] = {
[EPP_INDEX_DEFAULT] = 0,
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_PERFORMANCE,
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE,
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_LEGACY_POWERSAVE,
},
};
static_assert(ARRAY_SIZE(epp_values) == TOPO_CPU_TYPE_LOW_POWER + 1,
"epp_values must have entries for all CPU types up to TOPO_CPU_TYPE_LOW_POWER");
/*
* Get the EPP value row for a given CPU, accounting for hybrid CPU types.
* Non-hybrid systems use TOPO_CPU_TYPE_ANY; hybrid systems use the CPU's
* actual type (PERFORMANCE/EFFICIENCY/LOW_POWER).
*/
u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
{
switch (cpu_type) {
case TOPO_CPU_TYPE_PERFORMANCE:
case TOPO_CPU_TYPE_EFFICIENCY:
case TOPO_CPU_TYPE_LOW_POWER:
return epp_values[cpu_type];
default:
return epp_values[TOPO_CPU_TYPE_ANY];
}
}
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_cpu_epp_values);
/**
* struct amd_pstate_epp_values - EPP values for the four named preferences
* @performance: value for the "performance" preference
* @balance_performance: value for the "balance_performance" preference
* @balance_power: value for the "balance_power" preference
* @power: value for the "power" preference
*/
struct amd_pstate_epp_values {
u8 performance;
u8 balance_performance;
u8 balance_power;
u8 power;
};
static const char * const energy_perf_strings[] = {
[EPP_INDEX_DEFAULT] = "default",
[EPP_INDEX_PERFORMANCE] = "performance",
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
[EPP_INDEX_POWERSAVE] = "power",
[EPP_INDEX_CUSTOM] = "custom",
[EPP_INDEX_DYNAMIC] = "dynamic",
/**
* struct amd_pstate_epp_soc - per-CPU-type EPP overrides for hybrid systems
* @performance_core: values for TOPO_CPU_TYPE_PERFORMANCE cores
* @efficiency_core: values for TOPO_CPU_TYPE_EFFICIENCY cores
* @low_power_core: values for TOPO_CPU_TYPE_LOW_POWER cores
*
* Referenced from amd_pstate_epp_soc_ids[] to give a hybrid platform its own
* numeric EPP values for the four named preferences, with distinct values per
* CPU type. Non-hybrid systems are not listed in the table and always use the
* legacy defaults.
*/
struct amd_pstate_epp_soc {
struct amd_pstate_epp_values performance_core;
struct amd_pstate_epp_values efficiency_core;
struct amd_pstate_epp_values low_power_core;
};
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
static unsigned int epp_values[] = {
[EPP_INDEX_DEFAULT] = 0,
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_PERFORMANCE,
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_BALANCE_PERFORMANCE,
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
/*
* Per-CPU-type EPP overrides for hybrid systems. Only hybrid SoCs should be
* listed here; non-hybrid systems always use the legacy defaults.
*/
static const struct amd_pstate_epp_soc epp_soc_zen6_client __initconst = {
.performance_core = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
.power = 115,
},
.efficiency_core = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
.power = 115,
},
.low_power_core = {
.performance = 25,
.balance_performance = 64,
.balance_power = 64,
.power = 64,
},
};
static const struct x86_cpu_id amd_pstate_epp_soc_ids[] __initconst = {
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x80, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x81, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x84, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x85, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0xe0, &epp_soc_zen6_client),
{}
};
static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2);
typedef int (*cppc_mode_transition_fn)(int);
@ -145,19 +203,6 @@ static struct quirk_entry quirk_amd_7k62 = {
.lowest_freq = 550,
};
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
{
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
}
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
{
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
perf.nominal_perf);
}
static int __init dmi_matched_7k62_bios_bug(const struct dmi_system_id *dmi)
{
/**
@ -470,11 +515,13 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
if (ret)
return ret;
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
if (ret)
ret = amd_get_effective_highest_perf(cpudata->cpu);
if (ret < 0)
return ret;
ret = rdmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
numerator = ret;
ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
if (ret)
return ret;
@ -505,10 +552,12 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
if (ret)
return ret;
ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
if (ret)
ret = amd_get_effective_highest_perf(cpudata->cpu);
if (ret < 0)
return ret;
numerator = ret;
perf.highest_perf = numerator;
perf.max_limit_perf = numerator;
perf.min_limit_perf = cppc_perf.lowest_perf;
@ -524,8 +573,10 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
return 0;
}
ret = cppc_set_auto_sel(cpudata->cpu,
(cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
if ((cppc_state != AMD_PSTATE_PASSIVE) == auto_sel)
return 0;
ret = cppc_set_auto_sel(cpudata->cpu, !auto_sel);
if (ret)
pr_warn("failed to set auto_sel, ret: %d\n", ret);
@ -687,7 +738,10 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
struct amd_cpudata *cpudata = policy->driver_data;
union perf_cached perf = READ_ONCE(cpudata->perf);
perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
if (policy->max >= READ_ONCE(cpudata->max_freq))
perf.max_limit_perf = perf.highest_perf;
else
perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
WRITE_ONCE(cpudata->max_limit_freq, policy->max);
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
@ -1013,8 +1067,10 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
WRITE_ONCE(cpudata->nominal_freq, nominal_freq);
/* max_freq is calculated according to (nominal_freq * highest_perf)/nominal_perf */
max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
/* try to look up from BIOS/quirk first, fall back to (nominal_freq * highest_perf)/nominal_perf */
max_freq = amd_get_max_frequency(cpudata->cpu) * 1000;
if (!max_freq)
max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
WRITE_ONCE(cpudata->max_freq, max_freq);
lowest_nonlinear_freq = perf_to_freq(perf, nominal_freq, perf.lowest_nonlinear_perf);
@ -1068,6 +1124,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
return -ENOMEM;
cpudata->cpu = policy->cpu;
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
ret = amd_pstate_init_perf(cpudata);
if (ret)
@ -1195,13 +1252,16 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy,
enum platform_profile_option profile)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
return AMD_CPPC_EPP_PERFORMANCE;
return values[EPP_INDEX_PERFORMANCE];
case PLATFORM_PROFILE_BALANCED:
return amd_pstate_get_balanced_epp(policy);
case PLATFORM_PROFILE_LOW_POWER:
return AMD_CPPC_EPP_POWERSAVE;
return values[EPP_INDEX_POWERSAVE];
default:
break;
}
@ -1292,8 +1352,10 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
return ret;
cpudata->profile_name = kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", cpudata->cpu);
if (!cpudata->profile_name)
return -ENOMEM;
if (!cpudata->profile_name) {
ret = -ENOMEM;
goto cleanup;
}
cpudata->ppdev = platform_profile_register(get_cpu_device(policy->cpu),
cpudata->profile_name,
@ -1411,6 +1473,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
ssize_t ret;
bool raw_epp = false;
u8 epp;
@ -1445,12 +1508,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
}
if (ret)
epp = epp_values[ret];
epp = values[ret];
else
epp = cpudata->epp_default_dc;
}
if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
if (epp > 0 && epp != values[EPP_INDEX_PERFORMANCE] &&
cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
pr_debug("EPP cannot be set under performance policy\n");
return -EBUSY;
}
@ -1475,34 +1539,31 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference);
ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 preference, epp;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
u8 epp;
int i;
epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
if (!cpudata->dynamic_epp && cpudata->raw_epp)
return sysfs_emit(buf, "%u\n", epp);
switch (epp) {
case AMD_CPPC_EPP_PERFORMANCE:
preference = EPP_INDEX_PERFORMANCE;
break;
case AMD_CPPC_EPP_BALANCE_PERFORMANCE:
preference = EPP_INDEX_BALANCE_PERFORMANCE;
break;
case AMD_CPPC_EPP_BALANCE_POWERSAVE:
preference = EPP_INDEX_BALANCE_POWERSAVE;
break;
case AMD_CPPC_EPP_POWERSAVE:
preference = EPP_INDEX_POWERSAVE;
break;
default:
return -EINVAL;
/*
* Map the cached EPP value back to a named preference. Skip the
* "default" slot (index 0) so an EPP of 0 reports as "performance".
* Stop at POWERSAVE; CUSTOM and DYNAMIC are not initialized in epp_values.
*/
for (i = EPP_INDEX_PERFORMANCE; i <= EPP_INDEX_POWERSAVE; i++) {
const char *name = energy_perf_strings[i];
if (epp == values[i]) {
if (cpudata->dynamic_epp)
return sysfs_emit(buf, "dynamic(profile:%s)\n", name);
return sysfs_emit(buf, "%s\n", name);
}
}
if (cpudata->dynamic_epp)
return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]);
return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
return sysfs_emit(buf, "%u\n", epp);
}
EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
@ -1721,6 +1782,7 @@ static int amd_pstate_unregister_driver(int dummy)
static int amd_pstate_change_mode_without_dvr_change(int mode)
{
int cpu = 0;
int ret;
cppc_state = mode;
@ -1728,7 +1790,9 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
return 0;
for_each_online_cpu(cpu) {
cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
ret = cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
if (ret)
return ret;
}
return 0;
@ -1736,6 +1800,7 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
static int amd_pstate_change_driver_mode(int mode)
{
int old_mode = cppc_state;
int ret;
lockdep_assert_held(&amd_pstate_driver_lock);
@ -1745,10 +1810,16 @@ static int amd_pstate_change_driver_mode(int mode)
return ret;
ret = amd_pstate_register_driver(mode);
if (ret)
return ret;
if (ret) {
pr_err("Failed to register %s mode, restoring %s mode\n",
amd_pstate_get_mode_string(mode),
amd_pstate_get_mode_string(old_mode));
if (amd_pstate_register_driver(old_mode))
pr_err("Failed to restore %s mode\n",
amd_pstate_get_mode_string(old_mode));
}
return 0;
return ret;
}
static cppc_mode_transition_fn mode_state_machine[AMD_PSTATE_MAX][AMD_PSTATE_MAX] = {
@ -1794,6 +1865,7 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
int amd_pstate_update_status(const char *buf, size_t size)
{
cppc_mode_transition_fn fn;
int cpu, mode_idx;
if (size > strlen("passive") || size < strlen("active"))
@ -1803,6 +1875,7 @@ int amd_pstate_update_status(const char *buf, size_t size)
if (mode_idx < 0)
return mode_idx;
/* Resolve the transition under the lock; cppc_state is only stable while held. */
guard(mutex)(&amd_pstate_driver_lock);
if (!mode_state_machine[cppc_state][mode_idx])
@ -1819,7 +1892,8 @@ int amd_pstate_update_status(const char *buf, size_t size)
}
}
return mode_state_machine[cppc_state][mode_idx](mode_idx);
fn = mode_state_machine[cppc_state][mode_idx];
return fn(mode_idx);
}
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status);
@ -1905,6 +1979,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
return -ENOMEM;
cpudata->cpu = policy->cpu;
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
ret = amd_pstate_init_perf(cpudata);
if (ret)
@ -1955,9 +2030,11 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp;
cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
} else {
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
policy->policy = CPUFREQ_POLICY_POWERSAVE;
cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
cpudata->epp_default_ac = values[EPP_INDEX_PERFORMANCE];
cpudata->epp_default_dc = values[EPP_INDEX_BALANCE_PERFORMANCE];
cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
}
@ -2256,6 +2333,38 @@ static bool amd_cppc_supported(void)
return true;
}
/*
* Resolve the numeric EPP values for hybrid systems. Only hybrid SoCs are listed
* in amd_pstate_epp_soc_ids[]; non-hybrid systems always use the legacy defaults.
*/
static inline void __init amd_pstate_set_epp_values(enum x86_topology_cpu_type type,
const struct amd_pstate_epp_values *core)
{
epp_values[type][EPP_INDEX_PERFORMANCE] = core->performance;
epp_values[type][EPP_INDEX_BALANCE_PERFORMANCE] = core->balance_performance;
epp_values[type][EPP_INDEX_BALANCE_POWERSAVE] = core->balance_power;
epp_values[type][EPP_INDEX_POWERSAVE] = core->power;
}
static void __init amd_pstate_init_epp_values(void)
{
const struct x86_cpu_id *id = x86_match_cpu(amd_pstate_epp_soc_ids);
const struct amd_pstate_epp_soc *soc;
if (!id || !id->driver_data) {
if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
pr_warn_once("No EPP tunings found for platform\n");
return;
}
soc = (const struct amd_pstate_epp_soc *)id->driver_data;
amd_pstate_set_epp_values(TOPO_CPU_TYPE_PERFORMANCE, &soc->performance_core);
amd_pstate_set_epp_values(TOPO_CPU_TYPE_EFFICIENCY, &soc->efficiency_core);
amd_pstate_set_epp_values(TOPO_CPU_TYPE_LOW_POWER, &soc->low_power_core);
}
static int __init amd_pstate_init(void)
{
struct device *dev_root;
@ -2283,6 +2392,9 @@ static int __init amd_pstate_init(void)
/* check if this machine need CPPC quirks */
dmi_check_system(amd_pstate_quirks_table);
/* resolve per-SoC EPP values for the named preferences */
amd_pstate_init_epp_values();
/*
* determine the driver mode from the command line or kernel config.
* If no command line input is provided, cppc_state will be AMD_PSTATE_UNDEFINED.

View File

@ -11,6 +11,8 @@
#include <linux/pm_qos.h>
#include <linux/platform_profile.h>
#include <asm/processor.h>
#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT)
#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \
EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut")
@ -146,6 +148,8 @@ struct amd_cpudata {
enum platform_profile_option current_profile;
struct device *ppdev;
char *profile_name;
enum x86_topology_cpu_type cpu_type;
};
/*
@ -159,6 +163,62 @@ enum amd_pstate_mode {
AMD_PSTATE_GUIDED,
AMD_PSTATE_MAX,
};
/*
* AMD Energy Preference Performance (EPP)
* The EPP is used in the CCLK DPM controller to drive
* the frequency that a core is going to operate during
* short periods of activity. EPP values will be utilized for
* different OS profiles (balanced, performance, power savings)
* display strings corresponding to EPP index in the
* energy_perf_strings[]
* index String
*-------------------------------------
* 0 default
* 1 performance
* 2 balance_performance
* 3 balance_power
* 4 power
* 5 custom (for raw EPP values)
* 6 dynamic (platform profile driven selection)
*/
enum energy_perf_value_index {
EPP_INDEX_DEFAULT = 0,
EPP_INDEX_PERFORMANCE,
EPP_INDEX_BALANCE_PERFORMANCE,
EPP_INDEX_BALANCE_POWERSAVE,
EPP_INDEX_POWERSAVE,
EPP_INDEX_CUSTOM,
EPP_INDEX_DYNAMIC,
EPP_INDEX_MAX,
};
static const char * const energy_perf_strings[] = {
[EPP_INDEX_DEFAULT] = "default",
[EPP_INDEX_PERFORMANCE] = "performance",
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
[EPP_INDEX_POWERSAVE] = "power",
[EPP_INDEX_CUSTOM] = "custom",
[EPP_INDEX_DYNAMIC] = "dynamic",
};
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type);
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
{
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
}
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
{
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
perf.nominal_perf);
}
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);

View File

@ -132,8 +132,8 @@ static int bmips_cpufreq_init(struct cpufreq_policy *policy)
freq_table = bmips_cpufreq_get_freq_table(policy);
if (IS_ERR(freq_table)) {
pr_err("%s: couldn't determine frequency table (%ld).\n",
BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
pr_err("%s: couldn't determine frequency table (%pe).\n",
BMIPS_CPUFREQ_NAME, freq_table);
return PTR_ERR(freq_table);
}

View File

@ -247,8 +247,7 @@ static int cppc_fie_kworker_init(void)
worker = kthread_run_worker(0, "cppc_fie");
if (IS_ERR(worker)) {
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
PTR_ERR(worker));
pr_warn("%s: failed to create kworker_fie: %pe\n", __func__, worker);
return PTR_ERR(worker);
}

View File

@ -155,6 +155,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "nvidia,tegra234", },
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,milos", },
{ .compatible = "qcom,msm8909", },
{ .compatible = "qcom,msm8996", },
{ .compatible = "qcom,msm8998", },
@ -209,6 +210,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,ipq8064", },
{ .compatible = "qcom,ipq8074", },
{ .compatible = "qcom,ipq9574", },
{ .compatible = "qcom,ipq9650", },
{ .compatible = "qcom,apq8064", },
{ .compatible = "qcom,msm8974", },
{ .compatible = "qcom,msm8960", },

View File

@ -85,10 +85,12 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy)
freq_step = get_freq_step(cs_tuners, policy);
/*
* Decrease requested_freq one freq_step for each idle period that
* we didn't update the frequency.
* Apply deferred down steps only when the policy sample load is
* below down_threshold. Otherwise, multiple deferred down steps may
* cause a net frequency decrease outside the downscaling region.
*/
if (policy_dbs->idle_periods < UINT_MAX) {
if (policy_dbs->max_sample_load < cs_tuners->down_threshold &&
policy_dbs->idle_periods < UINT_MAX) {
unsigned int freq_steps = policy_dbs->idle_periods * freq_step;
if (requested_freq > policy->min + freq_steps)

View File

@ -124,7 +124,8 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
struct policy_dbs_info *policy_dbs = policy->governor_data;
struct dbs_data *dbs_data = policy_dbs->dbs_data;
unsigned int ignore_nice = dbs_data->ignore_nice_load;
unsigned int max_load = 0, idle_periods = UINT_MAX;
unsigned int max_load = 0, max_sample_load = 0;
unsigned int idle_periods = UINT_MAX;
unsigned int sampling_rate, io_busy, j;
u64 cur_nice;
@ -147,7 +148,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j);
u64 update_time, cur_idle_time;
unsigned int idle_time, time_elapsed;
unsigned int load;
unsigned int load, sample_load;
cur_idle_time = get_cpu_idle_time(j, &update_time, io_busy);
@ -186,6 +187,20 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
j_cdbs->prev_cpu_nice = cur_nice;
/*
* Compute the sample load separately from the prev_load value
* that may be reused after a long idle interval. The conservative
* governor uses it to decide whether to apply deferred down steps.
* If no time has elapsed, retain the existing behavior and use
* prev_load.
*/
if (unlikely(!time_elapsed))
sample_load = j_cdbs->prev_load;
else if (time_elapsed > idle_time)
sample_load = 100 * (time_elapsed - idle_time) / time_elapsed;
else
sample_load = 0;
if (unlikely(!time_elapsed)) {
/*
* That can only happen when this function is called
@ -220,11 +235,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
load = j_cdbs->prev_load;
j_cdbs->prev_load = 0;
} else {
if (time_elapsed > idle_time)
load = 100 * (time_elapsed - idle_time) / time_elapsed;
else
load = 0;
load = sample_load;
j_cdbs->prev_load = load;
}
@ -237,9 +248,13 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
if (load > max_load)
max_load = load;
if (sample_load > max_sample_load)
max_sample_load = sample_load;
}
policy_dbs->idle_periods = idle_periods;
policy_dbs->max_sample_load = max_sample_load;
return max_load;
}

View File

@ -95,6 +95,8 @@ struct policy_dbs_info {
/* Multiplier for increasing sample delay temporarily. */
unsigned int rate_mult;
unsigned int idle_periods; /* For conservative */
/* Maximum load from the current policy sample. */
unsigned int max_sample_load;
/* Status indicators */
bool is_shared; /* This object is used by multiple CPUs */
bool work_in_progress; /* Work is being queued up or in progress */

View File

@ -16,14 +16,16 @@
#include <asm/idle.h>
#include <asm/loongarch.h>
#include <asm/loongson.h>
#include <asm/smp.h>
#include <asm/numa.h>
/* Message */
union smc_message {
u32 value;
struct {
u32 id : 4;
u32 id : 8;
u32 info : 4;
u32 val : 16;
u32 val : 12;
u32 cmd : 6;
u32 extra : 1;
u32 complete : 1;
@ -164,27 +166,47 @@ union smc_message {
#define FREQ_MAX_LEVEL 16
#define MMIO_SMCMBX(node) \
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX))
#define MMIO_MISC_FUNC(node) \
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC))
struct loongson3_freq_data {
unsigned int def_freq_level;
struct cpufreq_frequency_table table[];
};
static struct mutex cpufreq_mutex[MAX_PACKAGES];
static struct mutex cpufreq_mutex[MAX_NUMNODES];
static struct cpufreq_driver loongson3_cpufreq_driver;
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);
static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
{
int retries;
unsigned int cpu = raw_smp_processor_id();
unsigned int package = cpu_data[cpu].package;
unsigned int cpu = 0, nid;
union smc_message msg, last;
mutex_lock(&cpufreq_mutex[package]);
#ifdef CONFIG_SMP
switch (cmd) {
case CMD_GET_FREQ_INFO:
case CMD_SET_FREQ_INFO:
case CMD_GET_FREQ_LEVEL_NUM:
case CMD_GET_FREQ_LEVEL_INFO:
case CMD_GET_FREQ_BOOST_LEVEL:
cpu = cpu_number_map(id);
break;
default:
cpu = raw_smp_processor_id();
}
#endif
nid = cpu_to_node(cpu);
last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
mutex_lock(&cpufreq_mutex[nid]);
last.value = readl(MMIO_SMCMBX(nid));
if (!last.complete) {
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
@ -195,12 +217,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
msg.extra = extra;
msg.complete = 0;
iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
LOONGARCH_IOCSR_MISC_FUNC);
writel(msg.value, MMIO_SMCMBX(nid));
writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid));
for (retries = 0; retries < 10000; retries++) {
msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
msg.value = readl(MMIO_SMCMBX(nid));
if (msg.complete)
break;
@ -208,49 +229,51 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
}
if (!msg.complete || msg.cmd != CMD_OK) {
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return msg.val;
}
static unsigned int loongson3_cpufreq_get(unsigned int cpu)
{
int ret;
int ret, core = cpu_logical_map(cpu);
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
return ret * KILO;
return (ret < 0) ? 0 : ret * KILO;
}
static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
{
int ret;
int ret, core = cpu_logical_map(policy->cpu);
ret = do_service_request(cpu_data[policy->cpu].core,
FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO,
index, 0);
return (ret >= 0) ? 0 : ret;
}
static int configure_freq_table(int cpu)
{
int i, ret, boost_level, max_level, freq_level;
int i, ret, core, boost_level, max_level, freq_level;
struct platform_device *pdev = cpufreq_get_driver_data();
struct loongson3_freq_data *data;
if (per_cpu(freq_data, cpu))
return 0;
ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
core = cpu_logical_map(cpu);
ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
if (ret < 0)
return ret;
max_level = ret;
ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
if (ret < 0)
return ret;
boost_level = ret;
@ -263,7 +286,7 @@ static int configure_freq_table(int cpu)
data->def_freq_level = boost_level - 1;
for (i = 0; i < freq_level; i++) {
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
if (ret < 0) {
devm_kfree(&pdev->dev, data);
return ret;
@ -337,7 +360,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
{
int i, ret;
for (i = 0; i < MAX_PACKAGES; i++) {
for (i = 0; i < MAX_NUMNODES; i++) {
ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
if (ret)
return ret;

View File

@ -603,6 +603,7 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
policy->freq_table = freq_table;
policy->driver_data = info;
policy->clk = info->cpu_clk;
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(info->cpu_dev) / 1000;
return 0;
}
@ -624,6 +625,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = {
.init = mtk_cpufreq_init,
.exit = mtk_cpufreq_exit,
.register_em = cpufreq_register_em_with_opp,
.suspend = cpufreq_generic_suspend,
.name = "mtk-cpufreq",
};

View File

@ -322,6 +322,8 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
if (index == args.args[0])
cpumask_set_cpu(cpu, m);
of_node_put(args.np);
}
}
@ -554,6 +556,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
return ret;
index = args.args[0];
of_node_put(args.np);
data = &qcom_cpufreq.data[index];
/* HW should be in enabled state to proceed */

View File

@ -211,6 +211,14 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
case QCOM_ID_IPQ5404:
drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0);
break;
case QCOM_ID_IPQ9610:
case QCOM_ID_IPQ9620:
case QCOM_ID_IPQ9630:
case QCOM_ID_IPQ9640:
case QCOM_ID_IPQ9650:
case QCOM_ID_IPQ9670:
drv->versions = (*speedbin == 0x4b) ? BIT(3) : BIT(2);
break;
case QCOM_ID_MSM8996SG:
case QCOM_ID_APQ8096SG:
drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
@ -633,6 +641,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u
{ .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 },
{ .compatible = "qcom,apq8064", .data = &match_data_krait },
{ .compatible = "qcom,ipq9574", .data = &match_data_kryo },
{ .compatible = "qcom,ipq9650", .data = &match_data_kryo },
{ .compatible = "qcom,msm8974", .data = &match_data_krait },
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
{},

View File

@ -57,8 +57,7 @@ static u32 get_bus_freq(void)
/* get platform freq by its clock name */
pltclk = clk_get(NULL, "cg-pll0-div1");
if (IS_ERR(pltclk)) {
pr_err("%s: can't get bus frequency %ld\n",
__func__, PTR_ERR(pltclk));
pr_err("%s: can't get bus frequency %pe\n", __func__, pltclk);
return PTR_ERR(pltclk);
}

View File

@ -225,4 +225,5 @@ fn probe<'bound>(
authors: ["Viresh Kumar <viresh.kumar@linaro.org>"],
description: "Generic CPUFreq DT driver",
license: "GPL v2",
alias: ["platform:cpufreq-dt"],
}

View File

@ -152,15 +152,14 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
policy->clk = clk_get(NULL, "armclk");
if (IS_ERR(policy->clk)) {
pr_err("Unable to obtain ARMCLK: %ld\n",
PTR_ERR(policy->clk));
pr_err("Unable to obtain ARMCLK: %pe\n", policy->clk);
return PTR_ERR(policy->clk);
}
#ifdef CONFIG_REGULATOR
vddarm = regulator_get(NULL, "vddarm");
if (IS_ERR(vddarm)) {
pr_err("Failed to obtain VDDARM: %ld\n", PTR_ERR(vddarm));
pr_err("Failed to obtain VDDARM: %pe\n", vddarm);
pr_err("Only frequency scaling available\n");
vddarm = NULL;
} else {

View File

@ -282,12 +282,11 @@ static int us2e_freq_cpu_init(struct cpufreq_policy *policy)
table[1].frequency = clock_tick / 2;
table[2].driver_data = 2;
table[2].frequency = clock_tick / 4;
table[2].driver_data = 3;
table[2].frequency = clock_tick / 6;
table[2].driver_data = 4;
table[2].frequency = clock_tick / 8;
table[2].driver_data = 5;
table[3].frequency = CPUFREQ_TABLE_END;
table[3].driver_data = 3;
table[3].frequency = clock_tick / 6;
table[4].driver_data = 4;
table[4].frequency = clock_tick / 8;
table[5].frequency = CPUFREQ_TABLE_END;
policy->cpuinfo.transition_latency = 0;
policy->cur = clock_tick;

View File

@ -263,7 +263,7 @@ static int __init sti_cpufreq_init(void)
ddata.cpu = get_cpu_device(0);
if (!ddata.cpu) {
dev_err(ddata.cpu, "Failed to get device for CPU0\n");
pr_err("Failed to get device for CPU0\n");
goto skip_voltage_scaling;
}
@ -281,7 +281,7 @@ static int __init sti_cpufreq_init(void)
goto register_cpufreq_dt;
skip_voltage_scaling:
dev_err(ddata.cpu, "Not doing voltage scaling\n");
pr_err("Not doing voltage scaling\n");
register_cpufreq_dt:
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);

View File

@ -367,7 +367,7 @@ static void tegra194_get_cpu_ndiv_sysreg(void *ndiv)
static int tegra194_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
{
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, &ndiv, true);
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, ndiv, true);
}
static void tegra194_set_cpu_ndiv_sysreg(void *data)

View File

@ -202,7 +202,9 @@ extern int cppc_set_auto_sel(int cpu, bool enable);
extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
extern int amd_get_effective_highest_perf(unsigned int cpu);
extern int amd_get_max_frequency(unsigned int cpu);
extern int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator);
extern int amd_detect_prefcore(bool *detected);
#else /* !CONFIG_ACPI_CPPC_LIB */
static inline int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
@ -321,7 +323,15 @@ static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
{
return -ENODEV;
}
static inline int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
static inline int amd_get_effective_highest_perf(unsigned int cpu)
{
return -EOPNOTSUPP;
}
static inline int amd_get_max_frequency(unsigned int cpu)
{
return 0;
}
static inline int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
{
return -EOPNOTSUPP;
}

View File

@ -73,6 +73,18 @@ static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
(s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
}
static void sugov_update_rate_limit_us_sync(struct sugov_policy *sg_policy)
{
/*
* Serialize with rate_limit_us_store() to prevent this path from using
* a stale rate limit tunable value to set the limit for the given
* policy.
*/
guard(mutex)(&sg_policy->tunables->attr_set.update_lock);
sugov_update_rate_limit_us(sg_policy);
}
static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
{
s64 delta_ns;
@ -861,7 +873,7 @@ static int sugov_start(struct cpufreq_policy *policy)
void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
unsigned int cpu;
sugov_update_rate_limit_us(sg_policy);
sugov_update_rate_limit_us_sync(sg_policy);
sg_policy->last_freq_update_time = 0;
sg_policy->next_freq = 0;
sg_policy->work_in_progress = false;

View File

@ -14,7 +14,13 @@
cpumask,
device::{Bound, Device},
devres,
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
error::{
code::*,
from_result,
to_result,
Result,
VTABLE_DEFAULT_ERROR, //
},
ffi::{c_char, c_ulong},
prelude::*,
types::ForeignOwnable,
@ -29,7 +35,10 @@
marker::PhantomData,
ops::{Deref, DerefMut},
pin::Pin,
ptr,
ptr::{
self,
NonNull, //
},
};
use macros::vtable;
@ -687,12 +696,13 @@ fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
impl<'a> PolicyCpu<'a> {
fn from_cpu(cpu: CpuId) -> Result<Self> {
// SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU.
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?;
let ptr =
NonNull::new(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) }).ok_or(ENODEV)?;
Ok(Self(
// SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of
// the returned reference.
unsafe { Policy::from_raw_mut(ptr) },
// SAFETY: `ptr` is non-NULL and `cpufreq_cpu_get()` took a reference on it, so it is
// valid for writing and remains valid for the lifetime of the returned reference.
unsafe { Policy::from_raw_mut(ptr.as_ptr()) },
))
}
}