PCI: Add quirk for devices which disable MSI when INTX_DISABLE is set.

A reasonably common problem with some devices is that they will
disable MSI generation when the INTX_DISABLE bit is set in the
PCI_COMMAND register.

Quirk this explicitly, guarding the pci_intx() calls in msi.c with
this quirk indication.

The first entries for this quirk are for 5714 and 5780 Tigon3 chips,
and thus we can remove the workaround code from the tg3.c driver.

Signed-off-by: David S. Miller <davem@davemloft.net>
Acked-by: Michael Chan <mchan@broadcom.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 09440d7..cad5199 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -7365,10 +7365,6 @@
 		} else if (pci_enable_msi(tp->pdev) == 0) {
 			u32 msi_mode;
 
-			/* Hardware bug - MSI won't work if INTX disabled. */
-			if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)
-				pci_intx(tp->pdev, 1);
-
 			msi_mode = tr32(MSGINT_MODE);
 			tw32(MSGINT_MODE, msi_mode | MSGINT_MODE_ENABLE);
 			tp->tg3_flags2 |= TG3_FLG2_USING_MSI;
@@ -12681,11 +12677,6 @@
 	if (err)
 		return err;
 
-	/* Hardware bug - MSI won't work if INTX disabled. */
-	if ((tp->tg3_flags2 & TG3_FLG2_5780_CLASS) &&
-	    (tp->tg3_flags2 & TG3_FLG2_USING_MSI))
-		pci_intx(tp->pdev, 1);
-
 	netif_device_attach(dev);
 
 	tg3_full_lock(tp, 0);
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 87e0161..07c9f09 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -224,6 +224,12 @@
 	return entry;
 }
 
+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);
+}
+
 #ifdef CONFIG_PM
 static void __pci_restore_msi_state(struct pci_dev *dev)
 {
@@ -237,7 +243,7 @@
 	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)
@@ -260,7 +266,7 @@
 		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) {
@@ -343,7 +349,7 @@
 	}
 
 	/* 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;
 
@@ -433,7 +439,7 @@
 		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;
 
@@ -528,7 +534,7 @@
 		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));
@@ -640,7 +646,7 @@
 		return;
 
 	msix_set_enable(dev, 0);
-	pci_intx(dev, 1);		/* enable intx */
+	pci_intx_for_msi(dev, 1);
 	dev->msix_enabled = 0;
 
 	msix_free_all_irqs(dev);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index f975f7f..9e8c7af 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1707,4 +1707,28 @@
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
 			quirk_nvidia_ck804_msi_ht_cap);
+
+static void __devinit quirk_msi_intx_disable_bug(struct pci_dev *dev)
+{
+	dev->dev_flags |= PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5780,
+			quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5780S,
+			quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5714,
+			quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5714S,
+			quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5715,
+			quirk_msi_intx_disable_bug);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM,
+			PCI_DEVICE_ID_TIGON3_5715S,
+			quirk_msi_intx_disable_bug);
+
 #endif /* CONFIG_PCI_MSI */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 5d2281f..7c04f38 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -109,6 +109,14 @@
 	pcie_hot_reset = (__force pcie_reset_state_t) 3
 };
 
+typedef unsigned short __bitwise pci_dev_flags_t;
+enum pci_dev_flags {
+	/* INTX_DISABLE in PCI_COMMAND register disables MSI
+	 * generation too.
+	 */
+	PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
+};
+
 typedef unsigned short __bitwise pci_bus_flags_t;
 enum pci_bus_flags {
 	PCI_BUS_FLAGS_NO_MSI   = (__force pci_bus_flags_t) 1,
@@ -185,6 +193,7 @@
 	unsigned int	msix_enabled:1;
 	unsigned int	is_managed:1;
 	unsigned int	is_pcie:1;
+	pci_dev_flags_t dev_flags;
 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
 
 	u32		saved_config_space[16]; /* config space saved at suspend time */