x86, ioapic: Optimize pin_2_irq
[safe/jmp/linux-2.6] / arch / x86 / kernel / apic / io_apic.c
index 97e1e3e..0d35f46 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/jiffies.h>     /* time_after() */
+#include <linux/slab.h>
 #ifdef CONFIG_ACPI
 #include <acpi/acpi_bus.h>
 #endif
@@ -88,6 +89,9 @@ int nr_ioapics;
 /* IO APIC gsi routing info */
 struct mp_ioapic_gsi  mp_gsi_routing[MAX_IO_APICS];
 
+/* The last gsi number used */
+u32 gsi_end;
+
 /* MP IRQ source entries */
 struct mpc_intsrc mp_irqs[MAX_IRQ_SOURCES];
 
@@ -143,12 +147,6 @@ static struct irq_cfg irq_cfgx[NR_IRQS_LEGACY];
 static struct irq_cfg irq_cfgx[NR_IRQS];
 #endif
 
-void __init io_apic_disable_legacy(void)
-{
-       nr_legacy_irqs = 0;
-       nr_irqs_gsi = 0;
-}
-
 int __init arch_early_irq_init(void)
 {
        struct irq_cfg *cfg;
@@ -157,6 +155,11 @@ int __init arch_early_irq_init(void)
        int node;
        int i;
 
+       if (!legacy_pic->nr_legacy_irqs) {
+               nr_irqs_gsi = 0;
+               io_apic_irqs = ~0UL;
+       }
+
        cfg = irq_cfgx;
        count = ARRAY_SIZE(irq_cfgx);
        node= cpu_to_node(boot_cpu_id);
@@ -170,7 +173,7 @@ int __init arch_early_irq_init(void)
                 * For legacy IRQ's, start with assigning irq0 to irq15 to
                 * IRQ0_VECTOR to IRQ15_VECTOR on cpu 0.
                 */
-               if (i < nr_legacy_irqs) {
+               if (i < legacy_pic->nr_legacy_irqs) {
                        cfg[i].vector = IRQ0_VECTOR + i;
                        cpumask_set_cpu(0, cfg[i].domain);
                }
@@ -852,7 +855,7 @@ static int __init find_isa_irq_apic(int irq, int type)
  */
 static int EISA_ELCR(unsigned int irq)
 {
-       if (irq < nr_legacy_irqs) {
+       if (irq < legacy_pic->nr_legacy_irqs) {
                unsigned int port = 0x4d0 + (irq >> 3);
                return (inb(port) >> (irq & 7)) & 1;
        }
@@ -1016,7 +1019,7 @@ static inline int irq_trigger(int idx)
 int (*ioapic_renumber_irq)(int ioapic, int irq);
 static int pin_2_irq(int idx, int apic, int pin)
 {
-       int irq, i;
+       int irq;
        int bus = mp_irqs[idx].srcbus;
 
        /*
@@ -1028,18 +1031,13 @@ static int pin_2_irq(int idx, int apic, int pin)
        if (test_bit(bus, mp_bus_not_pci)) {
                irq = mp_irqs[idx].srcbusirq;
        } else {
-               /*
-                * PCI IRQs are mapped in order
-                */
-               i = irq = 0;
-               while (i < apic)
-                       irq += nr_ioapic_registers[i++];
-               irq += pin;
+               u32 gsi = mp_gsi_routing[apic].gsi_base + pin;
                /*
                  * For MPS mode, so far only needed by ES7000 platform
                  */
                if (ioapic_renumber_irq)
-                       irq = ioapic_renumber_irq(apic, irq);
+                       gsi = ioapic_renumber_irq(apic, gsi);
+               irq = gsi;
        }
 
 #ifdef CONFIG_X86_32
@@ -1269,6 +1267,14 @@ void __setup_vector_irq(int cpu)
        /* Mark the inuse vectors */
        for_each_irq_desc(irq, desc) {
                cfg = desc->chip_data;
+
+               /*
+                * If it is a legacy IRQ handled by the legacy PIC, this cpu
+                * will be part of the irq_cfg's domain.
+                */
+               if (irq < legacy_pic->nr_legacy_irqs && !IO_APIC_IRQ(irq))
+                       cpumask_set_cpu(cpu, cfg->domain);
+
                if (!cpumask_test_cpu(cpu, cfg->domain))
                        continue;
                vector = cfg->vector;
@@ -1439,7 +1445,7 @@ static void setup_IO_APIC_irq(int apic_id, int pin, unsigned int irq, struct irq
         * controllers like 8259. Now that IO-APIC can handle this irq, update
         * the cfg->domain.
         */
-       if (irq < nr_legacy_irqs && cpumask_test_cpu(0, cfg->domain))
+       if (irq < legacy_pic->nr_legacy_irqs && cpumask_test_cpu(0, cfg->domain))
                apic->vector_allocation_domain(0, cfg->domain);
 
        if (assign_irq_vector(irq, cfg, apic->target_cpus()))
@@ -1463,8 +1469,8 @@ static void setup_IO_APIC_irq(int apic_id, int pin, unsigned int irq, struct irq
        }
 
        ioapic_register_intr(irq, desc, trigger);
-       if (irq < nr_legacy_irqs)
-               disable_8259A_irq(irq);
+       if (irq < legacy_pic->nr_legacy_irqs)
+               legacy_pic->chip->mask(irq);
 
        ioapic_write_entry(apic_id, pin, entry);
 }
@@ -1695,7 +1701,7 @@ __apicdebuginit(void) print_IO_APIC(void)
        printk(KERN_DEBUG ".... IRQ redirection table:\n");
 
        printk(KERN_DEBUG " NR Dst Mask Trig IRR Pol"
-                         " Stat Dmod Deli Vect:   \n");
+                         " Stat Dmod Deli Vect:\n");
 
        for (i = 0; i <= reg_01.bits.entries; i++) {
                struct IO_APIC_route_entry entry;
@@ -1873,7 +1879,7 @@ __apicdebuginit(void) print_PIC(void)
        unsigned int v;
        unsigned long flags;
 
-       if (!nr_legacy_irqs)
+       if (!legacy_pic->nr_legacy_irqs)
                return;
 
        printk(KERN_DEBUG "\nprinting PIC contents\n");
@@ -1942,22 +1948,10 @@ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
 
 void __init enable_IO_APIC(void)
 {
-       union IO_APIC_reg_01 reg_01;
        int i8259_apic, i8259_pin;
        int apic;
-       unsigned long flags;
-
-       /*
-        * The number of IO-APIC IRQ registers (== #pins):
-        */
-       for (apic = 0; apic < nr_ioapics; apic++) {
-               raw_spin_lock_irqsave(&ioapic_lock, flags);
-               reg_01.raw = io_apic_read(apic, 1);
-               raw_spin_unlock_irqrestore(&ioapic_lock, flags);
-               nr_ioapic_registers[apic] = reg_01.bits.entries+1;
-       }
 
-       if (!nr_legacy_irqs)
+       if (!legacy_pic->nr_legacy_irqs)
                return;
 
        for(apic = 0; apic < nr_ioapics; apic++) {
@@ -2014,7 +2008,7 @@ void disable_IO_APIC(void)
         */
        clear_IO_APIC();
 
-       if (!nr_legacy_irqs)
+       if (!legacy_pic->nr_legacy_irqs)
                return;
 
        /*
@@ -2247,9 +2241,9 @@ static unsigned int startup_ioapic_irq(unsigned int irq)
        struct irq_cfg *cfg;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
-       if (irq < nr_legacy_irqs) {
-               disable_8259A_irq(irq);
-               if (i8259A_irq_pending(irq))
+       if (irq < legacy_pic->nr_legacy_irqs) {
+               legacy_pic->chip->mask(irq);
+               if (legacy_pic->irq_pending(irq))
                        was_pending = 1;
        }
        cfg = irq_cfg(irq);
@@ -2782,8 +2776,8 @@ static inline void init_IO_APIC_traps(void)
                         * so default to an old-fashioned 8259
                         * interrupt if we can..
                         */
-                       if (irq < nr_legacy_irqs)
-                               make_8259A_irq(irq);
+                       if (irq < legacy_pic->nr_legacy_irqs)
+                               legacy_pic->make_irq(irq);
                        else
                                /* Strange. Oh, well.. */
                                desc->chip = &no_irq_chip;
@@ -2940,7 +2934,7 @@ static inline void __init check_timer(void)
        /*
         * get/set the timer IRQ vector:
         */
-       disable_8259A_irq(0);
+       legacy_pic->chip->mask(0);
        assign_irq_vector(0, cfg, apic->target_cpus());
 
        /*
@@ -2953,7 +2947,7 @@ static inline void __init check_timer(void)
         * automatically.
         */
        apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
-       init_8259A(1);
+       legacy_pic->init(1);
 #ifdef CONFIG_X86_32
        {
                unsigned int ver;
@@ -3012,7 +3006,7 @@ static inline void __init check_timer(void)
                if (timer_irq_works()) {
                        if (nmi_watchdog == NMI_IO_APIC) {
                                setup_nmi();
-                               enable_8259A_irq(0);
+                               legacy_pic->chip->unmask(0);
                        }
                        if (disable_timer_pin_1 > 0)
                                clear_IO_APIC_pin(0, pin1);
@@ -3035,14 +3029,14 @@ static inline void __init check_timer(void)
                 */
                replace_pin_at_irq_node(cfg, node, apic1, pin1, apic2, pin2);
                setup_timer_IRQ0_pin(apic2, pin2, cfg->vector);
-               enable_8259A_irq(0);
+               legacy_pic->chip->unmask(0);
                if (timer_irq_works()) {
                        apic_printk(APIC_QUIET, KERN_INFO "....... works.\n");
                        timer_through_8259 = 1;
                        if (nmi_watchdog == NMI_IO_APIC) {
-                               disable_8259A_irq(0);
+                               legacy_pic->chip->mask(0);
                                setup_nmi();
-                               enable_8259A_irq(0);
+                               legacy_pic->chip->unmask(0);
                        }
                        goto out;
                }
@@ -3050,7 +3044,7 @@ static inline void __init check_timer(void)
                 * Cleanup, just in case ...
                 */
                local_irq_disable();
-               disable_8259A_irq(0);
+               legacy_pic->chip->mask(0);
                clear_IO_APIC_pin(apic2, pin2);
                apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n");
        }
@@ -3069,22 +3063,22 @@ static inline void __init check_timer(void)
 
        lapic_register_intr(0, desc);
        apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector);     /* Fixed mode */
-       enable_8259A_irq(0);
+       legacy_pic->chip->unmask(0);
 
        if (timer_irq_works()) {
                apic_printk(APIC_QUIET, KERN_INFO "..... works.\n");
                goto out;
        }
        local_irq_disable();
-       disable_8259A_irq(0);
+       legacy_pic->chip->mask(0);
        apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector);
        apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n");
 
        apic_printk(APIC_QUIET, KERN_INFO
                    "...trying to set up timer as ExtINT IRQ...\n");
 
-       init_8259A(0);
-       make_8259A_irq(0);
+       legacy_pic->init(0);
+       legacy_pic->make_irq(0);
        apic_write(APIC_LVT0, APIC_DM_EXTINT);
 
        unlock_ExtINT_logic();
@@ -3126,7 +3120,7 @@ void __init setup_IO_APIC(void)
        /*
         * calling enable_IO_APIC() is moved to setup_local_APIC for BP
         */
-       io_apic_irqs = nr_legacy_irqs ? ~PIC_IRQS : ~0UL;
+       io_apic_irqs = legacy_pic->nr_legacy_irqs ? ~PIC_IRQS : ~0UL;
 
        apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n");
        /*
@@ -3137,7 +3131,7 @@ void __init setup_IO_APIC(void)
        sync_Arb_IDs();
        setup_IO_APIC_irqs();
        init_IO_APIC_traps();
-       if (nr_legacy_irqs)
+       if (legacy_pic->nr_legacy_irqs)
                check_timer();
 }
 
@@ -3847,7 +3841,11 @@ int __init io_apic_get_redir_entries (int ioapic)
        reg_01.raw = io_apic_read(ioapic, 1);
        raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 
-       return reg_01.bits.entries;
+       /* The register returns the maximum index redir index
+        * supported, which is one less than the total number of redir
+        * entries.
+        */
+       return reg_01.bits.entries + 1;
 }
 
 void __init probe_nr_irqs_gsi(void)
@@ -3863,7 +3861,7 @@ void __init probe_nr_irqs_gsi(void)
 
                nr = 0;
                for (idx = 0; idx < nr_ioapics; idx++)
-                       nr += io_apic_get_redir_entries(idx) + 1;
+                       nr += io_apic_get_redir_entries(idx);
 
                if (nr > nr_irqs_gsi)
                        nr_irqs_gsi = nr;
@@ -3928,7 +3926,7 @@ static int __io_apic_set_pci_routing(struct device *dev, int irq,
        /*
         * IRQs < 16 are already in the irq_2_pin[] map
         */
-       if (irq >= nr_legacy_irqs) {
+       if (irq >= legacy_pic->nr_legacy_irqs) {
                cfg = desc->chip_data;
                if (add_pin_to_irq_node_nopanic(cfg, node, ioapic, pin)) {
                        printk(KERN_INFO "can not add pin %d for irq %d\n",
@@ -4074,22 +4072,27 @@ int __init io_apic_get_version(int ioapic)
        return reg_01.bits.version;
 }
 
-int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity)
+int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity)
 {
-       int i;
+       int ioapic, pin, idx;
 
        if (skip_ioapic_setup)
                return -1;
 
-       for (i = 0; i < mp_irq_entries; i++)
-               if (mp_irqs[i].irqtype == mp_INT &&
-                   mp_irqs[i].srcbusirq == bus_irq)
-                       break;
-       if (i >= mp_irq_entries)
+       ioapic = mp_find_ioapic(gsi);
+       if (ioapic < 0)
+               return -1;
+
+       pin = mp_find_ioapic_pin(ioapic, gsi);
+       if (pin < 0)
+               return -1;
+
+       idx = find_irq_entry(ioapic, pin, mp_INT);
+       if (idx < 0)
                return -1;
 
-       *trigger = irq_trigger(i);
-       *polarity = irq_polarity(i);
+       *trigger = irq_trigger(idx);
+       *polarity = irq_polarity(idx);
        return 0;
 }
 
@@ -4230,7 +4233,7 @@ void __init ioapic_insert_resources(void)
        }
 }
 
-int mp_find_ioapic(int gsi)
+int mp_find_ioapic(u32 gsi)
 {
        int i = 0;
 
@@ -4245,7 +4248,7 @@ int mp_find_ioapic(int gsi)
        return -1;
 }
 
-int mp_find_ioapic_pin(int ioapic, int gsi)
+int mp_find_ioapic_pin(int ioapic, u32 gsi)
 {
        if (WARN_ON(ioapic == -1))
                return -1;
@@ -4273,6 +4276,7 @@ static int bad_ioapic(unsigned long address)
 void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
 {
        int idx = 0;
+       int entries;
 
        if (bad_ioapic(address))
                return;
@@ -4291,9 +4295,17 @@ void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
         * Build basic GSI lookup table to facilitate gsi->io_apic lookups
         * and to prevent reprogramming of IOAPIC pins (PCI GSIs).
         */
+       entries = io_apic_get_redir_entries(idx);
        mp_gsi_routing[idx].gsi_base = gsi_base;
-       mp_gsi_routing[idx].gsi_end = gsi_base +
-           io_apic_get_redir_entries(idx);
+       mp_gsi_routing[idx].gsi_end = gsi_base + entries - 1;
+
+       /*
+        * The number of IO-APIC IRQ registers (== #pins):
+        */
+       nr_ioapic_registers[idx] = entries;
+
+       if (mp_gsi_routing[idx].gsi_end > gsi_end)
+               gsi_end = mp_gsi_routing[idx].gsi_end;
 
        printk(KERN_INFO "IOAPIC[%d]: apic_id %d, version %d, address 0x%x, "
               "GSI %d-%d\n", idx, mp_ioapics[idx].apicid,
@@ -4302,3 +4314,24 @@ void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
 
        nr_ioapics++;
 }
+
+/* Enable IOAPIC early just for system timer */
+void __init pre_init_apic_IRQ0(void)
+{
+       struct irq_cfg *cfg;
+       struct irq_desc *desc;
+
+       printk(KERN_INFO "Early APIC setup for system timer0\n");
+#ifndef CONFIG_SMP
+       phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid);
+#endif
+       desc = irq_to_desc_alloc_node(0, 0);
+
+       setup_local_APIC();
+
+       cfg = irq_cfg(0);
+       add_pin_to_irq_node(cfg, 0, 0, 0);
+       set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge");
+
+       setup_IO_APIC_irq(0, 0, 0, desc, 0, 0);
+}