Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / drivers / pci / msi.c
index e6740d1..1835481 100644 (file)
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/msi.h>
+#include <linux/smp.h>
 
 #include <asm/errno.h>
 #include <asm/io.h>
-#include <asm/smp.h>
 
 #include "pci.h"
 #include "msi.h"
 
 static int pci_msi_enable = 1;
 
-static void msi_set_enable(struct pci_dev *dev, int enable)
+/* Arch hooks */
+
+int __attribute__ ((weak))
+arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
+{
+       return 0;
+}
+
+int __attribute__ ((weak))
+arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry)
+{
+       return 0;
+}
+
+int __attribute__ ((weak))
+arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+       struct msi_desc *entry;
+       int ret;
+
+       list_for_each_entry(entry, &dev->msi_list, list) {
+               ret = arch_setup_msi_irq(dev, entry);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq)
+{
+       return;
+}
+
+void __attribute__ ((weak))
+arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+       struct msi_desc *entry;
+
+       list_for_each_entry(entry, &dev->msi_list, list) {
+               if (entry->irq != 0)
+                       arch_teardown_msi_irq(entry->irq);
+       }
+}
+
+static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
 {
-       int pos;
        u16 control;
 
-       pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
        if (pos) {
                pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
                control &= ~PCI_MSI_FLAGS_ENABLE;
@@ -40,6 +83,11 @@ static void msi_set_enable(struct pci_dev *dev, int enable)
        }
 }
 
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+       __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
+}
+
 static void msix_set_enable(struct pci_dev *dev, int enable)
 {
        int pos;
@@ -78,7 +126,16 @@ static void msix_flush_writes(unsigned int irq)
        }
 }
 
-static void msi_set_mask_bit(unsigned int irq, int flag)
+/*
+ * PCI 2.3 does not specify mask bits for each MSI interrupt.  Attempting to
+ * mask all MSI interrupts by clearing the MSI enable bit does not work
+ * reliably as devices without an INTx disable bit will then generate a
+ * level IRQ which will never be cleared.
+ *
+ * Returns 1 if it succeeded in masking the interrupt and 0 if the device
+ * doesn't support MSI masking.
+ */
+static int msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
 {
        struct msi_desc *entry;
 
@@ -92,11 +149,11 @@ static void msi_set_mask_bit(unsigned int irq, int flag)
 
                        pos = (long)entry->mask_base;
                        pci_read_config_dword(entry->dev, pos, &mask_bits);
-                       mask_bits &= ~(1);
-                       mask_bits |= flag;
+                       mask_bits &= ~(mask);
+                       mask_bits |= flag & mask;
                        pci_write_config_dword(entry->dev, pos, mask_bits);
                } else {
-                       msi_set_enable(entry->dev, !flag);
+                       return 0;
                }
                break;
        case PCI_CAP_ID_MSIX:
@@ -112,6 +169,7 @@ static void msi_set_mask_bit(unsigned int irq, int flag)
                break;
        }
        entry->msi_attrib.masked = !!flag;
+       return 1;
 }
 
 void read_msi_msg(unsigned int irq, struct msi_msg *msg)
@@ -132,7 +190,7 @@ void read_msi_msg(unsigned int irq, struct msi_msg *msg)
                        pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
                } else {
                        msg->address_hi = 0;
-                       pci_read_config_word(dev, msi_data_reg(pos, 1), &data);
+                       pci_read_config_word(dev, msi_data_reg(pos, 0), &data);
                }
                msg->data = data;
                break;
@@ -196,13 +254,13 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
 
 void mask_msi_irq(unsigned int irq)
 {
-       msi_set_mask_bit(irq, 1);
+       msi_set_mask_bits(irq, 1, 1);
        msix_flush_writes(irq);
 }
 
 void unmask_msi_irq(unsigned int irq)
 {
-       msi_set_mask_bit(irq, 0);
+       msi_set_mask_bits(irq, 1, 0);
        msix_flush_writes(irq);
 }
 
@@ -224,7 +282,12 @@ static struct msi_desc* alloc_msi_entry(void)
        return entry;
 }
 
