diff options
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 155 |
1 files changed, 139 insertions, 16 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index ebc9fe28574..07fd97ba4c7 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -39,6 +39,7 @@ #include <acpi/processor.h> #include <asm/io.h> +#include <asm/msr.h> #include <asm/processor.h> #include <asm/cpufeature.h> #include <asm/delay.h> @@ -51,10 +52,19 @@ MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); +enum { + UNDEFINED_CAPABLE = 0, + SYSTEM_INTEL_MSR_CAPABLE, + SYSTEM_IO_CAPABLE, +}; + +#define INTEL_MSR_RANGE (0xffff) + struct acpi_cpufreq_data { struct acpi_processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; unsigned int resume; + unsigned int cpu_feature; }; static struct acpi_cpufreq_data *drv_data[NR_CPUS]; @@ -64,7 +74,20 @@ static struct cpufreq_driver acpi_cpufreq_driver; static unsigned int acpi_pstate_strict; -static unsigned extract_freq(u32 value, struct acpi_cpufreq_data *data) + +static int check_est_cpu(unsigned int cpuid) +{ + struct cpuinfo_x86 *cpu = &cpu_data[cpuid]; + + if (cpu->x86_vendor != X86_VENDOR_INTEL || + !cpu_has(cpu, X86_FEATURE_EST)) + return 0; + + return 1; +} + + +static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data) { struct acpi_processor_performance *perf; int i; @@ -79,6 +102,31 @@ static unsigned extract_freq(u32 value, struct acpi_cpufreq_data *data) } +static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) +{ + int i; + + msr &= INTEL_MSR_RANGE; + for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { + if (msr == data->freq_table[i].index) + 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->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + return extract_msr(val, data); + case SYSTEM_IO_CAPABLE: + return extract_io(val, data); + default: + return 0; + } +} + static void wrport(u16 port, u8 bit_width, u32 value) { if (bit_width <= 8) { @@ -102,27 +150,57 @@ static void rdport(u16 port, u8 bit_width, u32 *ret) } } +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; cpumask_t mask; - struct io_addr addr; + drv_addr_union addr; u32 val; }; static void do_drv_read(struct drv_cmd *cmd) { - rdport(cmd->addr.port, cmd->addr.bit_width, &cmd->val); - return; + u32 h; + + switch (cmd->type) { + case SYSTEM_INTEL_MSR_CAPABLE: + rdmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_IO_CAPABLE: + rdport(cmd->addr.io.port, cmd->addr.io.bit_width, &cmd->val); + break; + default: + break; + } } static void do_drv_write(struct drv_cmd *cmd) { - wrport(cmd->addr.port, cmd->addr.bit_width, cmd->val); - return; + u32 h = 0; + + switch (cmd->type) { + case SYSTEM_INTEL_MSR_CAPABLE: + wrmsr(cmd->addr.msr.reg, cmd->val, h); + break; + case SYSTEM_IO_CAPABLE: + wrport(cmd->addr.io.port, cmd->addr.io.bit_width, cmd->val); + break; + default: + break; + } } static inline void drv_read(struct drv_cmd *cmd) @@ -158,9 +236,21 @@ static u32 get_cur_val(cpumask_t mask) if (unlikely(cpus_empty(mask))) return 0; - perf = drv_data[first_cpu(mask)]->acpi_data; - cmd.addr.port = perf->control_register.address; - cmd.addr.bit_width = perf->control_register.bit_width; + switch (drv_data[first_cpu(mask)]->cpu_feature) { + 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 = drv_data[first_cpu(mask)]->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 = mask; drv_read(&cmd); @@ -213,6 +303,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, struct cpufreq_freqs freqs; cpumask_t online_policy_cpus; struct drv_cmd cmd; + unsigned int msr; unsigned int next_state = 0; unsigned int next_perf_state = 0; unsigned int i; @@ -256,9 +347,22 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, } } - cmd.addr.port = perf->control_register.address; - cmd.addr.bit_width = perf->control_register.bit_width; - cmd.val = (u32) perf->states[next_perf_state].control; + switch (data->cpu_feature) { + case SYSTEM_INTEL_MSR_CAPABLE: + cmd.type = SYSTEM_INTEL_MSR_CAPABLE; + cmd.addr.msr.reg = MSR_IA32_PERF_CTL; + msr = (u32) perf->states[next_perf_state].control & INTEL_MSR_RANGE; + cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr; + 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; + } cpus_clear(cmd.mask); @@ -405,6 +509,7 @@ acpi_cpufreq_cpu_init ( unsigned int valid_states = 0; unsigned int cpu = policy->cpu; struct acpi_cpufreq_data *data; + unsigned int l, h; unsigned int result = 0; struct cpuinfo_x86 *c = &cpu_data[policy->cpu]; struct acpi_processor_performance *perf; @@ -463,6 +568,15 @@ acpi_cpufreq_cpu_init ( switch (perf->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: dprintk("SYSTEM IO addr space\n"); + data->cpu_feature = SYSTEM_IO_CAPABLE; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + dprintk("HARDWARE addr space\n"); + if (!check_est_cpu(cpu)) { + result = -ENODEV; + goto err_unreg; + } + data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; break; default: dprintk("Unknown addr space %d\n", @@ -485,9 +599,6 @@ acpi_cpufreq_cpu_init ( } policy->governor = CPUFREQ_DEFAULT_GOVERNOR; - /* The current speed is unknown and not detectable by ACPI... */ - policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu); - /* table init */ for (i=0; i<perf->state_count; i++) { @@ -507,6 +618,18 @@ acpi_cpufreq_cpu_init ( goto err_freqfree; } + switch (data->cpu_feature) { + 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: + get_cur_freq_on_cpu(cpu); + break; + default: + break; + } + /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); @@ -599,7 +722,7 @@ acpi_cpufreq_init (void) acpi_cpufreq_early_init(); - return cpufreq_register_driver(&acpi_cpufreq_driver); + return cpufreq_register_driver(&acpi_cpufreq_driver); } |