x86: apic unify smp_spurious/error_interrupt
[deliverable/linux.git] / arch / x86 / kernel / apic_32.c
index a91c57cb666a3c752295875ac4ff14298502f5b7..60a901b2d4fef8aa5e42055871b0a40c6b136629 100644 (file)
 #include <linux/mc146818rtc.h>
 #include <linux/kernel_stat.h>
 #include <linux/sysdev.h>
+#include <linux/ioport.h>
 #include <linux/cpu.h>
 #include <linux/clockchips.h>
 #include <linux/acpi_pmtmr.h>
 #include <linux/module.h>
 #include <linux/dmi.h>
+#include <linux/dmar.h>
 
 #include <asm/atomic.h>
 #include <asm/smp.h>
 #include <asm/desc.h>
 #include <asm/arch_hooks.h>
 #include <asm/hpet.h>
+#include <asm/pgalloc.h>
 #include <asm/i8253.h>
 #include <asm/nmi.h>
+#include <asm/idle.h>
+#include <asm/proto.h>
+#include <asm/timex.h>
+#include <asm/apic.h>
+#include <asm/i8259.h>
 
 #include <mach_apic.h>
 #include <mach_apicdef.h>
 # error SPURIOUS_APIC_VECTOR definition error
 #endif
 
-unsigned long mp_lapic_addr;
-
+#ifdef CONFIG_X86_32
 /*
  * Knob to control our willingness to enable the local APIC.
  *
  * +1=force-enable
  */
 static int force_enable_local_apic;
-int disable_apic;
+/*
+ * APIC command line parameters
+ */
+static int __init parse_lapic(char *arg)
+{
+       force_enable_local_apic = 1;
+       return 0;
+}
+early_param("lapic", parse_lapic);
+/* Local APIC was disabled by the BIOS and enabled by the kernel */
+static int enabled_via_apicbase;
 
+#endif
+
+#ifdef CONFIG_X86_64
+static int apic_calibrate_pmtmr __initdata;
+static __init int setup_apicpmtimer(char *s)
+{
+       apic_calibrate_pmtmr = 1;
+       notsc_setup(NULL);
+       return 0;
+}
+__setup("apicpmtimer", setup_apicpmtimer);
+#endif
+
+unsigned long mp_lapic_addr;
+int disable_apic;
 /* Disable local APIC timer from the kernel commandline or via dmi quirk */
 static int disable_apic_timer __cpuinitdata;
 /* Local APIC timer works in C2 */
@@ -110,9 +142,6 @@ static struct clock_event_device lapic_clockevent = {
 };
 static DEFINE_PER_CPU(struct clock_event_device, lapic_events);
 
-/* Local APIC was disabled by the BIOS and enabled by the kernel */
-static int enabled_via_apicbase;
-
 static unsigned long apic_phys;
 
 /*
@@ -219,6 +248,7 @@ void __cpuinit enable_NMI_through_LVT0(void)
        apic_write(APIC_LVT0, v);
 }
 
+#ifdef CONFIG_X86_32
 /**
  * get_physical_broadcast - Get number of physical broadcast IDs
  */
@@ -226,6 +256,7 @@ int get_physical_broadcast(void)
 {
        return modern_apic() ? 0xff : 0xf;
 }
+#endif
 
 /**
  * lapic_get_maxlvt - get the maximum number of local vector table entries
@@ -295,6 +326,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
  *
  * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
  * MCE interrupts are supported. Thus MCE offset must be set to 0.
+ *
+ * If mask=1, the LVT entry does not generate interrupts while mask=0
+ * enables the vector. See also the BKDGs.
  */
 
 #define APIC_EILVT_LVTOFF_MCE 0
@@ -319,6 +353,7 @@ u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
        setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
        return APIC_EILVT_LVTOFF_IBS;
 }
+EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
 
 /*
  * Program the next event, relative to now
@@ -379,7 +414,7 @@ static void lapic_timer_broadcast(cpumask_t mask)
  * Setup the local APIC timer for this CPU. Copy the initilized values
  * of the boot CPU and register the clock event in the framework.
  */
-static void __devinit setup_APIC_timer(void)
+static void __cpuinit setup_APIC_timer(void)
 {
        struct clock_event_device *levt = &__get_cpu_var(lapic_events);
 
@@ -648,7 +683,7 @@ void __init setup_boot_APIC_clock(void)
        setup_APIC_timer();
 }
 
-void __devinit setup_secondary_APIC_clock(void)
+void __cpuinit setup_secondary_APIC_clock(void)
 {
        setup_APIC_timer();
 }
@@ -714,6 +749,9 @@ void smp_apic_timer_interrupt(struct pt_regs *regs)
         * Besides, if we don't timer interrupts ignore the global
         * interrupt lock, which is the WrongThing (tm) to do.
         */
