]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/kernel/io_apic_64.c
x86: I/O APIC: keep the timer IRQ masked during set-up
[linux-2.6-omap-h63xx.git] / arch / x86 / kernel / io_apic_64.c
index 953328b55a30503c58f2f8b3ed0f30939ff4047c..ae0ac990574fa4ea2cd3f01418413a9d00abbade 100644 (file)
 #include <linux/msi.h>
 #include <linux/htirq.h>
 #include <linux/dmar.h>
+#include <linux/jiffies.h>
 #ifdef CONFIG_ACPI
 #include <acpi/acpi_bus.h>
 #endif
+#include <linux/bootmem.h>
 
 #include <asm/idle.h>
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/desc.h>
 #include <asm/proto.h>
-#include <asm/mach_apic.h>
 #include <asm/acpi.h>
 #include <asm/dma.h>
 #include <asm/nmi.h>
 #include <asm/msidef.h>
 #include <asm/hypertransport.h>
 
+#include <mach_ipi.h>
+#include <mach_apic.h>
+
 struct irq_cfg {
        cpumask_t domain;
        cpumask_t old_domain;
@@ -86,7 +90,7 @@ static int no_timer_check;
 
 static int disable_timer_pin_1 __initdata;
 
-int timer_over_8254 __initdata = 1;
+int timer_through_8259 __initdata;
 
 /* Where if anywhere is the i8259 connect in external int mode */
 static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
@@ -99,6 +103,16 @@ DEFINE_SPINLOCK(vector_lock);
  */
 int nr_ioapic_registers[MAX_IO_APICS];
 
+/* I/O APIC entries */
+struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS];
+int nr_ioapics;
+
+/* MP IRQ source entries */
+struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
+
+/* # of MP IRQ source entries */
+int mp_irq_entries;
+
 /*
  * Rough estimation of how many shared IRQs there are, can
  * be changed anytime.
@@ -153,11 +167,10 @@ static inline void io_apic_modify(unsigned int apic, unsigned int value)
        writel(value, &io_apic->data);
 }
 
-static int io_apic_level_ack_pending(unsigned int irq)
+static bool io_apic_level_ack_pending(unsigned int irq)
 {
        struct irq_pin_list *entry;
        unsigned long flags;
-       int pending = 0;
 
        spin_lock_irqsave(&ioapic_lock, flags);
        entry = irq_2_pin + irq;
@@ -170,13 +183,17 @@ static int io_apic_level_ack_pending(unsigned int irq)
                        break;
                reg = io_apic_read(entry->apic, 0x10 + pin*2);
                /* Is the remote IRR bit set? */
-               pending |= (reg >> 14) & 1;
+               if ((reg >> 14) & 1) {
+                       spin_unlock_irqrestore(&ioapic_lock, flags);
+                       return true;
+               }
                if (!entry->next)
                        break;
                entry = irq_2_pin + entry->next;
        }
        spin_unlock_irqrestore(&ioapic_lock, flags);
-       return pending;
+
+       return false;
 }
 
 /*
@@ -413,20 +430,6 @@ static int __init disable_timer_pin_setup(char *arg)
 }
 __setup("disable_timer_pin_1", disable_timer_pin_setup);
 
-static int __init setup_disable_8254_timer(char *s)
-{
-       timer_over_8254 = -1;
-       return 1;
-}
-static int __init setup_enable_8254_timer(char *s)
-{
-       timer_over_8254 = 2;
-       return 1;
-}
-
-__setup("disable_8254_timer", setup_disable_8254_timer);
-__setup("enable_8254_timer", setup_enable_8254_timer);
-
 
 /*
  * Find the IRQ entry number of a certain pin.
@@ -546,7 +549,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
 #define default_PCI_trigger(idx)       (1)
 #define default_PCI_polarity(idx)      (1)
 
-static int __init MPBIOS_polarity(int idx)
+static int MPBIOS_polarity(int idx)
 {
        int bus = mp_irqs[idx].mpc_srcbus;
        int polarity;
@@ -768,7 +771,7 @@ static void __clear_irq_vector(int irq)
                per_cpu(vector_irq, cpu)[vector] = -1;
 
        cfg->vector = 0;
-       cfg->domain = CPU_MASK_NONE;
+       cpus_clear(cfg->domain);
 }
 
 void __setup_vector_irq(int cpu)
@@ -894,27 +897,21 @@ static void __init setup_IO_APIC_irqs(void)
 }
 
 /*
- * Set up the 8259A-master output pin as broadcast to all
- * CPUs.
+ * Set up the timer pin, possibly with the 8259A-master behind.
  */
