Merge branch 'intx' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/misc-2.6
[pandora-kernel.git] / drivers / pci / msi.c
index da2c6c2..ed3f7e1 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/smp_lock.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
+#include <linux/msi.h>
 
 #include <asm/errno.h>
 #include <asm/io.h>
 
 static DEFINE_SPINLOCK(msi_lock);
 static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };
-static kmem_cache_t* msi_cachep;
+static struct kmem_cache* msi_cachep;
 
 static int pci_msi_enable = 1;
 
-static struct msi_ops *msi_ops;
-
-int
-msi_register(struct msi_ops *ops)
-{
-       msi_ops = ops;
-       return 0;
-}
-
 static int msi_cache_init(void)
 {
        msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc),
@@ -53,21 +45,20 @@ static void msi_set_mask_bit(unsigned int irq, int flag)
        struct msi_desc *entry;
 
        entry = msi_desc[irq];
-       if (!entry || !entry->dev || !entry->mask_base)
-               return;
+       BUG_ON(!entry || !entry->dev);
        switch (entry->msi_attrib.type) {
        case PCI_CAP_ID_MSI:
-       {
-               int             pos;
-               u32             mask_bits;
-
-               pos = (long)entry->mask_base;
-               pci_read_config_dword(entry->dev, pos, &mask_bits);
-               mask_bits &= ~(1);
-               mask_bits |= flag;
-               pci_write_config_dword(entry->dev, pos, mask_bits);
+               if (entry->msi_attrib.maskbit) {
+                       int             pos;
+                       u32             mask_bits;
+
+                       pos = (long)entry->mask_base;
+                       pci_read_config_dword(entry->dev, pos, &mask_bits);
+                       mask_bits &= ~(1);
+                       mask_bits |= flag;
+                       pci_write_config_dword(entry->dev, pos, mask_bits);
+               }
                break;
-       }
        case PCI_CAP_ID_MSIX:
        {
                int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
@@ -76,12 +67,14 @@ static void msi_set_mask_bit(unsigned int irq, int flag)
                break;
        }
        default:
+               BUG();
                break;
        }
 }
 
