[CPUFREQ] Workaround for BIOS bug in software coordination of frequency
[pandora-kernel.git] / arch / i386 / kernel / cpu / cpufreq / acpi-cpufreq.c
index 11da3ca..ea19d09 100644 (file)
@@ -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>
@@ -174,7 +174,6 @@ acpi_processor_set_performance (
                        udelay(10);
                }
        } else {
-               i = 0;
                value = (u32) perf->states[state].status;
        }
 
@@ -372,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;
                        }
@@ -386,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)
@@ -419,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;
@@ -549,30 +589,26 @@ static struct freq_attr* acpi_cpufreq_attr[] = {
 };
 
 static struct cpufreq_driver acpi_cpufreq_driver = {
-       .verify         = acpi_cpufreq_verify,
-       .target         = acpi_cpufreq_target,
-       .init           = acpi_cpufreq_cpu_init,
-       .exit           = acpi_cpufreq_cpu_exit,
-       .resume         = acpi_cpufreq_resume,
-       .name           = "acpi-cpufreq",
-       .owner          = THIS_MODULE,
-       .attr           = acpi_cpufreq_attr,
+       .verify = acpi_cpufreq_verify,
+       .target = acpi_cpufreq_target,
+       .init   = acpi_cpufreq_cpu_init,
+       .exit   = acpi_cpufreq_cpu_exit,
+       .resume = acpi_cpufreq_resume,
+       .name   = "acpi-cpufreq",
+       .owner  = THIS_MODULE,
+       .attr   = acpi_cpufreq_attr,
+       .flags  = CPUFREQ_STICKY,
 };
 
 
 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;
        }