[PATCH] PCI: Provide a boot parameter to disable MSI

Several drivers are starting to grow options to disable MSI.  However,
it's often a host chipset issue, not something which individual drivers
should handle.  So we add the pci=nomsi kernel parameter to allow the user
to disable MSI modes for systems we haven't added to the quirk list yet.

Signed-off-by: Matthew Wilcox <matthew@wil.cx>
Signed-off-by: Randy Dunlap <rdunlap@xenotime.net>
Acked-by: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 7b7382d..44a25f3 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -49,6 +49,7 @@
 	MCA	MCA bus support is enabled.
 	MDA	MDA console support is enabled.
 	MOUSE	Appropriate mouse support is enabled.
+	MSI	Message Signaled Interrupts (PCI).
 	MTD	MTD support is enabled.
 	NET	Appropriate network support is enabled.
 	NUMA	NUMA support is enabled.
@@ -1152,6 +1153,9 @@
 				Mechanism 2.
 		nommconf	[IA-32,X86_64] Disable use of MMCONFIG for PCI
 				Configuration
+		nomsi		[MSI] If the PCI_MSI kernel config parameter is
+				enabled, this kernel boot option can be used to
+				disable the use of MSI interrupts system-wide.
 		nosort		[IA-32] Don't sort PCI devices according to
 				order given by the PCI BIOS. This sorting is
 				done to get a device order compatible with
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index d3dcce8..4d762fc 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -11,6 +11,10 @@
 	   generate an interrupt using an inbound Memory Write on its
 	   PCI bus instead of asserting a device IRQ pin.
 
+	   Use of PCI MSI interrupts can be disabled at kernel boot time
+	   by using the 'pci=nomsi' option.  This disables MSI for the
+	   entire system.
+
 	   If you don't know what to do here, say N.
 
 config PCI_DEBUG
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index aea8b25..a77e79c 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -765,8 +765,11 @@
 	u16 control;
 	unsigned long flags;
 
+	if (!pci_msi_enable)
+		return;
 	if (!dev)
 		return;
+
 	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
 	if (!pos)
 		return;
@@ -1026,6 +1029,8 @@
 	int pos, temp;
 	u16 control;
 
+	if (!pci_msi_enable)
+		return;
 	if (!dev)
 		return;
 
@@ -1152,6 +1157,11 @@
 	}
 }
 
+void pci_no_msi(void)
+{
+	pci_msi_enable = 0;
+}
+
 EXPORT_SYMBOL(pci_enable_msi);
 EXPORT_SYMBOL(pci_disable_msi);
 EXPORT_SYMBOL(pci_enable_msix);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 0bf6d25..03af232 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -900,8 +900,12 @@
 		if (k)
 			*k++ = 0;
 		if (*str && (str = pcibios_setup(str)) && *str) {
-			/* PCI layer options should be handled here */
-			printk(KERN_ERR "PCI: Unknown option `%s'\n", str);
+			if (!strcmp(str, "nomsi")) {
+				pci_no_msi();
+			} else {
+				printk(KERN_ERR "PCI: Unknown option `%s'\n",
+						str);
+			}
 		}
 		str = k;
 	}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index a6dfee2..8f3fb47 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -50,8 +50,10 @@
 
 #ifdef CONFIG_PCI_MSI
 void disable_msi_mode(struct pci_dev *dev, int pos, int type);
+void pci_no_msi(void);
 #else
 static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { }
+static inline void pci_no_msi(void) { }
 #endif
 
 extern int pcie_mch_quirk;