mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
eece15c579
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
===========================================
|
||||
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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", },
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
{},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()) },
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user