]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/kernel/apic_64.c
Merge branch 'x86/apic' into x86-v28-for-linus-phase4-B
[linux-2.6-omap-h63xx.git] / arch / x86 / kernel / apic_64.c
index 46523c0cc6f6db438587f84d79b000b1f47ad08b..53898b65a6ae1a7bac6a9145caf1113c39274d84 100644 (file)
@@ -101,7 +101,6 @@ static unsigned long apic_phys;
 
 unsigned long mp_lapic_addr;
 
-unsigned int __cpuinitdata maxcpus = NR_CPUS;
 /*
  * Get the LAPIC version
  */
@@ -111,11 +110,15 @@ static inline int lapic_get_version(void)
 }
 
 /*
- * Check, if the APIC is integrated or a seperate chip
+ * Check, if the APIC is integrated or a separate chip
  */
 static inline int lapic_is_integrated(void)
 {
+#ifdef CONFIG_X86_64
        return 1;
+#else
+       return APIC_INTEGRATED(lapic_get_version());
+#endif
 }
 
 /*
@@ -170,7 +173,7 @@ u64 xapic_icr_read(void)
        icr2 = apic_read(APIC_ICR2);
        icr1 = apic_read(APIC_ICR);
 
-       return (icr1 | ((u64)icr2 << 32));
+       return icr1 | ((u64)icr2 << 32);
 }
 
 static struct apic_ops xapic_ops = {
@@ -255,8 +258,12 @@ int lapic_get_maxlvt(void)
  * Local APIC timer
  */
 
-/* Clock divisor is set to 1 */
+/* Clock divisor */
+#ifdef CONFG_X86_64
 #define APIC_DIVISOR 1
+#else
+#define APIC_DIVISOR 16
+#endif
 
 /*
  * This function sets up the local APIC timer, with a timeout of
@@ -287,9 +294,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
         * Divide PICLK by 16
         */
        tmp_value = apic_read(APIC_TDCR);
-       apic_write(APIC_TDCR, (tmp_value
-                               & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
-                               | APIC_TDR_DIV_16);
+       apic_write(APIC_TDCR,
+               (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
+               APIC_TDR_DIV_16);
 
        if (!oneshot)
                apic_write(APIC_TMICT, clocks / APIC_DIVISOR);
@@ -559,7 +566,11 @@ static void local_apic_timer_interrupt(void)
        /*
         * the NMI deadlock-detector uses this.
         */
+#ifdef CONFIG_X86_64
        add_pda(apic_timer_irqs, 1);
+#else
+       per_cpu(irq_stat, cpu).apic_timer_irqs++;
+#endif
 
        evt->event_handler(evt);
 }
@@ -687,8 +698,28 @@ void disable_local_APIC(void)
        value = apic_read(APIC_SPIV);
        value &= ~APIC_SPIV_APIC_ENABLED;
        apic_write(APIC_SPIV, value);
+
+#ifdef CONFIG_X86_32
+       /*
+        * When LAPIC was disabled by the BIOS and enabled by the kernel,
+        * restore the disabled state.
+        */
+       if (enabled_via_apicbase) {
+               unsigned int l, h;
+
+               rdmsr(MSR_IA32_APICBASE, l, h);
+               l &= ~MSR_IA32_APICBASE_ENABLE;
+               wrmsr(MSR_IA32_APICBASE, l, h);
+       }
+#endif
 }
 
+/*
+ * If Linux enabled the LAPIC against the BIOS default disable it down before
+ * re-entering the BIOS on shutdown.  Otherwise the BIOS may get confused and
+ * not power-off.  Additionally clear all LVT entries before disable_local_APIC
+ * for the case where Linux didn't enable the LAPIC.
+ */
 void lapic_shutdown(void)
 {
        unsigned long flags;
@@ -698,7 +729,13 @@ void lapic_shutdown(void)
 
        local_irq_save(flags);
 
-       disable_local_APIC();
+#ifdef CONFIG_X86_32
+       if (!enabled_via_apicbase)
+               clear_local_APIC();
+       else
+#endif
+               disable_local_APIC();
+
 
        local_irq_restore(flags);
 }
@@ -833,6 +870,45 @@ void __init init_bsp_APIC(void)
        apic_write(APIC_LVT1, value);
 }
 
