Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / arch / i386 / kernel / apic.c
index 3d4b2f3..5bec4fb 100644 (file)
@@ -14,7 +14,6 @@
  *     Mikael Pettersson       :       PM converted to driver model.
  */
 
-#include <linux/config.h>
 #include <linux/init.h>
 
 #include <linux/mm.h>
@@ -27,6 +26,7 @@
 #include <linux/sysdev.h>
 #include <linux/cpu.h>
 #include <linux/module.h>
+#include <linux/dmi.h>
 
 #include <asm/atomic.h>
 #include <asm/smp.h>
@@ -36,6 +36,7 @@
 #include <asm/arch_hooks.h>
 #include <asm/hpet.h>
 #include <asm/i8253.h>
+#include <asm/nmi.h>
 
 #include <mach_apic.h>
 #include <mach_apicdef.h>
@@ -53,6 +54,9 @@ static cpumask_t timer_bcast_ipi;
  * Knob to control our willingness to enable the local APIC.
  */
 int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */
+int prefer_apic __initdata = 0;        /* when enable_local_apic == 0 prefer APIC but don't force against
+                                  BIOS wishes */
+int apic_disabled_by_dmi __initdata;
 
 /*
  * Debug level
@@ -62,7 +66,7 @@ int apic_verbosity;
 
 static void apic_pm_activate(void);
 
-int modern_apic(void)
+static int modern_apic(void)
 {
        unsigned int lvr, version;
        /* AMD systems use old APIC versions, so check the CPU */
@@ -113,7 +117,7 @@ void __init apic_intr_init(void)
 }
 
 /* Using APIC to generate smp_local_timer_interrupt? */
-int using_apic_timer = 0;
+int using_apic_timer __read_mostly = 0;
 
 static int enabled_via_apicbase;
 
@@ -156,7 +160,7 @@ void clear_local_APIC(void)
        maxlvt = get_maxlvt();
 
        /*
-        * Masking an LVT entry on a P6 can trigger a local APIC error
+        * Masking an LVT entry can trigger a local APIC error
         * if the vector is zero. Mask LVTERR first to prevent this.
         */
        if (maxlvt >= 3) {
@@ -753,6 +757,10 @@ static void apic_pm_activate(void) { }
 
 static int __init apic_set_verbosity(char *str)
 {
+       if (*str == '=')
+               ++str;
+       if (*str == 0)
+               prefer_apic = 1;
        if (strcmp("debug", str) == 0)
                apic_verbosity = APIC_DEBUG;
        else if (strcmp("verbose", str) == 0)
@@ -760,7 +768,7 @@ static int __init apic_set_verbosity(char *str)
        return 1;
 }
 
-__setup("apic=", apic_set_verbosity);
+__setup("apic", apic_set_verbosity);
 
 static int __init detect_init_APIC (void)
 {
@@ -791,8 +799,9 @@ static int __init detect_init_APIC (void)
                 * APIC only if "lapic" specified.
                 */
                if (enable_local_apic <= 0) {
-                       printk("Local APIC disabled by BIOS -- "
-                              "you can enable it with \"lapic\"\n");
+                       if (!apic_disabled_by_dmi)
+                               printk("Local APIC disabled by BIOS -- "
+                                      "you can enable it with \"lapic\"\n");
                        return -1;
                }
                /*
@@ -1117,7 +1126,18 @@ void disable_APIC_timer(void)
                unsigned long v;
 
                v = apic_read(APIC_LVTT);
-               apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED);
+               /*
+                * When an illegal vector value (0-15) is written to an LVT
+                * entry and delivery mode is Fixed, the APIC may signal an
+                * illegal vector error, with out regard to whether the mask
+                * bit is set or whether an interrupt is actually seen on input.
+                *
+                * Boot sequence might call this function when the LVTT has
+                * '0' vector value. So make sure vector field is set to
+                * valid value.
+                */
+               v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
+               apic_write_around(APIC_LVTT, v);
        }
 }
 
@@ -1315,6 +1335,64 @@ fastcall void smp_error_interrupt(struct pt_regs *regs)
        irq_exit();
 }
 
+#ifdef CONFIG_X86_APIC_AUTO
+
+/* Some heuristics to decide when to enable the APICs */
+
+static __init int dmi_enable_apic(void)
+{
+       int year;
+       int apic;
+       char *vendor;
+
+       /* If the machine has more than one CPU try to use APIC because it'll
+          be running the SMP kernel with APIC soon anyways.
+          This won't cover dual core, but they are handled by the date check
+          below. */
+       if (dmi_cpus > 1)
+               return 1;
+
+       year = dmi_get_year(DMI_BIOS_DATE);
+       vendor = dmi_get_system_info(DMI_BIOS_VENDOR);
+       apic = 0;
+
+       /* All Intel BIOS since 1998 assumed APIC on. Don't include 1998 itself
+          because we're not sure for that. */
+       if (vendor && !strncmp(vendor, "Intel", 5))
+               apic = 1;
+       /* Use APIC for anything since 2001 */
+       else if (year >= 2001)
+               apic = 1;
+
+#ifdef CONFIG_ACPI
+       /* When ACPI is disabled also default to APIC off on very new systems (>= 2004)
+          which typically don't have working mptables anymore */
+       if (acpi_noirq && year >= 2004)
+               apic = 0;
+#endif
+
+       if (!apic)
+               apic_disabled_by_dmi = 1;
+
+       return apic;
+}
+
+void __init dmi_check_apic(void)
+{
+       if (enable_local_apic != 0 || prefer_apic)
+               return;
+       if (!dmi_enable_apic()) {
+               clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
+               nr_ioapics = 0;
+               enable_local_apic = -1;
+               printk(KERN_INFO "IO/L-APIC disabled because your old system seems to be old\n");
+               printk(KERN_INFO "overwrite with \"apic\"\n");
+               return;
+       }
+       printk(KERN_INFO "IO/L-APIC allowed because system is MP or new enough\n");
+}
+#endif
+
 /*
  * This initializes the IO-APIC and APIC hardware if this is
  * a UP kernel.