diff options
Diffstat (limited to 'arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c')
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 05668e3598c0..ea19d091fd41 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -24,7 +24,6 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -33,6 +32,7 @@ #include <linux/seq_file.h> #include <linux/compiler.h> #include <linux/sched.h> /* current */ +#include <linux/dmi.h> #include <asm/io.h> #include <asm/delay.h> #include <asm/uaccess.h> @@ -371,11 +371,11 @@ static int acpi_cpufreq_early_init_acpi(void) dprintk("acpi_cpufreq_early_init\n"); - for_each_cpu(i) { + for_each_possible_cpu(i) { data = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); if (!data) { - for_each_cpu(j) { + for_each_possible_cpu(j) { kfree(acpi_perf_data[j]); acpi_perf_data[j] = NULL; } @@ -385,10 +385,36 @@ static int acpi_cpufreq_early_init_acpi(void) } /* Do initialization in ACPI core */ - acpi_processor_preregister_performance(acpi_perf_data); + return acpi_processor_preregister_performance(acpi_perf_data); +} + +/* + * Some BIOSes do SW_ANY coordination internally, either set it up in hw + * or do it in BIOS firmware and won't inform about it to OS. If not + * detected, this has a side effect of making CPU run at a different speed + * than OS intended it to run at. Detect it and handle it cleanly. + */ +static int bios_with_sw_any_bug; + +static int __init sw_any_bug_found(struct dmi_system_id *d) +{ + bios_with_sw_any_bug = 1; return 0; } +static struct dmi_system_id __initdata sw_any_bug_dmi_table[] = { + { + .callback = sw_any_bug_found, + .ident = "Supermicro Server X6DLP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), + DMI_MATCH(DMI_BIOS_VERSION, "080010"), + DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"), + }, + }, + { } +}; + static int acpi_cpufreq_cpu_init ( struct cpufreq_policy *policy) @@ -418,8 +444,23 @@ acpi_cpufreq_cpu_init ( goto err_free; perf = data->acpi_data; - policy->cpus = perf->shared_cpu_map; policy->shared_type = perf->shared_type; + /* + * Will let policy->cpus know about dependency only when software + * coordination is required. + */ + if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL || + policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { + policy->cpus = perf->shared_cpu_map; + } + +#ifdef CONFIG_SMP + dmi_check_system(sw_any_bug_dmi_table); + if (bios_with_sw_any_bug && cpus_weight(policy->cpus) == 1) { + policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; + policy->cpus = cpu_core_map[cpu]; + } +#endif if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; @@ -563,16 +604,11 @@ static struct cpufreq_driver acpi_cpufreq_driver = { static int __init acpi_cpufreq_init (void) { - int result = 0; - dprintk("acpi_cpufreq_init\n"); - result = acpi_cpufreq_early_init_acpi(); + acpi_cpufreq_early_init_acpi(); - if (!result) - result = cpufreq_register_driver(&acpi_cpufreq_driver); - - return (result); + return cpufreq_register_driver(&acpi_cpufreq_driver); } @@ -584,7 +620,7 @@ acpi_cpufreq_exit (void) cpufreq_unregister_driver(&acpi_cpufreq_driver); - for_each_cpu(i) { + for_each_possible_cpu(i) { kfree(acpi_perf_data[i]); acpi_perf_data[i] = NULL; } |