+#ifdef CONFIG_X86_64
+       exit_idle();
+#endif
        irq_enter();
        local_apic_timer_interrupt();
        irq_exit();
@@ -1029,9 +1067,10 @@ static void __cpuinit lapic_setup_esr(void)
  */
 void __cpuinit setup_local_APIC(void)
 {
-       unsigned long value, integrated;
+       unsigned int value;
        int i, j;
 
+#ifdef CONFIG_X86_32
        /* Pound the ESR really hard over the head with a big hammer - mbligh */
        if (esr_disable) {
                apic_write(APIC_ESR, 0);
@@ -1039,14 +1078,16 @@ void __cpuinit setup_local_APIC(void)
                apic_write(APIC_ESR, 0);
                apic_write(APIC_ESR, 0);
        }
+#endif
 
-       integrated = lapic_is_integrated();
+       preempt_disable();
 
        /*
         * Double-check whether this APIC is really registered.
+        * This is meaningless in clustered apic mode, so we skip it.
         */
        if (!apic_id_registered())
-               WARN_ON_ONCE(1);
+               BUG();
 
        /*
         * Intel recommends to set DFR, LDR and TPR before enabling
@@ -1092,6 +1133,7 @@ void __cpuinit setup_local_APIC(void)
         */
        value |= APIC_SPIV_APIC_ENABLED;
 
+#ifdef CONFIG_X86_32
        /*
         * Some unknown Intel IO/APIC (or APIC) errata is biting us with
         * certain networking cards. If high frequency interrupts are
@@ -1112,8 +1154,13 @@ void __cpuinit setup_local_APIC(void)
         * See also the comment in end_level_ioapic_irq().  --macro
         */
 
-       /* Enable focus processor (bit==0) */
+       /*
+        * - enable focus processor (bit==0)
+        * - 64bit mode always use processor focus
+        *   so no need to set it
+        */
        value &= ~APIC_SPIV_FOCUS_DISABLED;
+#endif
 
        /*
         * Set spurious IRQ vector
@@ -1150,9 +1197,11 @@ void __cpuinit setup_local_APIC(void)
                value = APIC_DM_NMI;
        else
                value = APIC_DM_NMI | APIC_LVT_MASKED;
-       if (!integrated)                /* 82489DX */
+       if (!lapic_is_integrated())             /* 82489DX */
                value |= APIC_LVT_LEVEL_TRIGGER;
        apic_write(APIC_LVT1, value);
+
+       preempt_enable();
 }
 
 void __cpuinit end_local_APIC_setup(void)
@@ -1173,6 +1222,25 @@ void __cpuinit end_local_APIC_setup(void)
        apic_pm_activate();
 }
 
+#ifdef CONFIG_X86_64
+/*
+ * Detect and enable local APICs on non-SMP boards.
+ * Original code written by Keir Fraser.
+ * On AMD64 we trust the BIOS - if it says no APIC it is likely
+ * not correctly set up (usually the APIC timer won't work etc.)
+ */
+static int __init detect_init_APIC(void)
+{
+       if (!cpu_has_apic) {
+               printk(KERN_INFO "No local APIC present\n");
+               return -1;
+       }
+
+       mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
+       boot_cpu_physical_apicid = 0;
+       return 0;
+}
+#else
 /*
  * Detect and initialize APIC
  */
@@ -1251,6 +1319,33 @@ no_apic:
        printk(KERN_INFO "No local APIC present or hardware disabled\n");
        return -1;
 }
+#endif
+
+#ifdef CONFIG_X86_64
+void __init early_init_lapic_mapping(void)
+{
+       unsigned long phys_addr;
+
+       /*
+        * If no local APIC can be found then go out
+        * : it means there is no mpatable and MADT
+        */
+       if (!smp_found_config)
+               return;
+
+       phys_addr = mp_lapic_addr;
+
+       set_fixmap_nocache(FIX_APIC_BASE, phys_addr);
+       apic_printk(APIC_VERBOSE, "mapped APIC to %16lx (%16lx)\n",
+                   APIC_BASE, phys_addr);
+
+       /*
+        * Fetch the APIC ID of the BSP in case we have a
+        * default configuration (or the MP table is broken).
+        */
+       boot_cpu_physical_apicid = read_apic_id();
+}
+#endif
 
 /**
  * init_apic_mappings - initialize APIC mappings
@@ -1269,8 +1364,8 @@ void __init init_apic_mappings(void)
                apic_phys = mp_lapic_addr;
 
        set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
-       printk(KERN_DEBUG "mapped APIC to %08lx (%08lx)\n", APIC_BASE,
-              apic_phys);
+       apic_printk(APIC_VERBOSE, "mapped APIC to %16lx (%16lx)\n",
+                               APIC_BASE, apic_phys);
 
        /*
         * Fetch the APIC ID of the BSP in case we have a
@@ -1278,18 +1373,27 @@ void __init init_apic_mappings(void)
         */
        if (boot_cpu_physical_apicid == -1U)
                boot_cpu_physical_apicid = read_apic_id();