+static void __cpuinit lapic_setup_esr(void)
+{
+       unsigned long oldvalue, value, maxlvt;
+       if (lapic_is_integrated() && !esr_disable) {
+               if (esr_disable) {
+                       /*
+                        * Something untraceable is creating bad interrupts on
+                        * secondary quads ... for the moment, just leave the
+                        * ESR disabled - we can't do anything useful with the
+                        * errors anyway - mbligh
+                        */
+                       printk(KERN_INFO "Leaving ESR disabled.\n");
+                       return;
+               }
+               /* !82489DX */
+               maxlvt = lapic_get_maxlvt();
+               if (maxlvt > 3)         /* Due to the Pentium erratum 3AP. */
+                       apic_write(APIC_ESR, 0);
+               oldvalue = apic_read(APIC_ESR);
+
+               /* enables sending errors */
+               value = ERROR_APIC_VECTOR;
+               apic_write(APIC_LVTERR, value);
+               /*
+                * spec says clear errors after enabling vector.
+                */
+               if (maxlvt > 3)
+                       apic_write(APIC_ESR, 0);
+               value = apic_read(APIC_ESR);
+               if (value != oldvalue)
+                       apic_printk(APIC_VERBOSE, "ESR value before enabling "
+                               "vector: 0x%08lx  after: 0x%08lx\n",
+                               oldvalue, value);
+       } else {
+               printk(KERN_INFO "No ESR for 82489DX.\n");
+       }
+}
+
+
 /**
  * setup_local_APIC - setup the local APIC
  */
@@ -938,21 +1014,20 @@ void __cpuinit setup_local_APIC(void)
        preempt_enable();
 }
 
-static void __cpuinit lapic_setup_esr(void)
-{
-       unsigned maxlvt = lapic_get_maxlvt();
-
-       apic_write(APIC_LVTERR, ERROR_APIC_VECTOR);
-       /*
-        * spec says clear errors after enabling vector.
-        */
-       if (maxlvt > 3)
-               apic_write(APIC_ESR, 0);
-}
-
 void __cpuinit end_local_APIC_setup(void)
 {
        lapic_setup_esr();
+
+#ifdef CONFIG_X86_32
+       {
+               unsigned int value;
+               /* Disable the local apic timer */
+               value = apic_read(APIC_LVTT);
+               value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
+               apic_write(APIC_LVTT, value);
+       }
+#endif
+
        setup_apic_nmi_watchdog(NULL);
        apic_pm_activate();
 }
@@ -1154,6 +1229,8 @@ void __init init_apic_mappings(void)
  * This initializes the IO-APIC and APIC hardware if this is
  * a UP kernel.
  */