-static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector)
+static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin,
+                                       int vector)
 {
        struct IO_APIC_route_entry entry;
-       unsigned long flags;
-
-       memset(&entry,0,sizeof(entry));
-
-       disable_8259A_irq(0);
 
-       /* mask LVT0 */
-       apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
+       memset(&entry, 0, sizeof(entry));
 
        /*
         * We use logical delivery to get the timer IRQ
         * to the first CPU.
         */
        entry.dest_mode = INT_DEST_MODE;
-       entry.mask = 0;                                 /* unmask IRQ now */
+       entry.mask = 1;                                 /* mask IRQ now */
        entry.dest = cpu_mask_to_apicid(TARGET_CPUS);
        entry.delivery_mode = INT_DELIVERY_MODE;
        entry.polarity = 0;
@@ -923,19 +920,14 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in
 
        /*
         * The timer IRQ doesn't have to know that behind the
-        * scene we have a 8259A-master in AEOI mode ...
+        * scene we may have a 8259A-master in AEOI mode ...
         */
        set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge");
 
        /*
         * Add it to the IO-APIC irq-routing table:
         */
-       spin_lock_irqsave(&ioapic_lock, flags);
-       io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1));
-       io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0));
-       spin_unlock_irqrestore(&ioapic_lock, flags);
-
-       enable_8259A_irq(0);
+       ioapic_write_entry(apic, pin, entry);
 }
 
 void __apicdebuginit print_IO_APIC(void)
@@ -1064,12 +1056,11 @@ void __apicdebuginit print_local_APIC(void * dummy)
 
        printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
                smp_processor_id(), hard_smp_processor_id());
-       v = apic_read(APIC_ID);
-       printk(KERN_INFO "... APIC ID:      %08x (%01x)\n", v, GET_APIC_ID(v));
+       printk(KERN_INFO "... APIC ID:      %08x (%01x)\n", v, GET_APIC_ID(read_apic_id()));
        v = apic_read(APIC_LVR);
        printk(KERN_INFO "... APIC VERSION: %08x\n", v);
        ver = GET_APIC_VERSION(v);
-       maxlvt = get_maxlvt();
+       maxlvt = lapic_get_maxlvt();
 
        v = apic_read(APIC_TASKPRI);
        printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
@@ -1171,7 +1162,7 @@ void __apicdebuginit print_PIC(void)
 
 #endif  /*  0  */
 
-static void __init enable_IO_APIC(void)
+void __init enable_IO_APIC(void)
 {
        union IO_APIC_reg_01 reg_01;
        int i8259_apic, i8259_pin;
@@ -1259,7 +1250,7 @@ void disable_IO_APIC(void)
                entry.dest_mode       = 0; /* Physical */
                entry.delivery_mode   = dest_ExtINT; /* ExtInt */
                entry.vector          = 0;
-               entry.dest          = GET_APIC_ID(apic_read(APIC_ID));
+               entry.dest          = GET_APIC_ID(read_apic_id());
 
                /*
                 * Add it to the IO-APIC irq-routing table:
@@ -1281,10 +1272,13 @@ void disable_IO_APIC(void)
 static int __init timer_irq_works(void)
 {
        unsigned long t1 = jiffies;
+       unsigned long flags;
 
+       local_save_flags(flags);
        local_irq_enable();
        /* Let ten ticks pass... */
        mdelay((10 * 1000) / HZ);
+       local_irq_restore(flags);
 
        /*
         * Expect a few ticks at least, to be sure some possible
@@ -1295,7 +1289,7 @@ static int __init timer_irq_works(void)
         */
 
        /* jiffies wrap? */
-       if (jiffies - t1 > 4)
+       if (time_after(jiffies, t1 + 4))
                return 1;
        return 0;
 }
