[PATCH 4/4] USB: OHCI/EHCI: automatically enable MSI on AMD SB700/SB800

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add whitelists so that the OHCI/EHCI PCI drivers can automatically
enable MSI for controllers where this is known to work.

At the moment, the only device I've tested is the rev A14 of the ATI
SB700/SB800 series southbridge.  The BIOS typically disables the MSI
capabilities of these controllers, so this patch will not make much
of a difference in practice.

Signed-off-by: Clemens Ladisch <clemens@xxxxxxxxxx>
---
 drivers/usb/host/ehci-pci.c |   45 +++++++++++++++++++++++++++---------
 drivers/usb/host/ohci-pci.c |   34 ++++++++++++++++++++++++---
 2 files changed, 65 insertions(+), 14 deletions(-)


BTW, does anybody happen to know the PCI IDs of the Hudson USB controllers?


--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -22,18 +22,48 @@
 #error "This file is PCI bus glue.  CONFIG_PCI must be defined."
 #endif
 
-static int enable_msi;
+static int enable_msi = -1;
 module_param(enable_msi, int, 0444);
 MODULE_PARM_DESC(enable_msi,
-		 "enable message-signaled interrupts, 0=no, 1=yes");
+		 "enable message-signaled interrupts, 0=no, 1=yes, -1=auto");
 
 /*-------------------------------------------------------------------------*/
 
+static u8 get_ati_southbridge_revision(void)
+{
+	struct pci_dev	*p_smbus;
+	u8		rev = 0;
+
+	p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
+				 PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+	if (p_smbus) {
+		rev = p_smbus->revision;
+		pci_dev_put(p_smbus);
+	}
+	return rev;
+}
+
+static bool ehci_in_msi_whitelist(struct pci_dev *dev)
+{
+	switch (dev->vendor) {
+	case PCI_VENDOR_ID_ATI:
+		switch (dev->device) {
+		case 0x4396:
+			/* AMD SB700/SB800: MSI works in rev A14 or later */
+			return get_ati_southbridge_revision() >= 0x3c;
+		}
+		break;
+	}
+
+	return false;
+}
+
 static int ehci_pci_prepare_irq(struct usb_hcd *hcd)
 {
 	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
 
-	if (enable_msi)
+	if (enable_msi > 0 ||
+	    (enable_msi < 0 && ehci_in_msi_whitelist(pdev)))
 		pci_enable_msi(pdev);
 	return 0;
 }
@@ -67,7 +97,6 @@ static int ehci_pci_setup(struct usb_hcd
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);
-	struct pci_dev		*p_smbus;
 	u8			rev;
 	u32			temp;
 	int			retval;
@@ -190,12 +219,7 @@ static int ehci_pci_setup(struct usb_hcd
 		 * which causes usb devices lose response in some cases.
 		 */
 		if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) {
-			p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
-						 PCI_DEVICE_ID_ATI_SBX00_SMBUS,
-						 NULL);
-			if (!p_smbus)
-				break;
-			rev = p_smbus->revision;
+			rev = get_ati_southbridge_revision();
 			if ((pdev->device == 0x4386) || (rev == 0x3a)
 			    || (rev == 0x3b)) {
 				u8 tmp;
@@ -204,7 +228,6 @@ static int ehci_pci_setup(struct usb_hcd
 				pci_read_config_byte(pdev, 0x53, &tmp);
 				pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
 			}
-			pci_dev_put(p_smbus);
 		}
 		break;
 	}
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -22,10 +22,10 @@
 #include <linux/io.h>
 
 
-static int enable_msi;
+static int enable_msi = -1;
 module_param(enable_msi, int, 0444);
 MODULE_PARM_DESC(enable_msi,
-		 "enable message-signaled interrupts, 0=no, 1=yes");
+		 "enable message-signaled interrupts, 0=no, 1=yes, -1=auto");
 
 /* constants used to work around PM-related transfer
  * glitches in some AMD 700 series southbridges
@@ -363,11 +363,39 @@ static const struct pci_device_id ohci_p
 	{},
 };
 
+static bool ohci_in_msi_whitelist(struct pci_dev *dev)
+{
+	struct pci_dev *smbus_dev;
+	u8 rev;
+
+	switch (dev->vendor) {
+	case PCI_VENDOR_ID_ATI:
+		switch (dev->device) {
+		case 0x4397:
+		case 0x4398:
+		case 0x4399:
+			/* AMD SB700/SB800: MSI works in rev A14 or later */
+			smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+					PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+			if (smbus_dev) {
+				rev = smbus_dev->revision;
+				pci_dev_put(smbus_dev);
+				return rev >= 0x3c;
+			}
+			break;
+		}
+		break;
+	}
+
+	return false;
+}
+
 static int ohci_pci_prepare_irq(struct usb_hcd *hcd)
 {
 	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
 
-	if (enable_msi)
+	if (enable_msi > 0 ||
+	    (enable_msi < 0 && ohci_in_msi_whitelist(pdev)))
 		pci_enable_msi(pdev);
 	return 0;
 }
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux