Add the callbacks to enable message-signaled interrupts for EHCI controllers. Due to the high number of broken implementations, this is done only for models that are known to work. For testing, this decision can be overridden with a module parameter. Signed-off-by: Clemens Ladisch <clemens@xxxxxxxxxx> --- drivers/usb/host/ehci-pci.c | 72 ++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 8 deletions(-) --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -22,8 +22,68 @@ #error "This file is PCI bus glue. CONFIG_PCI must be defined." #endif +static int enable_msi = -1; +module_param(enable_msi, int, 0444); +MODULE_PARM_DESC(enable_msi, + "enable message-signaled interrupts, 0=no, 1=yes, -1=auto"); + /*-------------------------------------------------------------------------*/ +static u8 get_ati_southbridge_revision(void) +{ + struct pci_dev *p_smbus; + u8 rev; + + p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, + PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + if (!p_smbus) + return 0; + 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_INTEL: + return true; + + case PCI_VENDOR_ID_ATI: + switch (dev->device) { + case 0x4396: /* ATI SB[78]00: MSI works in rev A14 or later */ + return get_ati_southbridge_revision() >= 0x3c; + } + break; + + case PCI_VENDOR_ID_AL: + switch (dev->device) { + case 0x5239: /* ULi M1575 */ + return false; + } + 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 > 0 || + (enable_msi < 0 && ehci_in_msi_whitelist(pdev))) + pci_enable_msi(pdev); + return 0; +} + +static void ehci_pci_cleanup_irq(struct usb_hcd *hcd) +{ + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + + pci_disable_msi(pdev); +} + /* called after powerup, by probe or system-pm "wakeup" */ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) { @@ -46,7 +106,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; @@ -163,12 +222,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; @@ -177,7 +231,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; } @@ -372,6 +425,9 @@ static const struct hc_driver ehci_pci_h .irq = ehci_irq, .flags = HCD_MEMORY | HCD_USB2, + .pci_prepare_irq = ehci_pci_prepare_irq, + .pci_cleanup_irq = ehci_pci_cleanup_irq, + /* * basic lifecycle operations */ -- 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