]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/kernel/io_apic_32.c
x86: I/O APIC: timer through 8259A second-chance
[linux-2.6-omap-h63xx.git] / arch / x86 / kernel / io_apic_32.c
index 97ac6104d63a0fcfa9fb2b9f9b8c1e51c9b7866d..a69a59d19e187999184a398fa28af9828167c20f 100644 (file)
@@ -58,7 +58,7 @@ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
 static DEFINE_SPINLOCK(ioapic_lock);
 static DEFINE_SPINLOCK(vector_lock);
 
-int timer_over_8254 __initdata = 1;
+int timer_through_8259 __initdata;
 
 /*
  *     Is the SiS APIC rmw bug present ?
@@ -71,6 +71,16 @@ int sis_apic_bug = -1;
  */
 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;
+
 static int disable_timer_pin_1 __initdata;
 
 /*
@@ -915,6 +925,7 @@ void __init setup_ioapic_dest(void)
 }
 #endif
 
+#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
 /*
  * EISA Edge/Level control register, ELCR
  */
@@ -928,6 +939,7 @@ static int EISA_ELCR(unsigned int irq)
                        "Broken MPtable reports ISA irq %d\n", irq);
        return 0;
 }
+#endif
 
 /* ISA interrupts are always polarity zero edge triggered,
  * when listed as conforming in the MP table. */
@@ -1013,6 +1025,7 @@ static int MPBIOS_trigger(int idx)
                        trigger = test_bit(bus, mp_bus_not_pci)?
                                        default_ISA_trigger(idx):
                                        default_PCI_trigger(idx);
+#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
                        switch (mp_bus_id_to_type[bus])
                        {
                                case MP_BUS_ISA: /* ISA pin */
@@ -1042,6 +1055,7 @@ static int MPBIOS_trigger(int idx)
                                        break;
                                }
                        }
+#endif
                        break;
                }
                case 1: /* edge */
@@ -1214,7 +1228,6 @@ static void __init setup_IO_APIC_irqs(void)
 {
        struct IO_APIC_route_entry entry;
        int apic, pin, idx, irq, first_notcon = 1, vector;
-       unsigned long flags;
 
        apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n");
 
@@ -1280,9 +1293,7 @@ static void __init setup_IO_APIC_irqs(void)
                        if (!apic && (irq < 16))
                                disable_8259A_irq(irq);
                }
-               spin_lock_irqsave(&ioapic_lock, flags);
-               __ioapic_write_entry(apic, pin, entry);
-               spin_unlock_irqrestore(&ioapic_lock, flags);
+               ioapic_write_entry(apic, pin, entry);
        }
        }
 
@@ -1291,25 +1302,21 @@ static void __init setup_IO_APIC_irqs(void)
 }
 
 /*
- * Set up the 8259A-master output pin:
+ * 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;
 
        memset(&entry,0,sizeof(entry));
 
-       disable_8259A_irq(0);
-
-       /* mask LVT0 */
-       apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
-
        /*
         * 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.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
        entry.delivery_mode = INT_DELIVERY_MODE;
        entry.polarity = 0;
@@ -1318,17 +1325,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 ...
         */
-       irq_desc[0].chip = &ioapic_chip;
-       set_irq_handler(0, handle_edge_irq);
+       ioapic_register_intr(0, vector, IOAPIC_EDGE);
 
        /*
         * Add it to the IO-APIC irq-routing table:
         */
        ioapic_write_entry(apic, pin, entry);
-
-       enable_8259A_irq(0);
 }
 
 void __init print_IO_APIC(void)
@@ -1478,8 +1482,8 @@ void /*__init*/ 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);
@@ -1688,7 +1692,7 @@ void disable_IO_APIC(void)
                entry.delivery_mode   = dest_ExtINT; /* ExtInt */
                entry.vector          = 0;
                entry.dest.physical.physical_dest =
-                                       GET_APIC_ID(apic_read(APIC_ID));
+                                       GET_APIC_ID(read_apic_id());
 
                /*
                 * Add it to the IO-APIC irq-routing table:
@@ -1985,8 +1989,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_vector[tmp]) {
+               if (IO_APIC_IRQ(irq) && !irq_vector[irq]) {
                        /*
                         * Hmm.. We don't have an entry for this,
                         * so default to an old-fashioned 8259
@@ -2027,7 +2030,7 @@ static void unmask_lapic_irq (unsigned int irq)
 }
 
 static struct irq_chip lapic_chip __read_mostly = {
-       .name           = "local-APIC-edge",
+       .name           = "local-APIC",
        .mask           = mask_lapic_irq,
        .unmask         = unmask_lapic_irq,
        .eoi            = ack_apic,
@@ -2058,7 +2061,7 @@ static void __init 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;
@@ -2110,8 +2113,6 @@ static inline void unlock_ExtINT_logic(void)
        ioapic_write_entry(apic, pin, entry0);
 }
 
-int timer_uses_ioapic_pin_0;
-
 /*
  * This code may look a bit paranoid, but it's supposed to cooperate with
  * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ
@@ -2121,11 +2122,16 @@ int timer_uses_ioapic_pin_0;
 static inline void __init check_timer(void)
 {
        int apic1, pin1, apic2, pin2;
+       int no_pin1 = 0;
        int vector;
+       unsigned int ver;
        unsigned long flags;
 
        local_irq_save(flags);
 
+       ver = apic_read(APIC_LVR);
+       ver = GET_APIC_VERSION(ver);
+
        /*
         * get/set the timer IRQ vector:
         */
