x86/cpufreq: move ACPI cpufreq driver into separate file

Separate ACPI driver from generic initialization cpufreq code.
This way acpi-cpufreq can become optional in the future and be disabled
from non-Intel builds.

no changes to code were introduced, except:
  acpi_cpufreq_register() helper added
  clean up a list of included headers
  license transformed into an SPDX line

Signed-off-by: Sergiy Kibrik <Sergiy_Kibrik@epam.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Sergiy Kibrik authored and Jan Beulich committed 2024-08-01 09:40:12 +02:00
1 parent 4fe446a429
commit aed2b87eeb
4 files changed
+523 -504

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
obj-y += acpi.o
obj-y += cpufreq.o
obj-y += hwp.o
obj-y += powernow.o
+518
View File
@@ -0,0 +1,518 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
* Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com>
*
* Feb 2008 - Liu Jinsong <jinsong.liu@intel.com>
* porting acpi-cpufreq.c from Linux 2.6.23 to Xen hypervisor
*/
#include <xen/errno.h>
#include <xen/delay.h>
#include <xen/param.h>
#include <xen/types.h>
#include <acpi/acpi.h>
#include <acpi/cpufreq/cpufreq.h>
enum {
UNDEFINED_CAPABLE = 0,
SYSTEM_INTEL_MSR_CAPABLE,
SYSTEM_IO_CAPABLE,
};
#define INTEL_MSR_RANGE (0xffffull)
static bool __read_mostly acpi_pstate_strict;
boolean_param("acpi_pstate_strict", acpi_pstate_strict);
static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
{
struct processor_performance *perf;
int i;
perf = data->acpi_data;
for (i=0; i<perf->state_count; i++) {
if (value == perf->states[i].status)
return data->freq_table[i].frequency;
}
return 0;
}
static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
{
int i;
struct processor_performance *perf;
msr &= INTEL_MSR_RANGE;
perf = data->acpi_data;
for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (msr == perf->states[data->freq_table[i].index].status)
return data->freq_table[i].frequency;
}
return data->freq_table[0].frequency;
}
static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
{
switch (data->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
return extract_msr(val, data);
case SYSTEM_IO_CAPABLE:
return extract_io(val, data);
default:
return 0;
}
}
struct msr_addr {
u32 reg;
};
struct io_addr {
u16 port;
u8 bit_width;
};
typedef union {
struct msr_addr msr;
struct io_addr io;
} drv_addr_union;
struct drv_cmd {
unsigned int type;
const cpumask_t *mask;
drv_addr_union addr;
u32 val;
};
static void cf_check do_drv_read(void *drvcmd)
{
struct drv_cmd *cmd;
cmd = (struct drv_cmd *)drvcmd;
switch (cmd->type) {
case SYSTEM_INTEL_MSR_CAPABLE:
rdmsrl(cmd->addr.msr.reg, cmd->val);
break;
case SYSTEM_IO_CAPABLE:
acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
&cmd->val, (u32)cmd->addr.io.bit_width);
break;
default:
break;
}
}
static void cf_check do_drv_write(void *drvcmd)
{
struct drv_cmd *cmd;
uint64_t msr_content;
cmd = (struct drv_cmd *)drvcmd;
switch (cmd->type) {
case SYSTEM_INTEL_MSR_CAPABLE:
rdmsrl(cmd->addr.msr.reg, msr_content);
msr_content = (msr_content & ~INTEL_MSR_RANGE)
| (cmd->val & INTEL_MSR_RANGE);
wrmsrl(cmd->addr.msr.reg, msr_content);
break;
case SYSTEM_IO_CAPABLE:
acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
cmd->val, (u32)cmd->addr.io.bit_width);
break;
default:
break;
}
}
static void drv_read(struct drv_cmd *cmd)
{
cmd->val = 0;
ASSERT(cpumask_weight(cmd->mask) == 1);
/* to reduce IPI for the sake of performance */
if (likely(cpumask_test_cpu(smp_processor_id(), cmd->mask)))
do_drv_read((void *)cmd);
else
on_selected_cpus(cmd->mask, do_drv_read, cmd, 1);
}
static void drv_write(struct drv_cmd *cmd)
{
if (cpumask_equal(cmd->mask, cpumask_of(smp_processor_id())))
do_drv_write((void *)cmd);
else
on_selected_cpus(cmd->mask, do_drv_write, cmd, 1);
}
static u32 get_cur_val(const cpumask_t *mask)
{
struct cpufreq_policy *policy;
struct processor_performance *perf;
struct drv_cmd cmd;
unsigned int cpu = smp_processor_id();
if (unlikely(cpumask_empty(mask)))
return 0;
if (!cpumask_test_cpu(cpu, mask))
cpu = cpumask_first(mask);
if (cpu >= nr_cpu_ids || !cpu_online(cpu))
return 0;
policy = per_cpu(cpufreq_cpu_policy, cpu);
if (!policy || !cpufreq_drv_data[policy->cpu])
return 0;
switch (cpufreq_drv_data[policy->cpu]->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
break;
case SYSTEM_IO_CAPABLE:
cmd.type = SYSTEM_IO_CAPABLE;
perf = cpufreq_drv_data[policy->cpu]->acpi_data;
cmd.addr.io.port = perf->control_register.address;
cmd.addr.io.bit_width = perf->control_register.bit_width;
break;
default:
return 0;
}
cmd.mask = cpumask_of(cpu);
drv_read(&cmd);
return cmd.val;
}
static unsigned int cf_check get_cur_freq_on_cpu(unsigned int cpu)
{
struct cpufreq_policy *policy;
struct acpi_cpufreq_data *data;
if (!cpu_online(cpu))
return 0;
policy = per_cpu(cpufreq_cpu_policy, cpu);
if (!policy)
return 0;
data = cpufreq_drv_data[policy->cpu];
if (unlikely(data == NULL ||
data->acpi_data == NULL || data->freq_table == NULL))
return 0;
return extract_freq(get_cur_val(cpumask_of(cpu)), data);
}
void intel_feature_detect(struct cpufreq_policy *policy)
{
unsigned int eax;
eax = cpuid_eax(6);
if (eax & 0x2) {
policy->turbo = CPUFREQ_TURBO_ENABLED;
if (cpufreq_verbose)
printk(XENLOG_INFO "CPU%u: Turbo Mode detected and enabled\n",
smp_processor_id());
}
}
static void cf_check feature_detect(void *info)
{
intel_feature_detect(info);
}
static unsigned int check_freqs(const cpumask_t *mask, unsigned int freq,
struct acpi_cpufreq_data *data)
{
unsigned int cur_freq;
unsigned int i;
for (i=0; i<100; i++) {
cur_freq = extract_freq(get_cur_val(mask), data);
if (cur_freq == freq)
return 1;
udelay(10);
}
return 0;
}
static int cf_check acpi_cpufreq_target(
struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
struct processor_performance *perf;
struct cpufreq_freqs freqs;
cpumask_t online_policy_cpus;
struct drv_cmd cmd;
unsigned int next_state = 0; /* Index into freq_table */
unsigned int next_perf_state = 0; /* Index into perf table */
unsigned int j;
int result = 0;
if (unlikely(data == NULL ||
data->acpi_data == NULL || data->freq_table == NULL)) {
return -ENODEV;
}
if (policy->turbo == CPUFREQ_TURBO_DISABLED)
if (target_freq > policy->cpuinfo.second_max_freq)
target_freq = policy->cpuinfo.second_max_freq;
perf = data->acpi_data;
result = cpufreq_frequency_table_target(policy,
data->freq_table,
target_freq,
relation, &next_state);
if (unlikely(result))
return -ENODEV;
cpumask_and(&online_policy_cpus, &cpu_online_map, policy->cpus);
next_perf_state = data->freq_table[next_state].index;
if (perf->state == next_perf_state) {
if (unlikely(policy->resume))
policy->resume = 0;
else
return 0;
}
switch (data->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
cmd.val = (u32) perf->states[next_perf_state].control;
break;
case SYSTEM_IO_CAPABLE:
cmd.type = SYSTEM_IO_CAPABLE;
cmd.addr.io.port = perf->control_register.address;
cmd.addr.io.bit_width = perf->control_register.bit_width;
cmd.val = (u32) perf->states[next_perf_state].control;
break;
default:
return -ENODEV;
}
if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
cmd.mask = &online_policy_cpus;
else
cmd.mask = cpumask_of(policy->cpu);
freqs.old = perf->states[perf->state].core_frequency * 1000;
freqs.new = data->freq_table[next_state].frequency;
drv_write(&cmd);
if (acpi_pstate_strict && !check_freqs(cmd.mask, freqs.new, data)) {
printk(KERN_WARNING "Fail transfer to new freq %d\n", freqs.new);
return -EAGAIN;
}
for_each_cpu(j, &online_policy_cpus)
cpufreq_statistic_update(j, perf->state, next_perf_state);
perf->state = next_perf_state;
policy->cur = freqs.new;
return result;
}
static int cf_check acpi_cpufreq_verify(struct cpufreq_policy *policy)
{
struct acpi_cpufreq_data *data;
struct processor_performance *perf;
if (!policy || !(data = cpufreq_drv_data[policy->cpu]) ||
!processor_pminfo[policy->cpu])
return -EINVAL;
perf = &processor_pminfo[policy->cpu]->perf;
cpufreq_verify_within_limits(policy, 0,
perf->states[perf->platform_limit].core_frequency * 1000);
return cpufreq_frequency_table_verify(policy, data->freq_table);
}
static unsigned long
acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
{
struct processor_performance *perf = data->acpi_data;
if (cpu_khz) {
/* search the closest match to cpu_khz */
unsigned int i;
unsigned long freq;
unsigned long freqn = perf->states[0].core_frequency * 1000;
for (i=0; i<(perf->state_count-1); i++) {
freq = freqn;
freqn = perf->states[i+1].core_frequency * 1000;
if ((2 * cpu_khz) > (freqn + freq)) {
perf->state = i;
return freq;
}
}
perf->state = perf->state_count-1;
return freqn;
} else {
/* assume CPU is at P0... */
perf->state = 0;
return perf->states[0].core_frequency * 1000;
}
}
static int cf_check acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int i;
unsigned int valid_states = 0;
unsigned int cpu = policy->cpu;
struct acpi_cpufreq_data *data;
unsigned int result = 0;
struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
struct processor_performance *perf;
data = xzalloc(struct acpi_cpufreq_data);
if (!data)
return -ENOMEM;
cpufreq_drv_data[cpu] = data;
data->acpi_data = &processor_pminfo[cpu]->perf;
perf = data->acpi_data;
policy->shared_type = perf->shared_type;
switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
if (cpufreq_verbose)
printk("xen_pminfo: @acpi_cpufreq_cpu_init,"
"SYSTEM IO addr space\n");
data->arch_cpu_flags = SYSTEM_IO_CAPABLE;
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
if (cpufreq_verbose)
printk("xen_pminfo: @acpi_cpufreq_cpu_init,"
"HARDWARE addr space\n");
if (!cpu_has(c, X86_FEATURE_EIST)) {
result = -ENODEV;
goto err_unreg;
}
data->arch_cpu_flags = SYSTEM_INTEL_MSR_CAPABLE;
break;
default:
result = -ENODEV;
goto err_unreg;
}
data->freq_table = xmalloc_array(struct cpufreq_frequency_table,
(perf->state_count+1));
if (!data->freq_table) {
result = -ENOMEM;
goto err_unreg;
}
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
for (i=0; i<perf->state_count; i++) {
if ((perf->states[i].transition_latency * 1000) >
policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency =
perf->states[i].transition_latency * 1000;
}
policy->governor = cpufreq_opt_governor ? : CPUFREQ_DEFAULT_GOVERNOR;
/* table init */
for (i=0; i<perf->state_count; i++) {
if (i>0 && perf->states[i].core_frequency >=
data->freq_table[valid_states-1].frequency / 1000)
continue;
data->freq_table[valid_states].index = i;
data->freq_table[valid_states].frequency =
perf->states[i].core_frequency * 1000;
valid_states++;
}
data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
perf->state = 0;
result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
if (result)
goto err_freqfree;
switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
/* Current speed is unknown and not detectable by IO port */
policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
cpufreq_driver.get = get_cur_freq_on_cpu;
policy->cur = get_cur_freq_on_cpu(cpu);
break;
default:
break;
}
/* Check for APERF/MPERF support in hardware
* also check for boost support */
if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6)
on_selected_cpus(cpumask_of(cpu), feature_detect, policy, 1);
/*
* the first call to ->target() should result in us actually
* writing something to the appropriate registers.
*/
policy->resume = 1;
return result;
err_freqfree:
xfree(data->freq_table);
err_unreg:
xfree(data);
cpufreq_drv_data[cpu] = NULL;
return result;
}
static int cf_check acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
if (data) {
cpufreq_drv_data[policy->cpu] = NULL;
xfree(data->freq_table);
xfree(data);
}
return 0;
}
static const struct cpufreq_driver __initconst_cf_clobber
acpi_cpufreq_driver = {
.name = "acpi-cpufreq",
.verify = acpi_cpufreq_verify,
.target = acpi_cpufreq_target,
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.get = get_cur_freq_on_cpu,
};
int __init acpi_cpufreq_register(void)
{
return cpufreq_register_driver(&acpi_cpufreq_driver);
}
+2 -504
View File
@@ -26,200 +26,14 @@
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <xen/types.h>
#include <xen/errno.h>
#include <xen/delay.h>
#include <xen/cpumask.h>
#include <xen/param.h>
#include <xen/sched.h>
#include <xen/timer.h>
#include <xen/xmalloc.h>
#include <asm/msr.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <acpi/acpi.h>
#include <acpi/cpufreq/cpufreq.h>
enum {
UNDEFINED_CAPABLE = 0,
SYSTEM_INTEL_MSR_CAPABLE,
SYSTEM_IO_CAPABLE,
};
#define INTEL_MSR_RANGE (0xffffull)
struct acpi_cpufreq_data *cpufreq_drv_data[NR_CPUS];
static bool __read_mostly acpi_pstate_strict;
boolean_param("acpi_pstate_strict", acpi_pstate_strict);
static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
{
struct processor_performance *perf;
int i;
perf = data->acpi_data;
for (i=0; i<perf->state_count; i++) {
if (value == perf->states[i].status)
return data->freq_table[i].frequency;
}
return 0;
}
static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
{
int i;
struct processor_performance *perf;
msr &= INTEL_MSR_RANGE;
perf = data->acpi_data;
for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (msr == perf->states[data->freq_table[i].index].status)
return data->freq_table[i].frequency;
}
return data->freq_table[0].frequency;
}
static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
{
switch (data->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
return extract_msr(val, data);
case SYSTEM_IO_CAPABLE:
return extract_io(val, data);
default:
return 0;
}
}
struct msr_addr {
u32 reg;
};
struct io_addr {
u16 port;
u8 bit_width;
};
typedef union {
struct msr_addr msr;
struct io_addr io;
} drv_addr_union;
struct drv_cmd {
unsigned int type;
const cpumask_t *mask;
drv_addr_union addr;
u32 val;
};
static void cf_check do_drv_read(void *drvcmd)
{
struct drv_cmd *cmd;
cmd = (struct drv_cmd *)drvcmd;
switch (cmd->type) {
case SYSTEM_INTEL_MSR_CAPABLE:
rdmsrl(cmd->addr.msr.reg, cmd->val);
break;
case SYSTEM_IO_CAPABLE:
acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
&cmd->val, (u32)cmd->addr.io.bit_width);
break;
default:
break;
}
}
static void cf_check do_drv_write(void *drvcmd)
{
struct drv_cmd *cmd;
uint64_t msr_content;
cmd = (struct drv_cmd *)drvcmd;
switch (cmd->type) {
case SYSTEM_INTEL_MSR_CAPABLE:
rdmsrl(cmd->addr.msr.reg, msr_content);
msr_content = (msr_content & ~INTEL_MSR_RANGE)
| (cmd->val & INTEL_MSR_RANGE);
wrmsrl(cmd->addr.msr.reg, msr_content);
break;
case SYSTEM_IO_CAPABLE:
acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
cmd->val, (u32)cmd->addr.io.bit_width);
break;
default:
break;
}
}
static void drv_read(struct drv_cmd *cmd)
{
cmd->val = 0;
ASSERT(cpumask_weight(cmd->mask) == 1);
/* to reduce IPI for the sake of performance */
if (likely(cpumask_test_cpu(smp_processor_id(), cmd->mask)))
do_drv_read((void *)cmd);
else
on_selected_cpus(cmd->mask, do_drv_read, cmd, 1);
}
static void drv_write(struct drv_cmd *cmd)
{
if (cpumask_equal(cmd->mask, cpumask_of(smp_processor_id())))
do_drv_write((void *)cmd);
else
on_selected_cpus(cmd->mask, do_drv_write, cmd, 1);
}
static u32 get_cur_val(const cpumask_t *mask)
{
struct cpufreq_policy *policy;
struct processor_performance *perf;
struct drv_cmd cmd;
unsigned int cpu = smp_processor_id();
if (unlikely(cpumask_empty(mask)))
return 0;
if (!cpumask_test_cpu(cpu, mask))
cpu = cpumask_first(mask);
if (cpu >= nr_cpu_ids || !cpu_online(cpu))
return 0;
policy = per_cpu(cpufreq_cpu_policy, cpu);
if (!policy || !cpufreq_drv_data[policy->cpu])
return 0;
switch (cpufreq_drv_data[policy->cpu]->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
break;
case SYSTEM_IO_CAPABLE:
cmd.type = SYSTEM_IO_CAPABLE;
perf = cpufreq_drv_data[policy->cpu]->acpi_data;
cmd.addr.io.port = perf->control_register.address;
cmd.addr.io.bit_width = perf->control_register.bit_width;
break;
default:
return 0;
}
cmd.mask = cpumask_of(cpu);
drv_read(&cmd);
return cmd.val;
}
struct perf_pair {
union {
struct {
@@ -255,7 +69,7 @@ static void cf_check read_measured_perf_ctrs(void *_readin)
*/
unsigned int get_measured_perf(unsigned int cpu, unsigned int flag)
{
struct cpufreq_policy *policy;
struct cpufreq_policy *policy;
struct perf_pair readin, cur, *saved;
unsigned int perf_percent;
@@ -308,322 +122,6 @@ unsigned int get_measured_perf(unsigned int cpu, unsigned int flag)
return policy->cpuinfo.perf_freq * perf_percent / 100;
}
static unsigned int cf_check get_cur_freq_on_cpu(unsigned int cpu)
{
struct cpufreq_policy *policy;
struct acpi_cpufreq_data *data;
if (!cpu_online(cpu))
return 0;
policy = per_cpu(cpufreq_cpu_policy, cpu);
if (!policy)
return 0;
data = cpufreq_drv_data[policy->cpu];
if (unlikely(data == NULL ||
data->acpi_data == NULL || data->freq_table == NULL))
return 0;
return extract_freq(get_cur_val(cpumask_of(cpu)), data);
}
void intel_feature_detect(struct cpufreq_policy *policy)
{
unsigned int eax;
eax = cpuid_eax(6);
if (eax & 0x2) {
policy->turbo = CPUFREQ_TURBO_ENABLED;
if (cpufreq_verbose)
printk(XENLOG_INFO "CPU%u: Turbo Mode detected and enabled\n",
smp_processor_id());
}
}
static void cf_check feature_detect(void *info)
{
intel_feature_detect(info);
}
static unsigned int check_freqs(const cpumask_t *mask, unsigned int freq,
struct acpi_cpufreq_data *data)
{
unsigned int cur_freq;
unsigned int i;
for (i=0; i<100; i++) {
cur_freq = extract_freq(get_cur_val(mask), data);
if (cur_freq == freq)
return 1;
udelay(10);
}
return 0;
}
static int cf_check acpi_cpufreq_target(
struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
struct processor_performance *perf;
struct cpufreq_freqs freqs;
cpumask_t online_policy_cpus;
struct drv_cmd cmd;
unsigned int next_state = 0; /* Index into freq_table */
unsigned int next_perf_state = 0; /* Index into perf table */
unsigned int j;
int result = 0;
if (unlikely(data == NULL ||
data->acpi_data == NULL || data->freq_table == NULL)) {
return -ENODEV;
}
if (policy->turbo == CPUFREQ_TURBO_DISABLED)
if (target_freq > policy->cpuinfo.second_max_freq)
target_freq = policy->cpuinfo.second_max_freq;
perf = data->acpi_data;
result = cpufreq_frequency_table_target(policy,
data->freq_table,
target_freq,
relation, &next_state);
if (unlikely(result))
return -ENODEV;
cpumask_and(&online_policy_cpus, &cpu_online_map, policy->cpus);
next_perf_state = data->freq_table[next_state].index;
if (perf->state == next_perf_state) {
if (unlikely(policy->resume))
policy->resume = 0;
else
return 0;
}
switch (data->arch_cpu_flags) {
case SYSTEM_INTEL_MSR_CAPABLE:
cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
cmd.val = (u32) perf->states[next_perf_state].control;
break;
case SYSTEM_IO_CAPABLE:
cmd.type = SYSTEM_IO_CAPABLE;
cmd.addr.io.port = perf->control_register.address;
cmd.addr.io.bit_width = perf->control_register.bit_width;
cmd.val = (u32) perf->states[next_perf_state].control;
break;
default:
return -ENODEV;
}
if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
cmd.mask = &online_policy_cpus;
else
cmd.mask = cpumask_of(policy->cpu);
freqs.old = perf->states[perf->state].core_frequency * 1000;
freqs.new = data->freq_table[next_state].frequency;
drv_write(&cmd);
if (acpi_pstate_strict && !check_freqs(cmd.mask, freqs.new, data)) {
printk(KERN_WARNING "Fail transfer to new freq %d\n", freqs.new);
return -EAGAIN;
}
for_each_cpu(j, &online_policy_cpus)
cpufreq_statistic_update(j, perf->state, next_perf_state);
perf->state = next_perf_state;
policy->cur = freqs.new;
return result;
}
static int cf_check acpi_cpufreq_verify(struct cpufreq_policy *policy)
{
struct acpi_cpufreq_data *data;
struct processor_performance *perf;
if (!policy || !(data = cpufreq_drv_data[policy->cpu]) ||
!processor_pminfo[policy->cpu])
return -EINVAL;
perf = &processor_pminfo[policy->cpu]->perf;
cpufreq_verify_within_limits(policy, 0,
perf->states[perf->platform_limit].core_frequency * 1000);
return cpufreq_frequency_table_verify(policy, data->freq_table);
}
static unsigned long
acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
{
struct processor_performance *perf = data->acpi_data;
if (cpu_khz) {
/* search the closest match to cpu_khz */
unsigned int i;
unsigned long freq;
unsigned long freqn = perf->states[0].core_frequency * 1000;
for (i=0; i<(perf->state_count-1); i++) {
freq = freqn;
freqn = perf->states[i+1].core_frequency * 1000;
if ((2 * cpu_khz) > (freqn + freq)) {
perf->state = i;
return freq;
}
}
perf->state = perf->state_count-1;
return freqn;
} else {
/* assume CPU is at P0... */
perf->state = 0;
return perf->states[0].core_frequency * 1000;
}
}
static int cf_check acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int i;
unsigned int valid_states = 0;
unsigned int cpu = policy->cpu;
struct acpi_cpufreq_data *data;
unsigned int result = 0;
struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
struct processor_performance *perf;
data = xzalloc(struct acpi_cpufreq_data);
if (!data)
return -ENOMEM;
cpufreq_drv_data[cpu] = data;
data->acpi_data = &processor_pminfo[cpu]->perf;
perf = data->acpi_data;
policy->shared_type = perf->shared_type;
switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
if (cpufreq_verbose)
printk("xen_pminfo: @acpi_cpufreq_cpu_init,"
"SYSTEM IO addr space\n");
data->arch_cpu_flags = SYSTEM_IO_CAPABLE;
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
if (cpufreq_verbose)
printk("xen_pminfo: @acpi_cpufreq_cpu_init,"
"HARDWARE addr space\n");
if (!cpu_has(c, X86_FEATURE_EIST)) {
result = -ENODEV;
goto err_unreg;
}
data->arch_cpu_flags = SYSTEM_INTEL_MSR_CAPABLE;
break;
default:
result = -ENODEV;
goto err_unreg;
}
data->freq_table = xmalloc_array(struct cpufreq_frequency_table,
(perf->state_count+1));
if (!data->freq_table) {
result = -ENOMEM;
goto err_unreg;
}
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
for (i=0; i<perf->state_count; i++) {
if ((perf->states[i].transition_latency * 1000) >
policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency =
perf->states[i].transition_latency * 1000;
}
policy->governor = cpufreq_opt_governor ? : CPUFREQ_DEFAULT_GOVERNOR;
/* table init */
for (i=0; i<perf->state_count; i++) {
if (i>0 && perf->states[i].core_frequency >=
data->freq_table[valid_states-1].frequency / 1000)
continue;
data->freq_table[valid_states].index = i;
data->freq_table[valid_states].frequency =
perf->states[i].core_frequency * 1000;
valid_states++;
}
data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
perf->state = 0;
result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
if (result)
goto err_freqfree;
switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
/* Current speed is unknown and not detectable by IO port */
policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
cpufreq_driver.get = get_cur_freq_on_cpu;
policy->cur = get_cur_freq_on_cpu(cpu);
break;
default:
break;
}
/* Check for APERF/MPERF support in hardware
* also check for boost support */
if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6)
on_selected_cpus(cpumask_of(cpu), feature_detect, policy, 1);
/*
* the first call to ->target() should result in us actually
* writing something to the appropriate registers.
*/
policy->resume = 1;
return result;
err_freqfree:
xfree(data->freq_table);
err_unreg:
xfree(data);
cpufreq_drv_data[cpu] = NULL;
return result;
}
static int cf_check acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct acpi_cpufreq_data *data = cpufreq_drv_data[policy->cpu];
if (data) {
cpufreq_drv_data[policy->cpu] = NULL;
xfree(data->freq_table);
xfree(data);
}
return 0;
}
static const struct cpufreq_driver __initconst_cf_clobber
acpi_cpufreq_driver = {
.name = "acpi-cpufreq",
.verify = acpi_cpufreq_verify,
.target = acpi_cpufreq_target,
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.get = get_cur_freq_on_cpu,
};
static int __init cf_check cpufreq_driver_init(void)
{
int ret = 0;
@@ -640,7 +138,7 @@ static int __init cf_check cpufreq_driver_init(void)
switch ( cpufreq_xen_opts[i] )
{
case CPUFREQ_xen:
ret = cpufreq_register_driver(&acpi_cpufreq_driver);
ret = acpi_cpufreq_register();
break;
case CPUFREQ_hwp:
ret = hwp_register_driver();
+2
View File
@@ -260,4 +260,6 @@ int get_hwp_para(unsigned int cpu,
int set_hwp_para(struct cpufreq_policy *policy,
struct xen_set_cppc_para *set_cppc);
int acpi_cpufreq_register(void);
#endif /* __XEN_CPUFREQ_PM_H__ */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.