]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/i386/kernel/io_apic.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[linux-2.6-omap-h63xx.git] / arch / i386 / kernel / io_apic.c
index b7287fb499f3f0537b1afd2111e3788976f94aa7..3b7a63e0ed1a5b07121ae63c9e35863ae0469a2b 100644 (file)
@@ -91,6 +91,46 @@ static struct irq_pin_list {
        int apic, pin, next;
 } irq_2_pin[PIN_MAP_SIZE];
 
+struct io_apic {
+       unsigned int index;
+       unsigned int unused[3];
+       unsigned int data;
+};
+
+static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
+{
+       return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx)
+               + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK);
+}
+
+static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
+{
+       struct io_apic __iomem *io_apic = io_apic_base(apic);
+       writel(reg, &io_apic->index);
+       return readl(&io_apic->data);
+}
+
+static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
+{
+       struct io_apic __iomem *io_apic = io_apic_base(apic);
+       writel(reg, &io_apic->index);
+       writel(value, &io_apic->data);
+}
+
+/*
+ * Re-write a value: to be used for read-modify-write
+ * cycles where the read already set up the index register.
+ *
+ * Older SiS APIC requires we rewrite the index register
+ */
+static inline void io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value)
+{
+       volatile struct io_apic *io_apic = io_apic_base(apic);
+       if (sis_apic_bug)
+               writel(reg, &io_apic->index);
+       writel(value, &io_apic->data);
+}
+
 union entry_union {
        struct { u32 w1, w2; };
        struct IO_APIC_route_entry entry;
@@ -107,11 +147,33 @@ static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin)
        return eu.entry;
 }
 
+/*
+ * When we write a new IO APIC routing entry, we need to write the high
+ * word first! If the mask bit in the low word is clear, we will enable
+ * the interrupt, and we need to make sure the entry is fully populated
+ * before that happens.
+ */
 static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
 {
        unsigned long flags;
        union entry_union eu;
        eu.entry = e;
+       spin_lock_irqsave(&ioapic_lock, flags);
+       io_apic_write(apic, 0x11 + 2*pin, eu.w2);
+       io_apic_write(apic, 0x10 + 2*pin, eu.w1);
+       spin_unlock_irqrestore(&ioapic_lock, flags);
+}
+
+/*
+ * When we mask an IO APIC routing entry, we need to write the low
+ * word first, in order to set the mask bit before we change the
+ * high bits!
+ */
+static void ioapic_mask_entry(int apic, int pin)
+{
+       unsigned long flags;
+       union entry_union eu = { .entry.mask = 1 };
+
        spin_lock_irqsave(&ioapic_lock, flags);
        io_apic_write(apic, 0x10 + 2*pin, eu.w1);
        io_apic_write(apic, 0x11 + 2*pin, eu.w2);
@@ -234,9 +296,7 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
        /*
         * Disable it in the IO-APIC irq-routing table:
         */
-       memset(&entry, 0, sizeof(entry));
-       entry.mask = 1;
-       ioapic_write_entry(apic, pin, entry);
+       ioapic_mask_entry(apic, pin);
 }
 
 static void clear_IO_APIC (void)
@@ -1184,8 +1244,8 @@ static int __assign_irq_vector(int irq)
 
        BUG_ON((unsigned)irq >= NR_IRQ_VECTORS);
 
-       if (IO_APIC_VECTOR(irq) > 0)
-               return IO_APIC_VECTOR(irq);
+       if (irq_vector[irq] > 0)
+               return irq_vector[irq];
 
        current_vector += 8;
        if (current_vector == SYSCALL_VECTOR)
@@ -1199,7 +1259,7 @@ static int __assign_irq_vector(int irq)
        }
 
        vector = current_vector;
-       IO_APIC_VECTOR(irq) = vector;
+       irq_vector[irq] = vector;
 
        return vector;
 }
@@ -1225,11 +1285,13 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger)
 {
        if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) ||
                        trigger == IOAPIC_LEVEL)
-               set_irq_chip_and_handler(irq, &ioapic_chip,
-                                        handle_fasteoi_irq);
-       else
-               set_irq_chip_and_handler(irq, &ioapic_chip,
-                                        handle_edge_irq);
+               set_irq_chip_and_handler_name(irq, &ioapic_chip,
+                                        handle_fasteoi_irq, "fasteoi");
+       else {
+               irq_desc[irq].status |= IRQ_DELAYED_DISABLE;
+               set_irq_chip_and_handler_name(irq, &ioapic_chip,
+                                        handle_edge_irq, "edge");
+       }
        set_intr_gate(vector, interrupt[irq]);
 }
 