-static void read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
+void read_msi_msg(unsigned int irq, struct msi_msg *msg)
 {
+       struct msi_desc *entry = get_irq_data(irq);
        switch(entry->msi_attrib.type) {
        case PCI_CAP_ID_MSI:
        {
@@ -118,8 +111,9 @@ static void read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
        }
 }
 
-static void write_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
+void write_msi_msg(unsigned int irq, struct msi_msg *msg)
 {
+       struct msi_desc *entry = get_irq_data(irq);
        switch (entry->msi_attrib.type) {
        case PCI_CAP_ID_MSI:
        {
@@ -157,135 +151,16 @@ static void write_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
        }
 }
 
-#ifdef CONFIG_SMP
-static void set_msi_affinity(unsigned int irq, cpumask_t cpu_mask)
-{
-       struct msi_desc *entry;
-       struct msi_msg msg;
-
-       entry = msi_desc[irq];
-       if (!entry || !entry->dev)
-               return;
-
-       read_msi_msg(entry, &msg);
-       msi_ops->target(irq, cpu_mask, &msg);
-       write_msi_msg(entry, &msg);
-       set_native_irq_info(irq, cpu_mask);
-}
-#else
-#define set_msi_affinity NULL
-#endif /* CONFIG_SMP */
-
-static void mask_MSI_irq(unsigned int irq)
+void mask_msi_irq(unsigned int irq)
 {
        msi_set_mask_bit(irq, 1);
 }
 
-static void unmask_MSI_irq(unsigned int irq)
+void unmask_msi_irq(unsigned int irq)
 {
        msi_set_mask_bit(irq, 0);
 }
 
-static unsigned int startup_msi_irq_wo_maskbit(unsigned int irq)
-{
-       struct msi_desc *entry;
-       unsigned long flags;
-
-       spin_lock_irqsave(&msi_lock, flags);
-       entry = msi_desc[irq];
-       if (!entry || !entry->dev) {
-               spin_unlock_irqrestore(&msi_lock, flags);
-               return 0;
-       }
-       entry->msi_attrib.state = 1;    /* Mark it active */
-       spin_unlock_irqrestore(&msi_lock, flags);
-
-       return 0;       /* never anything pending */
-}
-
-static unsigned int startup_msi_irq_w_maskbit(unsigned int irq)
-{
-       startup_msi_irq_wo_maskbit(irq);
-       unmask_MSI_irq(irq);
-       return 0;       /* never anything pending */
-}
-
-static void shutdown_msi_irq(unsigned int irq)
-{
-       struct msi_desc *entry;
-       unsigned long flags;
-
-       spin_lock_irqsave(&msi_lock, flags);
-       entry = msi_desc[irq];
-       if (entry && entry->dev)
-               entry->msi_attrib.state = 0;    /* Mark it not active */
-       spin_unlock_irqrestore(&msi_lock, flags);
-}
-
-static void end_msi_irq_wo_maskbit(unsigned int irq)
-{
-       move_native_irq(irq);
-       ack_APIC_irq();
-}
-
-static void end_msi_irq_w_maskbit(unsigned int irq)
-{
-       move_native_irq(irq);
-       unmask_MSI_irq(irq);
-       ack_APIC_irq();
-}
-
-static void do_nothing(unsigned int irq)
-{
-}
-
-/*
- * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI-X Capability Structure.
- */
-static struct hw_interrupt_type msix_irq_type = {
-       .typename       = "PCI-MSI-X",
-       .startup        = startup_msi_irq_w_maskbit,
-       .shutdown       = shutdown_msi_irq,
-       .enable         = unmask_MSI_irq,
-       .disable        = mask_MSI_irq,
-       .ack            = mask_MSI_irq,
-       .end            = end_msi_irq_w_maskbit,
-       .set_affinity   = set_msi_affinity
-};
-
-/*
- * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI Capability Structure with
- * Mask-and-Pending Bits.
- */
-static struct hw_interrupt_type msi_irq_w_maskbit_type = {
-       .typename       = "PCI-MSI",
-       .startup        = startup_msi_irq_w_maskbit,
-       .shutdown       = shutdown_msi_irq,
-       .enable         = unmask_MSI_irq,
-       .disable        = mask_MSI_irq,
-       .ack            = mask_MSI_irq,
-       .end            = end_msi_irq_w_maskbit,
-       .set_affinity   = set_msi_affinity
-};
-
-/*
- * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI Capability Structure without
- * Mask-and-Pending Bits.
- */
-static struct hw_interrupt_type msi_irq_wo_maskbit_type = {
-       .typename       = "PCI-MSI",
-       .startup        = startup_msi_irq_wo_maskbit,
-       .shutdown       = shutdown_msi_irq,
-       .enable         = do_nothing,
-       .disable        = do_nothing,
-       .ack            = do_nothing,
-       .end            = end_msi_irq_wo_maskbit,
-       .set_affinity   = set_msi_affinity
-};
-
 static int msi_free_irq(struct pci_dev* dev, int irq);
 static int msi_init(void)
 {
@@ -301,22 +176,6 @@ static int msi_init(void)
                return status;
        }
 
-       status = msi_arch_init();
-       if (status < 0) {
-               pci_msi_enable = 0;
-               printk(KERN_WARNING
-                      "PCI: MSI arch init failed.  MSI disabled.\n");
-               return status;
-       }
-
-       if (! msi_ops) {
-               pci_msi_enable = 0;
-               printk(KERN_WARNING
-                      "PCI: MSI ops not registered. MSI disabled.\n");
-               status = -EINVAL;
-               return status;
-       }
-
        status = msi_cache_init();
        if (status < 0) {
                pci_msi_enable = 0;
@@ -350,7 +209,7 @@ static void attach_msi_entry(struct msi_desc *entry, int irq)
        spin_unlock_irqrestore(&msi_lock, flags);
 }
 
-static int create_msi_irq(struct hw_interrupt_type *handler)
+static int create_msi_irq(void)
 {
        struct msi_desc *entry;
        int irq;
@@ -365,7 +224,6 @@ static int create_msi_irq(struct hw_interrupt_type *handler)
                return -EBUSY;
        }
 
-       set_irq_chip(irq, handler);
        set_irq_data(irq, entry);
 
        return irq;
@@ -397,10 +255,8 @@ static void enable_msi_mode(struct pci_dev *dev, int pos, int type)
                pci_write_config_word(dev, msi_control_reg(pos), control);
                dev->msix_enabled = 1;
        }
-       if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
-               /* PCI Express Endpoint device detected */
-               pci_intx(dev, 0);  /* disable intx */
-       }
+
+       pci_intx(dev, 0);  /* disable intx */
 }
 
 void disable_msi_mode(struct pci_dev *dev, int pos, int type)