@@ -2134,37 +2140,53 @@ static inline void __init check_timer(void)
        set_intr_gate(vector, interrupt[0]);
 
        /*
-        * 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.  Also
+        * timer interrupts need to be acknowledged manually in
+        * the 8259A for the i82489DX when using the NMI
+        * watchdog as that APIC treats NMIs as level-triggered.
+        * The AEOI mode will finish them in the 8259A
+        * automatically.
         */
        apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
        init_8259A(1);
-       timer_ack = 1;
-       if (timer_over_8254 > 0)
-               enable_8259A_irq(0);
+       timer_ack = (nmi_watchdog == NMI_IO_APIC && !APIC_INTEGRATED(ver));
 
        pin1  = find_isa_irq_pin(0, mp_INT);
        apic1 = find_isa_irq_apic(0, mp_INT);
        pin2  = ioapic_i8259.pin;
        apic2 = ioapic_i8259.apic;
 
-       if (pin1 == 0)
-               timer_uses_ioapic_pin_0 = 1;
-
        printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
                vector, apic1, pin1, apic2, pin2);
 
+       /*
+        * Some BIOS writers are clueless and report the ExtINTA
+        * I/O APIC input from the cascaded 8259A as the timer
+        * interrupt input.  So just in case, if only one pin
+        * was found above, try it both directly and through the
+        * 8259A.
+        */
+       if (pin1 == -1) {
+               pin1 = pin2;
+               apic1 = apic2;
+               no_pin1 = 1;
+       } else if (pin2 == -1) {
+               pin2 = pin1;
+               apic2 = apic1;
+       }
+
        if (pin1 != -1) {
                /*
                 * Ok, does IRQ0 through the IOAPIC work?
                 */
+               if (no_pin1) {
+                       add_pin_to_irq(0, apic1, pin1);
+                       setup_timer_IRQ0_pin(apic1, pin1, vector);
+               }
                unmask_IO_APIC_irq(0);
                if (timer_irq_works()) {
                        if (nmi_watchdog == NMI_IO_APIC) {
-                               disable_8259A_irq(0);
                                setup_nmi();
                                enable_8259A_irq(0);
                        }
@@ -2173,43 +2195,46 @@ static inline void __init check_timer(void)
                        goto out;
                }
                clear_IO_APIC_pin(apic1, pin1);
-               printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to "
-                               "IO-APIC\n");
-       }
+               if (!no_pin1)
+                       printk(KERN_ERR "..MP-BIOS bug: "
+                              "8254 timer not connected to IO-APIC\n");
 
-       printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");
-       if (pin2 != -1) {
+               printk(KERN_INFO "...trying to set up timer (IRQ0) "
+                      "through the 8259A ... ");
                printk("\n..... (found pin %d) ...", pin2);
                /*
                 * legacy devices should be connected to IO APIC #0
                 */
-               setup_ExtINT_IRQ0_pin(apic2, pin2, vector);
+               replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
+               setup_timer_IRQ0_pin(apic2, pin2, vector);
+               unmask_IO_APIC_irq(0);
+               enable_8259A_irq(0);
                if (timer_irq_works()) {
                        printk("works.\n");
-                       if (pin1 != -1)
-                               replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
-                       else
-                               add_pin_to_irq(0, apic2, pin2);
+                       timer_through_8259 = 1;
                        if (nmi_watchdog == NMI_IO_APIC) {
+                               disable_8259A_irq(0);
                                setup_nmi();
+                               enable_8259A_irq(0);
                        }
                        goto out;
                }
                /*
                 * Cleanup, just in case ...
                 */
+               disable_8259A_irq(0);
                clear_IO_APIC_pin(apic2, pin2);
+               printk(" failed.\n");
        }
-       printk(" failed.\n");
 
        if (nmi_watchdog == NMI_IO_APIC) {
                printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");
                nmi_watchdog = 0;
        }
+       timer_ack = 0;
 
        printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
 
-       disable_8259A_irq(0);
        set_irq_chip_and_handler_name(0, &lapic_chip, handle_fasteoi_irq,
                                      "fasteoi");
        apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector);   /* Fixed mode */
@@ -2219,12 +2244,12 @@ static inline void __init check_timer(void)
                printk(" works.\n");
                goto out;
        }
+       disable_8259A_irq(0);
        apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);
        printk(" failed.\n");
 
        printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");
 
-       timer_ack = 0;
        init_8259A(0);
        make_8259A_irq(0);
        apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
@@ -2281,20 +2306,6 @@ void __init setup_IO_APIC(void)
                print_IO_APIC();
 }
 
-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);
-
 /*
  *     Called after all the initialization is done. If we didnt find any
  *     APIC bugs then we can allow the modify fast path
@@ -2431,6 +2442,7 @@ void destroy_irq(unsigned int irq)
        dynamic_irq_cleanup(irq);
 
        spin_lock_irqsave(&vector_lock, flags);
+       clear_bit(irq_vector[irq], used_vectors);
        irq_vector[irq] = 0;
        spin_unlock_irqrestore(&vector_lock, flags);
 }
@@ -2743,7 +2755,6 @@ int __init io_apic_get_redir_entries (int ioapic)
 int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low)
 {
        struct IO_APIC_route_entry entry;
-       unsigned long flags;
 
        if (!IO_APIC_IRQ(irq)) {
                printk(KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n",
@@ -2784,9 +2795,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
        if (!ioapic && (irq < 16))
                disable_8259A_irq(irq);
 
-       spin_lock_irqsave(&ioapic_lock, flags);
-       __ioapic_write_entry(ioapic, pin, entry);
-       spin_unlock_irqrestore(&ioapic_lock, flags);
+       ioapic_write_entry(ioapic, pin, entry);
 
        return 0;
 }