@@ -1347,9 +1341,7 @@ static int ioapic_retrigger_irq(unsigned int irq)
        unsigned long flags;
 
        spin_lock_irqsave(&vector_lock, flags);
-       cpus_clear(mask);
-       cpu_set(first_cpu(cfg->domain), mask);
-
+       mask = cpumask_of_cpu(first_cpu(cfg->domain));
        send_IPI_mask(mask, cfg->vector);
        spin_unlock_irqrestore(&vector_lock, flags);
 
@@ -1408,7 +1400,7 @@ static void irq_complete_move(unsigned int irq)
        if (likely(!cfg->move_in_progress))
                return;
 
-       vector = ~get_irq_regs()->orig_rax;
+       vector = ~get_irq_regs()->orig_ax;
        me = smp_processor_id();
        if ((vector == cfg->vector) && cpu_isset(me, cfg->domain)) {
                cpumask_t cleanup_mask;
@@ -1435,7 +1427,7 @@ static void ack_apic_level(unsigned int irq)
        int do_unmask_irq = 0;
 
        irq_complete_move(irq);
-#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
+#ifdef CONFIG_GENERIC_PENDING_IRQ
        /* If we are moving the irq we need to mask it */
        if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) {
                do_unmask_irq = 1;
@@ -1512,8 +1504,7 @@ static inline void init_IO_APIC_traps(void)
         * 0x80, because int 0x80 is hm, kind of importantish. ;)
         */
        for (irq = 0; irq < NR_IRQS ; irq++) {
-               int tmp = irq;
-               if (IO_APIC_IRQ(tmp) && !irq_cfg[tmp].vector) {
+               if (IO_APIC_IRQ(irq) && !irq_cfg[irq].vector) {
                        /*
                         * Hmm.. We don't have an entry for this,
                         * so default to an old-fashioned 8259
@@ -1562,7 +1553,7 @@ static struct hw_interrupt_type lapic_irq_type __read_mostly = {
        .end = end_lapic_irq,
 };
 
-static void setup_nmi (void)
+static void __init setup_nmi(void)
 {
        /*
         * Dirty trick to enable the NMI watchdog ...
@@ -1575,7 +1566,7 @@ static void setup_nmi (void)
         */ 
        printk(KERN_INFO "activating NMI Watchdog ...");
 
-       enable_NMI_through_LVT0(NULL);
+       enable_NMI_through_LVT0();
 
        printk(" done.\n");
 }
@@ -1587,22 +1578,19 @@ static void setup_nmi (void)
  * cycles as some i82489DX-based boards have glue logic that keeps the
  * 8259A interrupt line asserted until INTA.  --macro
  */
-static inline void unlock_ExtINT_logic(void)
+static inline void __init unlock_ExtINT_logic(void)
 {
        int apic, pin, i;
        struct IO_APIC_route_entry entry0, entry1;
        unsigned char save_control, save_freq_select;
-       unsigned long flags;
 
        pin  = find_isa_irq_pin(8, mp_INT);
        apic = find_isa_irq_apic(8, mp_INT);
        if (pin == -1)
                return;
 
-       spin_lock_irqsave(&ioapic_lock, flags);
-       *(((int *)&entry0) + 1) = io_apic_read(apic, 0x11 + 2 * pin);
-       *(((int *)&entry0) + 0) = io_apic_read(apic, 0x10 + 2 * pin);
-       spin_unlock_irqrestore(&ioapic_lock, flags);
+       entry0 = ioapic_read_entry(apic, pin);
+
        clear_IO_APIC_pin(apic, pin);
 
        memset(&entry1, 0, sizeof(entry1));
@@ -1615,10 +1603,7 @@ static inline void unlock_ExtINT_logic(void)
        entry1.trigger = 0;
        entry1.vector = 0;
 
-       spin_lock_irqsave(&ioapic_lock, flags);
-       io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry1) + 1));
-       io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry1) + 0));
-       spin_unlock_irqrestore(&ioapic_lock, flags);
+       ioapic_write_entry(apic, pin, entry1);
 
        save_control = CMOS_READ(RTC_CONTROL);
        save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
@@ -1637,10 +1622,7 @@ static inline void unlock_ExtINT_logic(void)
        CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
        clear_IO_APIC_pin(apic, pin);
 