-#ifdef CONFIG_PM
+static void pci_intx_for_msi(struct pci_dev *dev, int enable)
+{
+       if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG))
+               pci_intx(dev, enable);
+}
+
 static void __pci_restore_msi_state(struct pci_dev *dev)
 {
        int pos;
@@ -237,11 +300,12 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
        entry = get_irq_msi(dev->irq);
        pos = entry->msi_attrib.pos;
 
-       pci_intx(dev, 0);               /* disable intx */
+       pci_intx_for_msi(dev, 0);
        msi_set_enable(dev, 0);
        write_msi_msg(dev->irq, &entry->msg);
        if (entry->msi_attrib.maskbit)
-               msi_set_mask_bit(dev->irq, entry->msi_attrib.masked);
+               msi_set_mask_bits(dev->irq, entry->msi_attrib.maskbits_mask,
+                                 entry->msi_attrib.masked);
 
        pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
        control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
@@ -260,12 +324,12 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
                return;
 
        /* route the table */
-       pci_intx(dev, 0);               /* disable intx */
+       pci_intx_for_msi(dev, 0);
        msix_set_enable(dev, 0);
 
        list_for_each_entry(entry, &dev->msi_list, list) {
                write_msi_msg(entry->irq, &entry->msg);
-               msi_set_mask_bit(entry->irq, entry->msi_attrib.masked);
+               msi_set_mask_bits(entry->irq, 1, entry->msi_attrib.masked);
        }
 
        BUG_ON(list_empty(&dev->msi_list));
@@ -282,7 +346,7 @@ void pci_restore_msi_state(struct pci_dev *dev)
        __pci_restore_msi_state(dev);
        __pci_restore_msix_state(dev);
 }
-#endif /* CONFIG_PM */
+EXPORT_SYMBOL_GPL(pci_restore_msi_state);
 
 /**
  * msi_capability_init - configure device's MSI capability structure
@@ -332,8 +396,9 @@ static int msi_capability_init(struct pci_dev *dev)
                pci_write_config_dword(dev,
                        msi_mask_bits_reg(pos, is_64bit_address(control)),
                        maskbits);
+               entry->msi_attrib.maskbits_mask = temp;
        }
-       list_add(&entry->list, &dev->msi_list);
+       list_add_tail(&entry->list, &dev->msi_list);
 
        /* Configure MSI capability structure */
        ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
@@ -343,7 +408,7 @@ static int msi_capability_init(struct pci_dev *dev)
        }
 
        /* Set MSI enabled bits  */
-       pci_intx(dev, 0);               /* disable intx */
+       pci_intx_for_msi(dev, 0);
        msi_set_enable(dev, 1);
        dev->msi_enabled = 1;
 
@@ -404,7 +469,7 @@ static int msix_capability_init(struct pci_dev *dev,
                entry->dev = dev;
                entry->mask_base = base;
 
-               list_add(&entry->list, &dev->msi_list);
+               list_add_tail(&entry->list, &dev->msi_list);
        }
 
        ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
@@ -433,7 +498,7 @@ static int msix_capability_init(struct pci_dev *dev,
                i++;
        }
        /* Set MSI-X enabled bits */
-       pci_intx(dev, 0);               /* disable intx */
+       pci_intx_for_msi(dev, 0);
        msix_set_enable(dev, 1);
        dev->msix_enabled = 1;
 
@@ -509,9 +574,8 @@ int pci_enable_msi(struct pci_dev* dev)
 
        /* Check whether driver already requested for MSI-X irqs */
        if (dev->msix_enabled) {
-               printk(KERN_INFO "PCI: %s: Can't enable MSI.  "
-                       "Device already has MSI-X enabled\n",
-                       pci_name(dev));
+               dev_info(&dev->dev, "can't enable MSI "
+                        "(MSI-X already enabled)\n");
                return -EINVAL;
        }
        status = msi_capability_init(dev);
@@ -519,29 +583,44 @@ int pci_enable_msi(struct pci_dev* dev)
 }
 EXPORT_SYMBOL(pci_enable_msi);
 