@@ -418,10 +274,8 @@ void disable_msi_mode(struct pci_dev *dev, int pos, int type)
                pci_write_config_word(dev, msi_control_reg(pos), control);
                dev->msix_enabled = 0;
        }
-       if (pci_find_capability(dev, PCI_CAP_ID_EXP)) {
-               /* PCI Express Endpoint device detected */
-               pci_intx(dev, 1);  /* enable intx */
-       }
+
+       pci_intx(dev, 1);  /* enable intx */
 }
 
 static int msi_lookup_irq(struct pci_dev *dev, int type)
@@ -555,7 +409,7 @@ int pci_save_msix_state(struct pci_dev *dev)
                struct msi_desc *entry;
 
                entry = msi_desc[irq];
-               read_msi_msg(entry, &entry->msg_save);
+               read_msi_msg(irq, &entry->msg_save);
 
                tail = msi_desc[irq]->link.tail;
                irq = tail;
@@ -594,7 +448,7 @@ void pci_restore_msix_state(struct pci_dev *dev)
        irq = head = dev->irq;
        while (head != tail) {
                entry = msi_desc[irq];
-               write_msi_msg(entry, &entry->msg_save);
+               write_msi_msg(irq, &entry->msg_save);
 
                tail = msi_desc[irq]->link.tail;
                irq = tail;
@@ -606,39 +460,6 @@ void pci_restore_msix_state(struct pci_dev *dev)
 }
 #endif
 
-static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry)
-{
-       int status;
-       struct msi_msg msg;
-       int pos;
-       u16 control;
-
-       pos = entry->msi_attrib.pos;
-       pci_read_config_word(dev, msi_control_reg(pos), &control);
-
-       /* Configure MSI capability structure */
-       status = msi_ops->setup(dev, dev->irq, &msg);
-       if (status < 0)
-               return status;
-
-       write_msi_msg(entry, &msg);
-       if (entry->msi_attrib.maskbit) {
-               unsigned int maskbits, temp;
-               /* All MSIs are unmasked by default, Mask them all */
-               pci_read_config_dword(dev,
-                       msi_mask_bits_reg(pos, is_64bit_address(control)),
-                       &maskbits);
-               temp = (1 << multi_msi_capable(control));
-               temp = ((temp - 1) & ~temp);
-               maskbits |= temp;
-               pci_write_config_dword(dev,
-                       msi_mask_bits_reg(pos, is_64bit_address(control)),
-                       maskbits);
-       }
-
-       return 0;
-}
-
 /**
  * msi_capability_init - configure device's MSI capability structure
  * @dev: pointer to the pci_dev data structure of MSI device function
@@ -654,16 +475,11 @@ static int msi_capability_init(struct pci_dev *dev)
        struct msi_desc *entry;
        int pos, irq;
        u16 control;
-       struct hw_interrupt_type *handler;
 
        pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
        pci_read_config_word(dev, msi_control_reg(pos), &control);
        /* MSI Entry Initialization */
-       handler = &msi_irq_wo_maskbit_type;
-       if (is_mask_bit_support(control))
-               handler = &msi_irq_w_maskbit_type;
-
-       irq = create_msi_irq(handler);
+       irq = create_msi_irq();
        if (irq < 0)
                return irq;
 
@@ -671,22 +487,32 @@ static int msi_capability_init(struct pci_dev *dev)
        entry->link.head = irq;
        entry->link.tail = irq;
        entry->msi_attrib.type = PCI_CAP_ID_MSI;
-       entry->msi_attrib.state = 0;                    /* Mark it not active */
        entry->msi_attrib.is_64 = is_64bit_address(control);
        entry->msi_attrib.entry_nr = 0;
        entry->msi_attrib.maskbit = is_mask_bit_support(control);
        entry->msi_attrib.default_irq = dev->irq;       /* Save IOAPIC IRQ */
        entry->msi_attrib.pos = pos;
-       dev->irq = irq;
-       entry->dev = dev;
        if (is_mask_bit_support(control)) {
                entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
                                is_64bit_address(control));
        }
