mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch 'acpi-cppc'
Merge ACPI CPPC library updates the CPPC cpufreq driver updates based on them for 7.4: - Fix multiple issues related to input validation, synchronization, access safety, memory layout and register field validation, and error handling in the ACPI CPPC library (Christian Loehle and Sumit Gupta) - Select the frequency-invariance callback per CPU in the CPPC cpufreq driver and create the FIE worker before enabling PCC callbacks in it to avoid queueing work through a missing worker and dereferencing a NULL pointer (Christian Loehle) - Update the CPPC cpufreq driver entry in MAINTAINERS and add a new entry for the ACPI CPPC library (Christian Loehle) * acpi-cppc: (22 commits) MAINTAINERS: ACPI: Add a separate CPPC entry MAINTAINERS: cpufreq: Replace CPPC reviewer cpufreq: CPPC: Create the FIE worker before enabling PCC callbacks cpufreq: CPPC: Select the frequency-invariance callback per CPU ACPI: CPPC: Accept requests to retain immutable autonomous selection ACPI: CPPC: Propagate errors from cross-CPU FFH calls ACPI: CPPC: Validate FFH register fields before hardware access ACPI: CPPC: Keep Performance Limited clearable on NVIDIA T41 ACPI: CPPC: Clear Performance Limited without a stale read ACPI: CPPC: Validate SystemIO overlaps across processors ACPI: CPPC: Validate PCC overlaps across processors ACPI: CPPC: Validate SystemIO register layouts ACPI: CPPC: Validate and access PCC register layouts ACPI: CPPC: Reject direct reads of write-only controls ACPI: CPPC: Reject unsafe cross-CPU SystemMemory RMW ACPI: CPPC: Release PCC data after probe failures ACPI: CPPC: Release CPC descriptors through kobject ACPI: CPPC: Serialize PCC EPP payload updates ACPI: CPPC: Serialize PCC single-register payload updates ACPI: CPPC: Propagate performance-control write errors ...
This commit is contained in:
commit
2da80f2b49
|
|
@ -335,12 +335,16 @@ Description: Performance Limited
|
|||
Read to check if platform throttling (thermal/power/current
|
||||
limits) caused delivered performance to fall below the
|
||||
requested level. A non-zero value indicates throttling occurred.
|
||||
If firmware provides the register but Linux cannot access it
|
||||
safely, reads return the literal string "<unsupported>".
|
||||
|
||||
Write the bitmask of bits to clear:
|
||||
|
||||
- 0x1 = clear bit 0 (desired performance excursion)
|
||||
- 0x2 = clear bit 1 (minimum performance excursion)
|
||||
- 0x3 = clear both bits
|
||||
Write 0x3 to clear both bits. Selectively clearing one bit is
|
||||
not supported because ACPI does not define the effect of writing
|
||||
one to these status bits and an interlocked read-modify-write is
|
||||
not generally available.
|
||||
Writing 0x3 returns -EOPNOTSUPP when firmware describes the
|
||||
register using an access which Linux cannot clear safely; reads
|
||||
may remain available in that case.
|
||||
|
||||
The platform sets these bits; OSPM can only clear them.
|
||||
|
||||
|
|
|
|||
13
MAINTAINERS
13
MAINTAINERS
|
|
@ -327,6 +327,17 @@ F: drivers/acpi/acpica/
|
|||
F: include/acpi/
|
||||
F: tools/power/acpi/
|
||||
|
||||
ACPI CPPC
|
||||
M: "Rafael J. Wysocki" <rafael@kernel.org>
|
||||
M: Christian Loehle <christian.loehle@arm.com>
|
||||
L: linux-acpi@vger.kernel.org
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
|
||||
F: Documentation/admin-guide/acpi/cppc_sysfs.rst
|
||||
F: drivers/acpi/cppc_acpi.c
|
||||
F: include/acpi/cppc_acpi.h
|
||||
|
||||
ACPI FOR ARM64 (ACPI/arm64)
|
||||
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
|
||||
M: Hanjun Guo <guohanjun@huawei.com>
|
||||
|
|
@ -6765,7 +6776,7 @@ M: "Rafael J. Wysocki" <rafael@kernel.org>
|
|||
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
R: Jie Zhan <zhanjie9@hisilicon.com>
|
||||
R: Lifeng Zheng <zhenglifeng1@huawei.com>
|
||||
R: Pierre Gondois <pierre.gondois@arm.com>
|
||||
R: Christian Loehle <christian.loehle@arm.com>
|
||||
R: Sumit Gupta <sumitg@nvidia.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ int counters_read_on_cpu(int cpu, smp_call_func_t func, void *val)
|
|||
return -EPERM;
|
||||
func(val);
|
||||
} else {
|
||||
smp_call_function_single(cpu, func, val, 1);
|
||||
return smp_call_function_single(cpu, func, val, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -470,6 +470,12 @@ static void amu_read_core_const_ctrs(void *val)
|
|||
cpu_read_corecnt(&ctrs->corecnt);
|
||||
}
|
||||
|
||||
static bool cpc_ffh_reg_valid(const struct cpc_reg *reg)
|
||||
{
|
||||
return reg->bit_width && reg->bit_width <= 64 &&
|
||||
reg->bit_offset <= 64 - reg->bit_width;
|
||||
}
|
||||
|
||||
static u64 cpc_ffh_extract_bits(const struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
val &= GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
@ -506,7 +512,8 @@ int cpc_read_ffh_fb_ctrs(int cpu, struct cpc_reg *reg1, u64 *val1,
|
|||
struct amu_ffh_ctrs ctrs;
|
||||
int ret;
|
||||
|
||||
if (!is_amu_ctr_reg(reg1) || !is_amu_ctr_reg(reg2))
|
||||
if (!is_amu_ctr_reg(reg1) || !is_amu_ctr_reg(reg2) ||
|
||||
!cpc_ffh_reg_valid(reg1) || !cpc_ffh_reg_valid(reg2))
|
||||
return -EINVAL;
|
||||
|
||||
ret = counters_read_on_cpu(cpu, amu_read_core_const_ctrs, &ctrs);
|
||||
|
|
@ -530,6 +537,9 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
switch ((u64)reg->address) {
|
||||
case CPC_FFH_CTR_CORE:
|
||||
ret = counters_read_on_cpu(cpu, cpu_read_corecnt, val);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <acpi/cppc_acpi.h>
|
||||
#include <asm/msr.h>
|
||||
|
|
@ -45,10 +46,20 @@ bool cpc_ffh_supported(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cpc_ffh_reg_valid(const struct cpc_reg *reg)
|
||||
{
|
||||
return reg->address <= U32_MAX && reg->bit_width &&
|
||||
reg->bit_width <= 64 &&
|
||||
reg->bit_offset <= 64 - reg->bit_width;
|
||||
}
|
||||
|
||||
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdmsrq_safe_on_cpu(cpunum, reg->address, val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
@ -65,6 +76,9 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
|
|||
u64 rd_val;
|
||||
int err;
|
||||
|
||||
if (!cpc_ffh_reg_valid(reg))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdmsrq_safe_on_cpu(cpunum, reg->address, &rd_val);
|
||||
if (!err) {
|
||||
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -97,6 +97,7 @@ bool cpc_ffh_supported(void)
|
|||
int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
||||
{
|
||||
struct sbi_cppc_data data;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(irqs_disabled()))
|
||||
return -EPERM;
|
||||
|
|
@ -107,19 +108,27 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
|
||||
data.reg = FFH_CPPC_SBI_REG(reg->address);
|
||||
|
||||
smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
|
||||
ret = smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.ret.error)
|
||||
return sbi_err_map_linux_errno(data.ret.error);
|
||||
|
||||
*val = data.ret.value;
|
||||
|
||||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
|
||||
return 0;
|
||||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
|
||||
data.reg = FFH_CPPC_CSR_NUM(reg->address);
|
||||
|
||||
smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
|
||||
ret = smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.ret.error)
|
||||
return data.ret.error;
|
||||
|
||||
*val = data.ret.value;
|
||||
|
||||
return data.ret.error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
|
@ -128,6 +137,7 @@ int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
|
|||
int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
|
||||
{
|
||||
struct sbi_cppc_data data;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(irqs_disabled()))
|
||||
return -EPERM;
|
||||
|
|
@ -139,14 +149,18 @@ int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
|
|||
data.reg = FFH_CPPC_SBI_REG(reg->address);
|
||||
data.val = val;
|
||||
|
||||
smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
|
||||
ret = smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
|
||||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
|
||||
data.reg = FFH_CPPC_CSR_NUM(reg->address);
|
||||
data.val = val;
|
||||
|
||||
smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
|
||||
ret = smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return data.ret.error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1069,19 +1069,26 @@ EXPORT_SYMBOL(acpi_dev_is_video_device);
|
|||
* @plat: pointer to acpi_platform_list table terminated by a NULL entry
|
||||
*
|
||||
* Return the matched index if the system is found in the platform list.
|
||||
* Otherwise, return a negative error code.
|
||||
* Return -ENODEV for no match, or another negative error code if a table
|
||||
* header could not be read and no entry matched.
|
||||
*/
|
||||
int acpi_match_platform_list(const struct acpi_platform_list *plat)
|
||||
{
|
||||
struct acpi_table_header hdr;
|
||||
acpi_status status;
|
||||
int ret = -ENODEV;
|
||||
int idx = 0;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
|
||||
for (; plat->oem_id[0]; plat++, idx++) {
|
||||
if (ACPI_FAILURE(acpi_get_table_header(plat->table, 0, &hdr)))
|
||||
status = acpi_get_table_header(plat->table, 0, &hdr);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
if (status != AE_NOT_FOUND)
|
||||
ret = status == AE_NO_MEMORY ? -ENOMEM : -EIO;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(plat->oem_id, hdr.oem_id, ACPI_OEM_ID_SIZE))
|
||||
continue;
|
||||
|
|
@ -1096,6 +1103,6 @@ int acpi_match_platform_list(const struct acpi_platform_list *plat)
|
|||
return idx;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_match_platform_list);
|
||||
|
|
|
|||
|
|
@ -544,7 +544,11 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
|
|||
u8 des_perf, u8 max_perf, u8 epp, bool fast_switch)
|
||||
{
|
||||
struct amd_cpudata *cpudata = policy->driver_data;
|
||||
struct cppc_perf_ctrls perf_ctrls;
|
||||
struct cppc_perf_ctrls perf_ctrls = {
|
||||
.max_perf = max_perf,
|
||||
.min_perf = min_perf,
|
||||
.desired_perf = des_perf,
|
||||
};
|
||||
u64 value, prev;
|
||||
int ret;
|
||||
|
||||
|
|
@ -577,10 +581,6 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
|
|||
if (value == prev)
|
||||
return 0;
|
||||
|
||||
perf_ctrls.max_perf = max_perf;
|
||||
perf_ctrls.min_perf = min_perf;
|
||||
perf_ctrls.desired_perf = des_perf;
|
||||
|
||||
ret = cppc_set_perf(cpudata->cpu, &perf_ctrls);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1794,7 +1794,7 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
|
|||
|
||||
int amd_pstate_update_status(const char *buf, size_t size)
|
||||
{
|
||||
int mode_idx;
|
||||
int cpu, mode_idx;
|
||||
|
||||
if (size > strlen("passive") || size < strlen("active"))
|
||||
return -EINVAL;
|
||||
|
|
@ -1803,12 +1803,23 @@ int amd_pstate_update_status(const char *buf, size_t size)
|
|||
if (mode_idx < 0)
|
||||
return mode_idx;
|
||||
|
||||
if (mode_state_machine[cppc_state][mode_idx]) {
|
||||
guard(mutex)(&amd_pstate_driver_lock);
|
||||
return mode_state_machine[cppc_state][mode_idx](mode_idx);
|
||||
guard(mutex)(&amd_pstate_driver_lock);
|
||||
|
||||
if (!mode_state_machine[cppc_state][mode_idx])
|
||||
return 0;
|
||||
|
||||
if (mode_idx == AMD_PSTATE_PASSIVE &&
|
||||
!cpu_feature_enabled(X86_FEATURE_CPPC)) {
|
||||
guard(cpus_read_lock)();
|
||||
|
||||
/* Check offline CPUs too, before changing or removing the driver. */
|
||||
for_each_present_cpu(cpu) {
|
||||
if (cppc_auto_sel_is_immutable(cpu))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return mode_state_machine[cppc_state][mode_idx](mode_idx);
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/cpufreq.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
|
@ -41,6 +42,7 @@ MODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)");
|
|||
/* Frequency invariance support */
|
||||
struct cppc_freq_invariance {
|
||||
int cpu;
|
||||
bool pcc_work_initialized;
|
||||
struct irq_work irq_work;
|
||||
struct kthread_work work;
|
||||
struct cppc_perf_fb_ctrs prev_perf_fb_ctrs;
|
||||
|
|
@ -49,10 +51,12 @@ struct cppc_freq_invariance {
|
|||
|
||||
static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv);
|
||||
static struct kthread_worker *kworker_fie;
|
||||
static DEFINE_MUTEX(cppc_fie_lock);
|
||||
|
||||
static int cppc_perf_from_fbctrs(u64 reference_perf,
|
||||
struct cppc_perf_fb_ctrs *fb_ctrs_t0,
|
||||
struct cppc_perf_fb_ctrs *fb_ctrs_t1);
|
||||
static int cppc_fie_kworker_init(void);
|
||||
|
||||
/**
|
||||
* __cppc_scale_freq_tick - CPPC arch_freq_scale updater for frequency invariance
|
||||
|
|
@ -149,7 +153,7 @@ static struct scale_freq_data cppc_sftd_pcc = {
|
|||
|
||||
static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct scale_freq_data *sftd = &cppc_sftd;
|
||||
struct scale_freq_data *sftd;
|
||||
struct cppc_freq_invariance *cppc_fi;
|
||||
int cpu, ret;
|
||||
|
||||
|
|
@ -161,9 +165,12 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
|||
cppc_fi->cpu = cpu;
|
||||
cppc_fi->cpu_data = policy->driver_data;
|
||||
if (cppc_perf_ctrs_in_pcc_cpu(cpu)) {
|
||||
if (cppc_fie_kworker_init())
|
||||
return;
|
||||
|
||||
kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
|
||||
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
|
||||
sftd = &cppc_sftd_pcc;
|
||||
cppc_fi->pcc_work_initialized = true;
|
||||
}
|
||||
|
||||
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
|
||||
|
|
@ -179,17 +186,20 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
|
|||
}
|
||||
}
|
||||
|
||||
/* Register for freq-invariance */
|
||||
topology_set_scale_freq_source(sftd, policy->cpus);
|
||||
/* A shared policy may contain both PCC and non-PCC counters. */
|
||||
for_each_cpu(cpu, policy->cpus) {
|
||||
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
|
||||
if (cppc_fi->pcc_work_initialized)
|
||||
sftd = &cppc_sftd_pcc;
|
||||
else
|
||||
sftd = &cppc_sftd;
|
||||
topology_set_scale_freq_source(sftd, cpumask_of(cpu));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We free all the resources on policy's removal and not on CPU removal as the
|
||||
* irq-work are per-cpu and the hotplug core takes care of flushing the pending
|
||||
* irq-works (hint: smpcfd_dying_cpu()) on CPU hotplug. Even if the kthread-work
|
||||
* fires on another CPU after the concerned CPU is removed, it won't harm.
|
||||
*
|
||||
* We just need to make sure to remove them all on policy->exit().
|
||||
* Drain work initialized by this policy even if processor removal has
|
||||
* already unpublished the CPU's CPC descriptor.
|
||||
*/
|
||||
static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
|
|
@ -203,16 +213,18 @@ static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
|
|||
topology_clear_scale_freq_source(SCALE_FREQ_SOURCE_CPPC, policy->related_cpus);
|
||||
|
||||
for_each_cpu(cpu, policy->related_cpus) {
|
||||
if (!cppc_perf_ctrs_in_pcc_cpu(cpu))
|
||||
continue;
|
||||
cppc_fi = &per_cpu(cppc_freq_inv, cpu);
|
||||
if (!cppc_fi->pcc_work_initialized)
|
||||
continue;
|
||||
irq_work_sync(&cppc_fi->irq_work);
|
||||
kthread_cancel_work_sync(&cppc_fi->work);
|
||||
cppc_fi->pcc_work_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void cppc_fie_kworker_init(void)
|
||||
static int cppc_fie_kworker_init(void)
|
||||
{
|
||||
struct kthread_worker *worker;
|
||||
struct sched_attr attr = {
|
||||
.size = sizeof(struct sched_attr),
|
||||
.sched_policy = SCHED_DEADLINE,
|
||||
|
|
@ -228,23 +240,28 @@ static void cppc_fie_kworker_init(void)
|
|||
};
|
||||
int ret;
|
||||
|
||||
kworker_fie = kthread_run_worker(0, "cppc_fie");
|
||||
if (IS_ERR(kworker_fie)) {
|
||||
guard(mutex)(&cppc_fie_lock);
|
||||
|
||||
if (kworker_fie)
|
||||
return 0;
|
||||
|
||||
worker = kthread_run_worker(0, "cppc_fie");
|
||||
if (IS_ERR(worker)) {
|
||||
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
|
||||
PTR_ERR(kworker_fie));
|
||||
fie_disabled = FIE_DISABLED;
|
||||
kworker_fie = NULL;
|
||||
return;
|
||||
PTR_ERR(worker));
|
||||
return PTR_ERR(worker);
|
||||
}
|
||||
|
||||
ret = sched_setattr_nocheck(kworker_fie->task, &attr);
|
||||
ret = sched_setattr_nocheck(worker->task, &attr);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__,
|
||||
ret);
|
||||
kthread_destroy_worker(kworker_fie);
|
||||
fie_disabled = FIE_DISABLED;
|
||||
kworker_fie = NULL;
|
||||
kthread_destroy_worker(worker);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kworker_fie = worker;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init cppc_freq_invariance_init(void)
|
||||
|
|
@ -259,11 +276,6 @@ static void __init cppc_freq_invariance_init(void)
|
|||
fie_disabled = FIE_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fie_disabled || !perf_ctrs_in_pcc)
|
||||
return;
|
||||
|
||||
cppc_fie_kworker_init();
|
||||
}
|
||||
|
||||
static void cppc_freq_invariance_exit(void)
|
||||
|
|
@ -883,6 +895,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct cppc_cpudata *cpu_data = policy->driver_data;
|
||||
bool old_auto_sel = cpu_data->perf_ctrls.auto_sel;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
|
|
@ -911,8 +924,8 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
|
|||
if (ret) {
|
||||
cpu_data->perf_ctrls.min_perf = old_min_perf;
|
||||
cpu_data->perf_ctrls.max_perf = old_max_perf;
|
||||
cppc_set_auto_sel(policy->cpu, false);
|
||||
cpu_data->perf_ctrls.auto_sel = false;
|
||||
cppc_set_auto_sel(policy->cpu, old_auto_sel);
|
||||
cpu_data->perf_ctrls.auto_sel = old_auto_sel;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,11 +72,16 @@ struct cpc_register_resource {
|
|||
struct {
|
||||
struct cpc_reg reg;
|
||||
bool use_rmw_lock;
|
||||
bool read_unsupported;
|
||||
bool write_unsupported;
|
||||
};
|
||||
u64 int_value;
|
||||
} cpc_entry;
|
||||
};
|
||||
|
||||
struct cpc_sysmem_node;
|
||||
struct cpc_non_mmio_node;
|
||||
|
||||
/* Container to hold the CPC details for each CPU */
|
||||
struct cpc_desc {
|
||||
int num_entries;
|
||||
|
|
@ -84,10 +89,12 @@ struct cpc_desc {
|
|||
int cpu_id;
|
||||
int write_cmd_status;
|
||||
int write_cmd_id;
|
||||
/* Lock used for RMW operations in cpc_write() */
|
||||
/* Serialize partial SystemMemory writes within this descriptor. */
|
||||
raw_spinlock_t rmw_lock;
|
||||
struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
|
||||
struct acpi_psd_package domain_info;
|
||||
struct cpc_sysmem_node *sysmem_nodes;
|
||||
struct cpc_non_mmio_node *non_mmio_nodes;
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
|
|
@ -141,6 +148,8 @@ struct cppc_perf_ctrls {
|
|||
u32 desired_perf;
|
||||
u32 energy_perf;
|
||||
bool auto_sel;
|
||||
/* Allow an explicit zero minimum; otherwise zero omits the update. */
|
||||
bool min_perf_valid;
|
||||
};
|
||||
|
||||
struct cppc_perf_fb_ctrs {
|
||||
|
|
@ -188,6 +197,7 @@ extern int cppc_set_epp(int cpu, u64 epp_val);
|
|||
extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
|
||||
extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
|
||||
extern int cppc_get_auto_sel(int cpu, bool *enable);
|
||||
bool cppc_auto_sel_is_immutable(int cpu);
|
||||
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);
|
||||
|
|
@ -289,6 +299,12 @@ static inline int cppc_get_auto_sel(int cpu, bool *enable)
|
|||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool cppc_auto_sel_is_immutable(int cpu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int cppc_set_auto_sel(int cpu, bool enable)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user