-void pci_disable_msi(struct pci_dev* dev)
+void pci_msi_shutdown(struct pci_dev* dev)
 {
        struct msi_desc *entry;
-       int default_irq;
 
        if (!pci_msi_enable || !dev || !dev->msi_enabled)
                return;
 
        msi_set_enable(dev, 0);
-       pci_intx(dev, 1);               /* enable intx */
+       pci_intx_for_msi(dev, 1);
        dev->msi_enabled = 0;
 
        BUG_ON(list_empty(&dev->msi_list));
        entry = list_entry(dev->msi_list.next, struct msi_desc, list);
-       if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) {
-               return;
+       /* Return the the pci reset with msi irqs unmasked */
+       if (entry->msi_attrib.maskbit) {
+               u32 mask = entry->msi_attrib.maskbits_mask;
+               msi_set_mask_bits(dev->irq, mask, ~mask);
        }
-
-       default_irq = entry->msi_attrib.default_irq;
-       msi_free_irqs(dev);
+       if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
+               return;
 
        /* Restore dev->irq to its default pin-assertion irq */
-       dev->irq = default_irq;
+       dev->irq = entry->msi_attrib.default_irq;
+}
+void pci_disable_msi(struct pci_dev* dev)
+{
+       struct msi_desc *entry;
+
+       if (!pci_msi_enable || !dev || !dev->msi_enabled)
+               return;
+
+       pci_msi_shutdown(dev);
+
+       entry = list_entry(dev->msi_list.next, struct msi_desc, list);
+       if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI)
+               return;
+
+       msi_free_irqs(dev);
 }
 EXPORT_SYMBOL(pci_disable_msi);
 
@@ -549,19 +628,21 @@ static int msi_free_irqs(struct pci_dev* dev)
 {
        struct msi_desc *entry, *tmp;
 
-       list_for_each_entry(entry, &dev->msi_list, list)
-               BUG_ON(irq_has_action(entry->irq));
+       list_for_each_entry(entry, &dev->msi_list, list) {
+               if (entry->irq)
+                       BUG_ON(irq_has_action(entry->irq));
+       }
 
        arch_teardown_msi_irqs(dev);
 
        list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) {
                if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) {
-                       if (list_is_last(&entry->list, &dev->msi_list))
-                               iounmap(entry->mask_base);
-
                        writel(1, entry->mask_base + entry->msi_attrib.entry_nr
                                  * PCI_MSIX_ENTRY_SIZE
                                  + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+
+                       if (list_is_last(&entry->list, &dev->msi_list))
+                               iounmap(entry->mask_base);
                }
                list_del(&entry->list);
                kfree(entry);
@@ -617,9 +698,8 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
 
        /* Check whether driver already requested for MSI irq */
        if (dev->msi_enabled) {
-               printk(KERN_INFO "PCI: %s: Can't enable MSI-X.  "
-                      "Device already has an MSI irq assigned\n",
-                      pci_name(dev));
+               dev_info(&dev->dev, "can't enable MSI-X "
+                      "(MSI IRQ already assigned)\n");
                return -EINVAL;
        }
        status = msix_capability_init(dev, entries, nvec);
@@ -632,14 +712,21 @@ static void msix_free_all_irqs(struct pci_dev *dev)
        msi_free_irqs(dev);
 }
 
-void pci_disable_msix(struct pci_dev* dev)
+void pci_msix_shutdown(struct pci_dev* dev)
 {
        if (!pci_msi_enable || !dev || !dev->msix_enabled)
                return;
 
        msix_set_enable(dev, 0);
-       pci_intx(dev, 1);               /* enable intx */
+       pci_intx_for_msi(dev, 1);
        dev->msix_enabled = 0;
+}
+void pci_disable_msix(struct pci_dev* dev)
+{
+       if (!pci_msi_enable || !dev || !dev->msix_enabled)
+               return;
+
+       pci_msix_shutdown(dev);
 
        msix_free_all_irqs(dev);
 }
@@ -675,49 +762,3 @@ void pci_msi_init_pci_dev(struct pci_dev *dev)
 {
        INIT_LIST_HEAD(&dev->msi_list);
 }
-
-
-/* Arch hooks */
-
-int __attribute__ ((weak))
-arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
-{
-       return 0;
-}
-
-int __attribute__ ((weak))
-arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry)
-{
-       return 0;
-}
-
-int __attribute__ ((weak))
-arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
-{
-       struct msi_desc *entry;
-       int ret;
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               ret = arch_setup_msi_irq(dev, entry);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq)
-{
-       return;
-}
-
-void __attribute__ ((weak))
-arch_teardown_msi_irqs(struct pci_dev *dev)
-{
-       struct msi_desc *entry;
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               if (entry->irq != 0)
-                       arch_teardown_msi_irq(entry->irq);
-       }
-}