summaryrefslogtreecommitdiff
path: root/arch/i386/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r--arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c155
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 ebc9fe285748..07fd97ba4c7a 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);
}