-       spin_lock_irqsave(&ioapic_lock, flags);
-       io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry0) + 1));
-       io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry0) + 0));
-       spin_unlock_irqrestore(&ioapic_lock, flags);
+       ioapic_write_entry(apic, pin, entry0);
 }
 
 /*
@@ -1651,10 +1633,13 @@ static inline void unlock_ExtINT_logic(void)
  *
  * FIXME: really need to revamp this for modern platforms only.
  */
-static inline void check_timer(void)
+static inline void __init check_timer(void)
 {
        struct irq_cfg *cfg = irq_cfg + 0;
        int apic1, pin1, apic2, pin2;
+       unsigned long flags;
+
+       local_irq_save(flags);
 
        /*
         * get/set the timer IRQ vector:
@@ -1663,16 +1648,11 @@ static inline void check_timer(void)
        assign_irq_vector(0, TARGET_CPUS);
 
        /*
-        * Subtle, code in do_timer_interrupt() expects an AEOI
-        * mode for the 8259A whenever interrupts are routed
-        * through I/O APICs.  Also IRQ0 has to be enabled in
-        * the 8259A which implies the virtual wire has to be
-        * disabled in the local APIC.
+        * As IRQ0 is to be enabled in the 8259A, the virtual
+        * wire has to be disabled in the local APIC.
         */
        apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
        init_8259A(1);
-       if (timer_over_8254 > 0)
-               enable_8259A_irq(0);
 
        pin1  = find_isa_irq_pin(0, mp_INT);
        apic1 = find_isa_irq_apic(0, mp_INT);
@@ -1690,13 +1670,12 @@ static inline void check_timer(void)
                if (!no_timer_check && timer_irq_works()) {
                        nmi_watchdog_default();
                        if (nmi_watchdog == NMI_IO_APIC) {
-                               disable_8259A_irq(0);
                                setup_nmi();
                                enable_8259A_irq(0);
                        }
                        if (disable_timer_pin_1 > 0)
                                clear_IO_APIC_pin(0, pin1);
-                       return;
+                       goto out;
                }
                clear_IO_APIC_pin(apic1, pin1);
                apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not "
@@ -1711,18 +1690,24 @@ static inline void check_timer(void)
                /*
                 * legacy devices should be connected to IO APIC #0
                 */
-               setup_ExtINT_IRQ0_pin(apic2, pin2, cfg->vector);
+               setup_timer_IRQ0_pin(apic2, pin2, cfg->vector);
+               unmask_IO_APIC_irq(0);
+               enable_8259A_irq(0);
                if (timer_irq_works()) {
                        apic_printk(APIC_VERBOSE," works.\n");
+                       timer_through_8259 = 1;
                        nmi_watchdog_default();
                        if (nmi_watchdog == NMI_IO_APIC) {
+                               disable_8259A_irq(0);
                                setup_nmi();
+                               enable_8259A_irq(0);
                        }
-                       return;
+                       goto out;
                }
                /*
                 * Cleanup, just in case ...
                 */
+               disable_8259A_irq(0);
                clear_IO_APIC_pin(apic2, pin2);
        }
        apic_printk(APIC_VERBOSE," failed.\n");
@@ -1734,15 +1719,15 @@ static inline void check_timer(void)
 
        apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
 
-       disable_8259A_irq(0);
        irq_desc[0].chip = &lapic_irq_type;
        apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector);     /* Fixed mode */
        enable_8259A_irq(0);
 
        if (timer_irq_works()) {
                apic_printk(APIC_VERBOSE," works.\n");
-               return;
+               goto out;
        }
+       disable_8259A_irq(0);
        apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector);
        apic_printk(APIC_VERBOSE," failed.\n");
 
@@ -1756,10 +1741,12 @@ static inline void check_timer(void)
 
        if (timer_irq_works()) {
                apic_printk(APIC_VERBOSE," works.\n");
-               return;
+               goto out;
        }
        apic_printk(APIC_VERBOSE," failed :(.\n");
        panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n");
+out:
+       local_irq_restore(flags);
 }
 
 static int __init notimercheck(char *s)
