mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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:
1 parent
4fe446a429
commit
aed2b87eeb
4 files changed
+523
-504
No files matched your search
@@ -1,3 +1,4 @@
|
||||
obj-y += acpi.o
|
||||
obj-y += cpufreq.o
|
||||
obj-y += hwp.o
|
||||
obj-y += powernow.o
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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__ */
|
||||