-
 }
 
 /*
  * 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)
 {
+#ifdef CONFIG_X86_64
+       if (disable_apic) {
+               printk(KERN_INFO "Apic disabled\n");
+               return -1;
+       }
+       if (!cpu_has_apic) {
+               disable_apic = 1;
+               printk(KERN_INFO "Apic disabled by BIOS\n");
+               return -1;
+       }
+#else
        if (!smp_found_config && !cpu_has_apic)
                return -1;
 
@@ -1303,34 +1407,62 @@ int __init APIC_init_uniprocessor(void)
                clear_cpu_cap(&boot_cpu_data, X86_FEATURE_APIC);
                return -1;
        }
+#endif
 
+#ifdef HAVE_X2APIC
+       enable_IR_x2apic();
+#endif
+#ifdef CONFIG_X86_64
+       setup_apic_routing();
+#endif
        verify_local_APIC();
-
        connect_bsp_APIC();
 
+#ifdef CONFIG_X86_64
+       apic_write(APIC_ID, SET_APIC_ID(boot_cpu_physical_apicid));
+#else
        /*
         * Hack: In case of kdump, after a crash, kernel might be booting
         * on a cpu with non-zero lapic id. But boot_cpu_physical_apicid
         * might be zero if read from MP tables. Get it from LAPIC.
         */
-#ifdef CONFIG_CRASH_DUMP
+# ifdef CONFIG_CRASH_DUMP
        boot_cpu_physical_apicid = read_apic_id();
+# endif
 #endif
        physid_set_mask_of_physid(boot_cpu_physical_apicid, &phys_cpu_present_map);
-
        setup_local_APIC();
 
+#ifdef CONFIG_X86_64
+       /*
+        * Now enable IO-APICs, actually call clear_IO_APIC
+        * We need clear_IO_APIC before enabling vector on BP
+        */
+       if (!skip_ioapic_setup && nr_ioapics)
+               enable_IO_APIC();
+#endif
+
 #ifdef CONFIG_X86_IO_APIC
        if (!smp_found_config || skip_ioapic_setup || !nr_ioapics)
 #endif
                localise_nmi_watchdog();
        end_local_APIC_setup();
+
 #ifdef CONFIG_X86_IO_APIC
-       if (smp_found_config)
-               if (!skip_ioapic_setup && nr_ioapics)
-                       setup_IO_APIC();
+       if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
+               setup_IO_APIC();
+# ifdef CONFIG_X86_64
+       else
+               nr_ioapics = 0;
+# endif
 #endif
+
+#ifdef CONFIG_X86_64
+       setup_boot_APIC_clock();
+       check_nmi_watchdog();
+#else
        setup_boot_clock();
+#endif
 
        return 0;
 }
@@ -1342,10 +1474,17 @@ int __init APIC_init_uniprocessor(void)
 /*
  * This interrupt should _never_ happen with our APIC/SMP architecture
  */
+#ifdef CONFIG_X86_64
+asmlinkage void smp_spurious_interrupt(void)
+#else
 void smp_spurious_interrupt(struct pt_regs *regs)
+#endif
 {
-       unsigned long v;
+       u32 v;
 
+#ifdef CONFIG_X86_64
+       exit_idle();
+#endif
        irq_enter();
        /*
         * Check if this really is a spurious interrupt and ACK it
@@ -1356,20 +1495,31 @@ void smp_spurious_interrupt(struct pt_regs *regs)
        if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
                ack_APIC_irq();
 
+#ifdef CONFIG_X86_64
+       add_pda(irq_spurious_count, 1);
+#else
        /* see sw-dev-man vol 3, chapter 7.4.13.5 */
        printk(KERN_INFO "spurious APIC interrupt on CPU#%d, "
               "should never happen.\n", smp_processor_id());
        __get_cpu_var(irq_stat).irq_spurious_count++;
+#endif
        irq_exit();
 }
 
 /*
  * This interrupt should never happen with our APIC/SMP architecture
  */
+#ifdef CONFIG_X86_64
+asmlinkage void smp_error_interrupt(void)
+#else
 void smp_error_interrupt(struct pt_regs *regs)