@@ -1780,7 +1767,10 @@ __setup("no_timer_check", notimercheck);
 
 void __init setup_IO_APIC(void)
 {
-       enable_IO_APIC();
+
+       /*
+        * calling enable_IO_APIC() is moved to setup_local_APIC for BP
+        */
 
        if (acpi_ioapic)
                io_apic_irqs = ~0;      /* all IRQs go through IOAPIC */
@@ -1842,7 +1832,7 @@ static int ioapic_resume(struct sys_device *dev)
 }
 
 static struct sysdev_class ioapic_sysdev_class = {
-       set_kset_name("ioapic"),
+       .name = "ioapic",
        .suspend = ioapic_suspend,
        .resume = ioapic_resume,
 };
@@ -2222,8 +2212,27 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p
        return 0;
 }
 
-#endif /* CONFIG_ACPI */
 
+int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity)
+{
+       int i;
+
+       if (skip_ioapic_setup)
+               return -1;
+
+       for (i = 0; i < mp_irq_entries; i++)
+               if (mp_irqs[i].mpc_irqtype == mp_INT &&
+                   mp_irqs[i].mpc_srcbusirq == bus_irq)
+                       break;
+       if (i >= mp_irq_entries)
+               return -1;
+
+       *trigger = irq_trigger(i);
+       *polarity = irq_polarity(i);
+       return 0;
+}
+
+#endif /* CONFIG_ACPI */
 
 /*
  * This function currently is only a helper for the i386 smp boot process where
@@ -2260,3 +2269,92 @@ void __init setup_ioapic_dest(void)
        }
 }
 #endif
+
+#define IOAPIC_RESOURCE_NAME_SIZE 11
+
+static struct resource *ioapic_resources;
+
+static struct resource * __init ioapic_setup_resources(void)
+{
+       unsigned long n;
+       struct resource *res;
+       char *mem;
+       int i;
+
+       if (nr_ioapics <= 0)
+               return NULL;
+
+       n = IOAPIC_RESOURCE_NAME_SIZE + sizeof(struct resource);
+       n *= nr_ioapics;
+
+       mem = alloc_bootmem(n);
+       res = (void *)mem;
+
+       if (mem != NULL) {
+               mem += sizeof(struct resource) * nr_ioapics;
+
+               for (i = 0; i < nr_ioapics; i++) {
+                       res[i].name = mem;
+                       res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+                       sprintf(mem,  "IOAPIC %u", i);
+                       mem += IOAPIC_RESOURCE_NAME_SIZE;
+               }
+       }
+
+       ioapic_resources = res;
+
+       return res;
+}
+
+void __init ioapic_init_mappings(void)
+{
+       unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
+       struct resource *ioapic_res;
+       int i;
+
+       ioapic_res = ioapic_setup_resources();
+       for (i = 0; i < nr_ioapics; i++) {
+               if (smp_found_config) {
+                       ioapic_phys = mp_ioapics[i].mpc_apicaddr;
+               } else {
+                       ioapic_phys = (unsigned long)
+                               alloc_bootmem_pages(PAGE_SIZE);
+                       ioapic_phys = __pa(ioapic_phys);
+               }
+               set_fixmap_nocache(idx, ioapic_phys);
+               apic_printk(APIC_VERBOSE,
+                           "mapped IOAPIC to %016lx (%016lx)\n",
+                           __fix_to_virt(idx), ioapic_phys);
+               idx++;
+
+               if (ioapic_res != NULL) {
+                       ioapic_res->start = ioapic_phys;
+                       ioapic_res->end = ioapic_phys + (4 * 1024) - 1;
+                       ioapic_res++;
+               }
+       }
+}
+
+static int __init ioapic_insert_resources(void)
+{
+       int i;
+       struct resource *r = ioapic_resources;
+
+       if (!r) {
+               printk(KERN_ERR
+                      "IO APIC resources could be not be allocated.\n");
+               return -1;
+       }
+
+       for (i = 0; i < nr_ioapics; i++) {
+               insert_resource(&iomem_resource, r);
+               r++;
+       }
+
+       return 0;
+}
+
+/* Insert the IO APIC resources after PCI initialization has occured to handle
+ * IO APICS that are mapped in on a BAR in PCI space. */
+late_initcall(ioapic_insert_resources);
+