@@ -1967,7 +2029,7 @@ static void ack_ioapic_quirk_irq(unsigned int irq)
  * operation to prevent an edge-triggered interrupt escaping meanwhile.
  * The idea is from Manfred Spraul.  --macro
  */
-       i = IO_APIC_VECTOR(irq);
+       i = irq_vector[irq];
 
        v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
 
@@ -1984,7 +2046,7 @@ static void ack_ioapic_quirk_irq(unsigned int irq)
 
 static int ioapic_retrigger_irq(unsigned int irq)
 {
-       send_IPI_self(IO_APIC_VECTOR(irq));
+       send_IPI_self(irq_vector[irq]);
 
        return 1;
 }
@@ -2020,7 +2082,7 @@ static inline void init_IO_APIC_traps(void)
         */
        for (irq = 0; irq < NR_IRQS ; irq++) {
                int tmp = irq;
-               if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) {
+               if (IO_APIC_IRQ(tmp) && !irq_vector[tmp]) {
                        /*
                         * Hmm.. We don't have an entry for this,
                         * so default to an old-fashioned 8259
@@ -2235,7 +2297,8 @@ static inline void check_timer(void)
        printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
 
        disable_8259A_irq(0);
-       set_irq_chip_and_handler(0, &lapic_chip, handle_fasteoi_irq);
+       set_irq_chip_and_handler_name(0, &lapic_chip, handle_fasteoi_irq,
+                                     "fasteio");
        apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector);   /* Fixed mode */
        enable_8259A_irq(0);
 
@@ -2541,7 +2604,8 @@ int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev)
 
        write_msi_msg(irq, &msg);
 
-       set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq);
+       set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq,
+                                     "edge");
 
        return 0;
 }
@@ -2562,18 +2626,16 @@ void arch_teardown_msi_irq(unsigned int irq)
 
 static void target_ht_irq(unsigned int irq, unsigned int dest)
 {
-       u32 low, high;
-       low  = read_ht_irq_low(irq);
-       high = read_ht_irq_high(irq);
+       struct ht_irq_msg msg;
+       fetch_ht_irq_msg(irq, &msg);
 
-       low  &= ~(HT_IRQ_LOW_DEST_ID_MASK);
-       high &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
+       msg.address_lo &= ~(HT_IRQ_LOW_DEST_ID_MASK);
+       msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
 
-       low  |= HT_IRQ_LOW_DEST_ID(dest);
-       high |= HT_IRQ_HIGH_DEST_ID(dest);
+       msg.address_lo |= HT_IRQ_LOW_DEST_ID(dest);
+       msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
 
-       write_ht_irq_low(irq, low);
-       write_ht_irq_high(irq, high);
+       write_ht_irq_msg(irq, &msg);
 }
 
 static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask)
@@ -2594,7 +2656,7 @@ static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask)
 }
 #endif
 
-static struct hw_interrupt_type ht_irq_chip = {
+static struct irq_chip ht_irq_chip = {
        .name           = "PCI-HT",
        .mask           = mask_ht_irq,
        .unmask         = unmask_ht_irq,
@@ -2611,7 +2673,7 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
 
        vector = assign_irq_vector(irq);
        if (vector >= 0) {
-               u32 low, high;
+               struct ht_irq_msg msg;
                unsigned dest;
                cpumask_t tmp;
 
@@ -2619,9 +2681,10 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
                cpu_set(vector >> 8, tmp);
                dest = cpu_mask_to_apicid(tmp);
 
-               high =  HT_IRQ_HIGH_DEST_ID(dest);
+               msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
 
-               low =   HT_IRQ_LOW_BASE |
+               msg.address_lo =
+                       HT_IRQ_LOW_BASE |
                        HT_IRQ_LOW_DEST_ID(dest) |
                        HT_IRQ_LOW_VECTOR(vector) |
                        ((INT_DEST_MODE == 0) ?
@@ -2633,10 +2696,10 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
                                HT_IRQ_LOW_MT_ARBITRATED) |
                        HT_IRQ_LOW_IRQ_MASKED;
 
-               write_ht_irq_low(irq, low);
-               write_ht_irq_high(irq, high);
+               write_ht_irq_msg(irq, &msg);
 
-               set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq);
+               set_irq_chip_and_handler_name(irq, &ht_irq_chip,
+                                             handle_edge_irq, "edge");
        }
        return vector;
 }