+int apic_version[MAX_APICS];
+
 int __init APIC_init_uniprocessor(void)
 {
        if (disable_apic) {
@@ -1255,10 +1332,26 @@ asmlinkage void smp_error_interrupt(void)
 }
 
 /**
- *  * connect_bsp_APIC - attach the APIC to the interrupt system
- *   */
+ * connect_bsp_APIC - attach the APIC to the interrupt system
+ */
 void __init connect_bsp_APIC(void)
 {
+#ifdef CONFIG_X86_32
+       if (pic_mode) {
+               /*
+                * Do not trust the local APIC being empty at bootup.
+                */
+               clear_local_APIC();
+               /*
+                * PIC mode, enable APIC mode in the IMCR, i.e.  connect BSP's
+                * local APIC to INT and NMI lines.
+                */
+               apic_printk(APIC_VERBOSE, "leaving PIC mode, "
+                               "enabling APIC mode.\n");
+               outb(0x70, 0x22);
+               outb(0x01, 0x23);
+       }
+#endif
        enable_apic_mode();
 }
 
@@ -1271,8 +1364,25 @@ void __init connect_bsp_APIC(void)
  */
 void disconnect_bsp_APIC(int virt_wire_setup)
 {
+       unsigned int value;
+
+#ifdef CONFIG_X86_32
+       if (pic_mode) {
+               /*
+                * Put the board back into PIC mode (has an effect only on
+                * certain older boards).  Note that APIC interrupts, including
+                * IPIs, won't work beyond this point!  The only exception are
+                * INIT IPIs.
+                */
+               apic_printk(APIC_VERBOSE, "disabling APIC mode, "
+                               "entering PIC mode.\n");
+               outb(0x70, 0x22);
+               outb(0x00, 0x23);
+               return;
+       }
+#endif
+
        /* Go back to Virtual Wire compatibility mode */
-       unsigned long value;
 
        /* For the spurious interrupt use vector F, and enable it */
        value = apic_read(APIC_SPIV);
@@ -1298,7 +1408,10 @@ void disconnect_bsp_APIC(int virt_wire_setup)
                apic_write(APIC_LVT0, APIC_LVT_MASKED);
        }
 
-       /* For LVT1 make it edge triggered, active high, nmi and enabled */
+       /*
+        * For LVT1 make it edge triggered, active high,
+        * nmi and enabled
+        */
        value = apic_read(APIC_LVT1);
        value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING |
                        APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR |
@@ -1313,15 +1426,20 @@ void __cpuinit generic_processor_info(int apicid, int version)
        int cpu;
        cpumask_t tmp_map;
 
-       if (num_processors >= NR_CPUS) {
-               printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached."
-                      " Processor ignored.\n", NR_CPUS);
-               return;
+       /*
+        * Validate version
+        */
+       if (version == 0x0) {
+               printk(KERN_WARNING "BIOS bug, APIC version is 0 for CPU#%d! "
+                               "fixing up to 0x10. (tell your hw vendor)\n",
+                               version);
+               version = 0x10;
        }
+       apic_version[apicid] = version;
 
-       if (num_processors >= maxcpus) {
-               printk(KERN_WARNING "WARNING: maxcpus limit of %i reached."
-                      " Processor ignored.\n", maxcpus);
+       if (num_processors >= NR_CPUS) {
+               printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached."
+                       "  Processor ignored.\n", NR_CPUS);
                return;
        }
 
@@ -1341,6 +1459,29 @@ void __cpuinit generic_processor_info(int apicid, int version)
        if (apicid > max_physical_apicid)
                max_physical_apicid = apicid;
 
+#ifdef CONFIG_X86_32
+       /*
+        * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y
+        * but we need to work other dependencies like SMP_SUSPEND etc
+        * before this can be done without some confusion.
+        * if (CPU_HOTPLUG_ENABLED || num_processors > 8)
+        *       - Ashok Raj <ashok.raj@intel.com>
+        */
+       if (max_physical_apicid >= 8) {
+               switch (boot_cpu_data.x86_vendor) {
+               case X86_VENDOR_INTEL:
+                       if (!APIC_XAPIC(version)) {
+                               def_to_bigsmp = 0;
+                               break;
+                       }
+                       /* If P4 and above fall through */
+               case X86_VENDOR_AMD:
+                       def_to_bigsmp = 1;
+               }
+       }
+#endif
+
+#if defined(CONFIG_X86_SMP) || defined(CONFIG_X86_64)
        /* are we being called early in kernel startup? */
        if (early_per_cpu_ptr(x86_cpu_to_apicid)) {
                u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
@@ -1352,6 +1493,7 @@ void __cpuinit generic_processor_info(int apicid, int version)
                per_cpu(x86_cpu_to_apicid, cpu) = apicid;
                per_cpu(x86_bios_cpu_apicid, cpu) = apicid;
        }
+#endif
 
        cpu_set(cpu, cpu_possible_map);
        cpu_set(cpu, cpu_present_map);
@@ -1442,6 +1584,7 @@ static int lapic_resume(struct sys_device *dev)
                enable_x2apic();
        else
 #endif
+       {
                /*
                 * Make sure the APICBASE points to the right address
                 *
@@ -1452,6 +1595,7 @@ static int lapic_resume(struct sys_device *dev)
                l &= ~MSR_IA32_APICBASE_BASE;
                l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr;
                wrmsr(MSR_IA32_APICBASE, l, h);
+       }
 
        apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
        apic_write(APIC_ID, apic_pm_state.apic_id);
@@ -1611,28 +1755,7 @@ early_param("nox2apic", setup_nox2apic);
 /*
  * APIC command line parameters
  */
-static int __init apic_set_verbosity(char *str)
-{
-       if (str == NULL)  {
-               skip_ioapic_setup = 0;
-               ioapic_force = 1;
-               return 0;
-       }
-       if (strcmp("debug", str) == 0)
-               apic_verbosity = APIC_DEBUG;
-       else if (strcmp("verbose", str) == 0)
-               apic_verbosity = APIC_VERBOSE;
-       else {
-               printk(KERN_WARNING "APIC Verbosity level %s not recognised"
-                               " use apic=verbose or apic=debug\n", str);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-early_param("apic", apic_set_verbosity);
-
-static __init int setup_disableapic(char *str)
+static int __init setup_disableapic(char *arg)
 {
        disable_apic = 1;
        setup_clear_cpu_cap(X86_FEATURE_APIC);
@@ -1641,9 +1764,9 @@ static __init int setup_disableapic(char *str)
 early_param("disableapic", setup_disableapic);
 
 /* same as disableapic, for compatibility */
-static __init int setup_nolapic(char *str)
+static int __init setup_nolapic(char *arg)
 {
-       return setup_disableapic(str);
+       return setup_disableapic(arg);
 }
 early_param("nolapic", setup_nolapic);
 
@@ -1676,6 +1799,31 @@ static __init int setup_apicpmtimer(char *s)
 }
 __setup("apicpmtimer", setup_apicpmtimer);
 
+static int __init apic_set_verbosity(char *arg)
+{
+       if (!arg)  {
+#ifdef CONFIG_X86_64
+               skip_ioapic_setup = 0;
+               ioapic_force = 1;
+               return 0;
+#endif
+               return -EINVAL;
+       }
+
+       if (strcmp("debug", arg) == 0)
+               apic_verbosity = APIC_DEBUG;
+       else if (strcmp("verbose", arg) == 0)
+               apic_verbosity = APIC_VERBOSE;
+       else {
+               printk(KERN_WARNING "APIC Verbosity level %s not recognised"
+                       " use apic=verbose or apic=debug\n", arg);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+early_param("apic", apic_set_verbosity);
+
 static int __init lapic_insert_resource(void)
 {
        if (!apic_phys)