This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / x86_64 / kernel / io_apic.c
index 28c1cb3..93f1ec7 100644 (file)
 #include <asm/smp.h>
 #include <asm/desc.h>
 #include <asm/proto.h>
-#include <asm/mach_apic.h>
 
 #define __apicdebuginit  __init
 
 int sis_apic_bug; /* not actually supported, dummy for compile */
 
+#undef APIC_LOCKUP_DEBUG
+
+#define APIC_LOCKUP_DEBUG
+
 static spinlock_t ioapic_lock = SPIN_LOCK_UNLOCKED;
 
 /*
@@ -80,7 +83,7 @@ int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1};
  * shared ISA-space IRQs, so we have to support them. We are super
  * fast in the common case, and fast for shared ISA-space IRQs.
  */
-static void add_pin_to_irq(unsigned int irq, int apic, int pin)
+static void __init add_pin_to_irq(unsigned int irq, int apic, int pin)
 {
        static int first_free_entry = NR_IRQS;
        struct irq_pin_list *entry = irq_2_pin + irq;
@@ -128,6 +131,10 @@ DO_ACTION( __mask,             0, |= 0x00010000, io_apic_sync(entry->apic) )
                                                /* mask = 1 */
 DO_ACTION( __unmask,           0, &= 0xfffeffff, )
                                                /* mask = 0 */
+DO_ACTION( __mask_and_edge,    0, = (reg & 0xffff7fff) | 0x00010000, )
+                                               /* mask = 1, trigger = 0 */
+DO_ACTION( __unmask_and_level, 0, = (reg & 0xfffeffff) | 0x00008000, )
+                                               /* mask = 0, trigger = 1 */
 
 static void mask_IO_APIC_irq (unsigned int irq)
 {
@@ -310,7 +317,7 @@ __setup("pirq=", ioapic_pirq_setup);
 /*
  * Find the IRQ entry number of a certain pin.
  */
-static int find_irq_entry(int apic, int pin, int type)
+static int __init find_irq_entry(int apic, int pin, int type)
 {
        int i;
 
@@ -394,7 +401,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
 /*
  * EISA Edge/Level control register, ELCR
  */
-static int EISA_ELCR(unsigned int irq)
+static int __init EISA_ELCR(unsigned int irq)
 {
        if (irq < 16) {
                unsigned int port = 0x4d0 + (irq >> 3);
@@ -499,7 +506,7 @@ static int __init MPBIOS_polarity(int idx)
        return polarity;
 }
 
-static int MPBIOS_trigger(int idx)
+static int __init MPBIOS_trigger(int idx)
 {
        int bus = mp_irqs[idx].mpc_srcbus;
        int trigger;
@@ -654,7 +661,11 @@ static inline int IO_APIC_irq_trigger(int irq)
 /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
 u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 };
 
+#ifdef CONFIG_PCI_MSI
 int assign_irq_vector(int irq)
+#else
+int __init assign_irq_vector(int irq)
+#endif
 {
        static int current_vector = FIRST_DEVICE_VECTOR, offset = 0;
 
@@ -723,8 +734,8 @@ void __init setup_IO_APIC_irqs(void)
                 */
                memset(&entry,0,sizeof(entry));
 
-               entry.delivery_mode = INT_DELIVERY_MODE;
-               entry.dest_mode = INT_DEST_MODE;
+               entry.delivery_mode = dest_LowestPrio;
+               entry.dest_mode = INT_DELIVERY_MODE;
                entry.mask = 0;                         /* enable IRQ */
                entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
 
@@ -792,10 +803,10 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector)
         * We use logical delivery to get the timer IRQ
         * to the first CPU.
         */
-       entry.dest_mode = INT_DEST_MODE;
+       entry.dest_mode = INT_DELIVERY_MODE;
        entry.mask = 0;                                 /* unmask IRQ now */
        entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
-       entry.delivery_mode = INT_DELIVERY_MODE;
+       entry.delivery_mode = dest_LowestPrio;
        entry.polarity = 0;
        entry.trigger = 0;
        entry.vector = vector;
@@ -1175,6 +1186,7 @@ void disable_IO_APIC(void)
 static void __init setup_ioapic_ids_from_mpc (void)
 {
        union IO_APIC_reg_00 reg_00;
+       physid_mask_t phys_id_present_map = phys_cpu_present_map;
        int apic;
        int i;
        unsigned char old_id;
@@ -1200,7 +1212,28 @@ static void __init setup_ioapic_ids_from_mpc (void)
                        mp_ioapics[apic].mpc_apicid = reg_00.bits.ID;
                }
 
-               printk(KERN_INFO "Using IO-APIC %d\n", mp_ioapics[apic].mpc_apicid);
+               /*
+                * Sanity check, is the ID really free? Every APIC in a
+                * system must have a unique ID or we get lots of nice
+                * 'stuck on smp_invalidate_needed IPI wait' messages.
+                */
+               if (physid_isset(mp_ioapics[apic].mpc_apicid, phys_id_present_map)) {
+                       printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n",
+                               apic, mp_ioapics[apic].mpc_apicid);
+                       for (i = 0; i < 0xf; i++)
+                               if (!physid_isset(i, phys_id_present_map))
+                                       break;
+                       if (i >= 0xf)
+                               panic("Max APIC ID exceeded!\n");
+                       printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",
+                               i);
+                       physid_set(i, phys_id_present_map);
+                       mp_ioapics[apic].mpc_apicid = i;
+               } else {
+                       printk(KERN_INFO 
+                              "Using IO-APIC %d\n", mp_ioapics[apic].mpc_apicid);
+                       physid_set(mp_ioapics[apic].mpc_apicid, phys_id_present_map);
+               }
 
 
                /*
@@ -1344,7 +1377,61 @@ static unsigned int startup_level_ioapic_irq (unsigned int irq)
 
 static void end_level_ioapic_irq (unsigned int irq)
 {
+       unsigned long v;
+       int i;
+
+/*
+ * It appears there is an erratum which affects at least version 0x11
+ * of I/O APIC (that's the 82093AA and cores integrated into various
+ * chipsets).  Under certain conditions a level-triggered interrupt is
+ * erroneously delivered as edge-triggered one but the respective IRR
+ * bit gets set nevertheless.  As a result the I/O unit expects an EOI
+ * message but it will never arrive and further interrupts are blocked
+ * from the source.  The exact reason is so far unknown, but the
+ * phenomenon was observed when two consecutive interrupt requests
+ * from a given source get delivered to the same CPU and the source is
+ * temporarily disabled in between.
+ *
+ * A workaround is to simulate an EOI message manually.  We achieve it
+ * by setting the trigger mode to edge and then to level when the edge
+ * trigger mode gets detected in the TMR of a local APIC for a
+ * level-triggered interrupt.  We mask the source for the time of the
+ * operation to prevent an edge-triggered interrupt escaping meanwhile.
+ * The idea is from Manfred Spraul.  --macro
+ */
+       i = IO_APIC_VECTOR(irq);
+       v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
+
        ack_APIC_irq();
+
+       if (!(v & (1 << (i & 0x1f)))) {
+#ifdef APIC_LOCKUP_DEBUG
+               struct irq_pin_list *entry;
+#endif
+
+#ifdef APIC_MISMATCH_DEBUG
+               atomic_inc(&irq_mis_count);
+#endif
+               spin_lock(&ioapic_lock);
+               __mask_and_edge_IO_APIC_irq(irq);
+#ifdef APIC_LOCKUP_DEBUG
+               for (entry = irq_2_pin + irq;;) {
+                       unsigned int reg;
+
+                       if (entry->pin == -1)
+                               break;
+                       reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2);
+                       if (reg & 0x00004000)
+                               printk(KERN_CRIT "Aieee!!!  Remote IRR"
+                                       " still set after unlock!\n");
+                       if (!entry->next)
+                               break;
+                       entry = irq_2_pin + entry->next;
+               }
+#endif
+               __unmask_and_level_IO_APIC_irq(irq);
+               spin_unlock(&ioapic_lock);
+       }
 }
 
 static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