+#endif
 {
-       unsigned long v, v1;
+       u32 v, v1;
 
+#ifdef CONFIG_X86_64
+       exit_idle();
+#endif
        irq_enter();
        /* First tickle the hardware, only then report what went on. -- REW */
        v = apic_read(APIC_ESR);
@@ -1388,7 +1538,7 @@ void smp_error_interrupt(struct pt_regs *regs)
           6: Received illegal vector
           7: Illegal register address
        */
-       printk(KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n",
+       printk(KERN_DEBUG "APIC error on CPU%d: %02x(%02x)\n",
                smp_processor_id(), v , v1);
        irq_exit();
 }
@@ -1561,6 +1711,13 @@ void __cpuinit generic_processor_info(int apicid, int version)
        cpu_set(cpu, cpu_present_map);
 }
 
+#ifdef CONFIG_X86_64
+int hard_smp_processor_id(void)
+{
+       return read_apic_id();
+}
+#endif
+
 /*
  * Power management
  */
@@ -1636,11 +1793,6 @@ static int lapic_resume(struct sys_device *dev)
 
        local_irq_save(flags);
 
-#ifdef CONFIG_X86_64
-       if (x2apic)
-               enable_x2apic();
-       else
-#endif
        {
                /*
                 * Make sure the APICBASE points to the right address
@@ -1698,7 +1850,7 @@ static struct sys_device device_lapic = {
        .cls    = &lapic_sysclass,
 };
 
-static void __devinit apic_pm_activate(void)
+static void __cpuinit apic_pm_activate(void)
 {
        apic_pm_state.active = 1;
 }
@@ -1724,16 +1876,87 @@ static void apic_pm_activate(void) { }
 
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_X86_64
 /*
- * APIC command line parameters
+ * apic_is_clustered_box() -- Check if we can expect good TSC
+ *
+ * Thus far, the major user of this is IBM's Summit2 series:
+ *
+ * Clustered boxes may have unsynced TSC problems if they are
+ * multi-chassis. Use available data to take a good guess.
+ * If in doubt, go HPET.
  */
-static int __init parse_lapic(char *arg)
+__cpuinit int apic_is_clustered_box(void)
 {
-       force_enable_local_apic = 1;
-       return 0;
+       int i, clusters, zeros;
+       unsigned id;
+       u16 *bios_cpu_apicid;
+       DECLARE_BITMAP(clustermap, NUM_APIC_CLUSTERS);
+
+       /*
+        * there is not this kind of box with AMD CPU yet.
+        * Some AMD box with quadcore cpu and 8 sockets apicid
+        * will be [4, 0x23] or [8, 0x27] could be thought to
+        * vsmp box still need checking...
+        */
+       if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && !is_vsmp_box())
+               return 0;
+
+       bios_cpu_apicid = early_per_cpu_ptr(x86_bios_cpu_apicid);
+       bitmap_zero(clustermap, NUM_APIC_CLUSTERS);
+
+       for (i = 0; i < NR_CPUS; i++) {
+               /* are we being called early in kernel startup? */
+               if (bios_cpu_apicid) {
+                       id = bios_cpu_apicid[i];
+               }
+               else if (i < nr_cpu_ids) {
+                       if (cpu_present(i))
+                               id = per_cpu(x86_bios_cpu_apicid, i);
+                       else
+                               continue;
+               }
+               else
+                       break;
+
+               if (id != BAD_APICID)
+                       __set_bit(APIC_CLUSTERID(id), clustermap);
+       }
+
+       /* Problem:  Partially populated chassis may not have CPUs in some of
+        * the APIC clusters they have been allocated.  Only present CPUs have
+        * x86_bios_cpu_apicid entries, thus causing zeroes in the bitmap.
+        * Since clusters are allocated sequentially, count zeros only if
+        * they are bounded by ones.
+        */
+       clusters = 0;
+       zeros = 0;
+       for (i = 0; i < NUM_APIC_CLUSTERS; i++) {
+               if (test_bit(i, clustermap)) {
+                       clusters += 1 + zeros;
+                       zeros = 0;
+               } else
+                       ++zeros;
+       }
+
+       /* ScaleMP vSMPowered boxes have one cluster per board and TSCs are
+        * not guaranteed to be synced between boards
+        */
+       if (is_vsmp_box() && clusters > 1)
+               return 1;
+
+       /*
+        * If clusters > 2, then should be multi-chassis.
+        * May have to revisit this when multi-core + hyperthreaded CPUs come
+        * out, but AFAIK this will work even for them.
+        */
+       return (clusters > 2);
 }
-early_param("lapic", parse_lapic);
+#endif
 
+/*
+ * APIC command line parameters
+ */
 static int __init setup_disableapic(char *arg)
 {
        disable_apic = 1;
This page took 0.044351 seconds and 5 git commands to generate.