+       entry->dev = dev;
+       if (entry->msi_attrib.maskbit) {
+               unsigned int maskbits, temp;
+               /* All MSIs are unmasked by default, Mask them all */
+               pci_read_config_dword(dev,
+                       msi_mask_bits_reg(pos, is_64bit_address(control)),
+                       &maskbits);
+               temp = (1 << multi_msi_capable(control));
+               temp = ((temp - 1) & ~temp);
+               maskbits |= temp;
+               pci_write_config_dword(dev,
+                       msi_mask_bits_reg(pos, is_64bit_address(control)),
+                       maskbits);
+       }
        /* Configure MSI capability structure */
-       status = msi_register_init(dev, entry);
-       if (status != 0) {
-               dev->irq = entry->msi_attrib.default_irq;
+       status = arch_setup_msi_irq(irq, dev);
+       if (status < 0) {
                destroy_msi_irq(irq);
                return status;
        }
@@ -695,6 +521,7 @@ static int msi_capability_init(struct pci_dev *dev)
        /* Set MSI enabled bits  */
        enable_msi_mode(dev, pos, PCI_CAP_ID_MSI);
 
+       dev->irq = irq;
        return 0;
 }
 
@@ -712,7 +539,6 @@ static int msix_capability_init(struct pci_dev *dev,
                                struct msix_entry *entries, int nvec)
 {
        struct msi_desc *head = NULL, *tail = NULL, *entry = NULL;
-       struct msi_msg msg;
        int status;
        int irq, pos, i, j, nr_entries, temp = 0;
        unsigned long phys_addr;
@@ -736,7 +562,7 @@ static int msix_capability_init(struct pci_dev *dev,
 
        /* MSI-X Table Initialization */
        for (i = 0; i < nvec; i++) {
-               irq = create_msi_irq(&msix_irq_type);
+               irq = create_msi_irq();
                if (irq < 0)
                        break;
 
@@ -744,7 +570,6 @@ static int msix_capability_init(struct pci_dev *dev,
                j = entries[i].entry;
                entries[i].vector = irq;
                entry->msi_attrib.type = PCI_CAP_ID_MSIX;
-               entry->msi_attrib.state = 0;            /* Mark it not active */
                entry->msi_attrib.is_64 = 1;
                entry->msi_attrib.entry_nr = j;
                entry->msi_attrib.maskbit = 1;
@@ -765,13 +590,12 @@ static int msix_capability_init(struct pci_dev *dev,
                temp = irq;
                tail = entry;
                /* Configure MSI-X capability structure */
-               status = msi_ops->setup(dev, irq, &msg);
+               status = arch_setup_msi_irq(irq, dev);
                if (status < 0) {
                        destroy_msi_irq(irq);
                        break;
                }
 
-               write_msi_msg(entry, &msg);
                attach_msi_entry(entry, irq);
        }
        if (i != nvec) {
@@ -799,22 +623,24 @@ static int msix_capability_init(struct pci_dev *dev,
  * pci_msi_supported - check whether MSI may be enabled on device
  * @dev: pointer to the pci_dev data structure of MSI device function
  *
- * MSI must be globally enabled and supported by the device and its root
- * bus. But, the root bus is not easy to find since some architectures
- * have virtual busses on top of the PCI hierarchy (for instance the
- * hypertransport bus), while the actual bus where MSI must be supported
- * is below. So we test the MSI flag on all parent busses and assume
- * that no quirk will ever set the NO_MSI flag on a non-root bus.
+ * Look at global flags, the device itself, and its parent busses
+ * to return 0 if MSI are supported for the device.
  **/
 static
 int pci_msi_supported(struct pci_dev * dev)
 {
        struct pci_bus *bus;
 
+       /* MSI must be globally enabled and supported by the device */
        if (!pci_msi_enable || !dev || dev->no_msi)
                return -EINVAL;
 
-       /* check MSI flags of all parent busses */
+       /* Any bridge which does NOT route MSI transactions from it's
+        * secondary bus to it's primary bus must set NO_MSI flag on
+        * the secondary pci_bus.
+        * We expect only arch-specific PCI host bus controller driver
+        * or quirks for specific PCI bridges to be setting NO_MSI.
+        */
        for (bus = dev->bus; bus; bus = bus->parent)
                if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
                        return -EINVAL;
@@ -835,7 +661,6 @@ int pci_msi_supported(struct pci_dev * dev)
 int pci_enable_msi(struct pci_dev* dev)
 {
        int pos, temp, status;
-       u16 control;
 
        if (pci_msi_supported(dev) < 0)
                return -EINVAL;
@@ -850,10 +675,6 @@ int pci_enable_msi(struct pci_dev* dev)
        if (!pos)
                return -EINVAL;
 
-       pci_read_config_word(dev, msi_control_reg(pos), &control);
-       if (!is_64bit_address(control) && msi_ops->needs_64bit_address)
-               return -EINVAL;
-
        WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSI));
 
        /* Check whether driver already requested for MSI-X irqs */
@@ -897,12 +718,12 @@ void pci_disable_msi(struct pci_dev* dev)
                spin_unlock_irqrestore(&msi_lock, flags);
                return;
        }
-       if (entry->msi_attrib.state) {
+       if (irq_has_action(dev->irq)) {
                spin_unlock_irqrestore(&msi_lock, flags);
                printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without "
                       "free_irq() on MSI irq %d\n",
                       pci_name(dev), dev->irq);
-               BUG_ON(entry->msi_attrib.state > 0);
+               BUG_ON(irq_has_action(dev->irq));
        } else {
                default_irq = entry->msi_attrib.default_irq;
                spin_unlock_irqrestore(&msi_lock, flags);
@@ -920,7 +741,7 @@ static int msi_free_irq(struct pci_dev* dev, int irq)
        void __iomem *base;
        unsigned long flags;
 
-       msi_ops->teardown(irq);
+       arch_teardown_msi_irq(irq);
 
        spin_lock_irqsave(&msi_lock, flags);
        entry = msi_desc[irq];
@@ -1035,17 +856,16 @@ void pci_disable_msix(struct pci_dev* dev)
 
        temp = dev->irq;
        if (!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) {
-               int state, irq, head, tail = 0, warning = 0;
+               int irq, head, tail = 0, warning = 0;
                unsigned long flags;
 
                irq = head = dev->irq;
                dev->irq = temp;                        /* Restore pin IRQ */
                while (head != tail) {
                        spin_lock_irqsave(&msi_lock, flags);
-                       state = msi_desc[irq]->msi_attrib.state;
                        tail = msi_desc[irq]->link.tail;
                        spin_unlock_irqrestore(&msi_lock, flags);
-                       if (state)
+                       if (irq_has_action(irq))
                                warning = 1;
                        else if (irq != head)   /* Release MSI-X irq */
                                msi_free_irq(dev, irq);
@@ -1072,7 +892,7 @@ void pci_disable_msix(struct pci_dev* dev)
  **/
 void msi_remove_pci_irq_vectors(struct pci_dev* dev)
 {
-       int state, pos, temp;
+       int pos, temp;
        unsigned long flags;
 
        if (!pci_msi_enable || !dev)
@@ -1081,14 +901,11 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev)
        temp = dev->irq;                /* Save IOAPIC IRQ */
        pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
        if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) {
-               spin_lock_irqsave(&msi_lock, flags);
-               state = msi_desc[dev->irq]->msi_attrib.state;
-               spin_unlock_irqrestore(&msi_lock, flags);
-               if (state) {
+               if (irq_has_action(dev->irq)) {
                        printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
                               "called without free_irq() on MSI irq %d\n",
                               pci_name(dev), dev->irq);
-                       BUG_ON(state > 0);
+                       BUG_ON(irq_has_action(dev->irq));
                } else /* Release MSI irq assigned to this device */
                        msi_free_irq(dev, dev->irq);
                dev->irq = temp;                /* Restore IOAPIC IRQ */
@@ -1101,11 +918,10 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev)
                irq = head = dev->irq;
                while (head != tail) {
                        spin_lock_irqsave(&msi_lock, flags);
-                       state = msi_desc[irq]->msi_attrib.state;
                        tail = msi_desc[irq]->link.tail;
                        base = msi_desc[irq]->mask_base;
                        spin_unlock_irqrestore(&msi_lock, flags);
-                       if (state)
+                       if (irq_has_action(irq))
                                warning = 1;
                        else if (irq != head) /* Release MSI-X irq */
                                msi_free_irq(dev, irq);