@@ -1355,9 +1442,9 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
        dest = cpu_mask_to_apicid(mask);
 
        /*
-        * Only the high 8 bits are valid.
+        * Only the first 8 bits are valid.
         */
-       dest = SET_APIC_LOGICAL_ID(dest);
+       dest = dest << 24;
 
        spin_lock_irqsave(&ioapic_lock, flags);
        __DO_ACTION(1, = dest, )
@@ -1839,7 +1926,7 @@ device_initcall(ioapic_init_sysfs);
 
 #ifdef CONFIG_ACPI_BOOT
 
-#define IO_APIC_MAX_ID         0xFE
+#define IO_APIC_MAX_ID         15
 
 int __init io_apic_get_unique_id (int ioapic, int apic_id)
 {
@@ -1956,8 +2043,8 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
 
        memset(&entry,0,sizeof(entry));
 
-       entry.delivery_mode = INT_DELIVERY_MODE;
-       entry.dest_mode = INT_DEST_MODE;
+       entry.delivery_mode = dest_LowestPrio;
+       entry.dest_mode = INT_DELIVERY_MODE;
        entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
        entry.trigger = edge_level;
        entry.polarity = active_high_low;
@@ -1991,6 +2078,24 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
 
 #endif /*CONFIG_ACPI_BOOT*/
 
+#ifndef CONFIG_SMP
+void send_IPI_self(int vector)
+{
+       unsigned int cfg;
+
+       /*
+        * Wait for idle.
+        */
+       apic_wait_icr_idle();
+       cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL;
+
+       /*
+        * Send the IPI. The write to APIC_ICR fires this off.
+        */
+       apic_write_around(APIC_ICR, cfg);
+}
+#endif
+
 
 /*
  * This function currently is only a helper for the i386 smp boot process where