OHCI: add a quirk for ULi M5237 blocking on reset
[pandora-kernel.git] / drivers / usb / host / pci-quirks.c
index 301f67f..d5cb148 100644 (file)
@@ -470,7 +470,8 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
 {
        void __iomem *base;
        u32 control;
-       u32 fminterval;
+       u32 fminterval = 0;
+       bool no_fminterval = false;
        int cnt;
 
        if (!mmio_resource_enabled(pdev, 0))
@@ -480,6 +481,13 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
        if (base == NULL)
                return;
 
+       /*
+        * ULi M5237 OHCI controller locks the whole system when accessing
+        * the OHCI_FMINTERVAL offset.
+        */
+       if (pdev->vendor == PCI_VENDOR_ID_AL && pdev->device == 0x5237)
+               no_fminterval = true;
+
        control = readl(base + OHCI_CONTROL);
 
 /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -518,7 +526,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
        }
 
        /* software reset of the controller, preserving HcFmInterval */
-       fminterval = readl(base + OHCI_FMINTERVAL);
+       if (!no_fminterval)
+               fminterval = readl(base + OHCI_FMINTERVAL);
+
        writel(OHCI_HCR, base + OHCI_CMDSTATUS);
 
        /* reset requires max 10 us delay */
@@ -527,7 +537,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
                        break;
                udelay(1);
        }
-       writel(fminterval, base + OHCI_FMINTERVAL);
+
+       if (!no_fminterval)
+               writel(fminterval, base + OHCI_FMINTERVAL);
 
        /* Now the controller is safely in SUSPEND and nothing can wake it up */
        iounmap(base);
@@ -545,7 +557,22 @@ static const struct dmi_system_id __devinitconst ehci_dmi_nohandoff_table[] = {
                /*  Pegatron Lucid (Ordissimo AIRIS) */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "M11JB"),
-                       DMI_MATCH(DMI_BIOS_VERSION, "Lucid-GE-133"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
+               },
+       },
+       {
+               /*  Pegatron Lucid (Ordissimo) */
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "Ordissimo"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
+               },
+       },
+       {
+               /* HASEE E200 */
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "HASEE"),
+                       DMI_MATCH(DMI_BOARD_NAME, "E210"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "6.00"),
                },
        },
        { }
@@ -557,9 +584,14 @@ static void __devinit ehci_bios_handoff(struct pci_dev *pdev,
 {
        int try_handoff = 1, tried_handoff = 0;
 
-       /* The Pegatron Lucid tablet sporadically waits for 98 seconds trying
-        * the handoff on its unused controller.  Skip it. */
-       if (pdev->vendor == 0x8086 && pdev->device == 0x283a) {
+       /*
+        * The Pegatron Lucid tablet sporadically waits for 98 seconds trying
+        * the handoff on its unused controller.  Skip it.
+        *
+        * The HASEE E200 hangs when the semaphore is set (bugzilla #77021).
+        */
+       if (pdev->vendor == 0x8086 && (pdev->device == 0x283a ||
+                       pdev->device == 0x27cc)) {
                if (dmi_check_system(ehci_dmi_nohandoff_table))
                        try_handoff = 0;
        }
@@ -716,6 +748,7 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,
 }
 
 #define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI    0x8C31
+#define PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI 0x9C31
 
 bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev)
 {
@@ -729,7 +762,8 @@ bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev)
 {
        return pdev->class == PCI_CLASS_SERIAL_USB_XHCI &&
                pdev->vendor == PCI_VENDOR_ID_INTEL &&
-               pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI;
+               (pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI ||
+                pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI);
 }
 
 bool usb_is_intel_switchable_xhci(struct pci_dev *pdev)
@@ -771,6 +805,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
                                "defaulting to EHCI.\n");
                dev_warn(&xhci_pdev->dev,
                                "USB 3.0 devices will work at USB 2.0 speeds.\n");
+               usb_disable_xhci_ports(xhci_pdev);
                return;
        }
 
@@ -819,6 +854,13 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
 }
 EXPORT_SYMBOL_GPL(usb_enable_xhci_ports);
 
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+{
+       pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, 0x0);
+       pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, 0x0);
+}
+EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+
 /**
  * PCI Quirks for xHCI.
  *
@@ -834,12 +876,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
        void __iomem *op_reg_base;
        u32 val;
        int timeout;
+       int len = pci_resource_len(pdev, 0);
 
        if (!mmio_resource_enabled(pdev, 0))
                return;
 
-       base = ioremap_nocache(pci_resource_start(pdev, 0),
-                               pci_resource_len(pdev, 0));
+       base = ioremap_nocache(pci_resource_start(pdev, 0), len);
        if (base == NULL)
                return;
 
@@ -849,9 +891,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
         */
        ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
        do {
+               if ((ext_cap_offset + sizeof(val)) > len) {
+                       /* We're reading garbage from the controller */
+                       dev_warn(&pdev->dev,
+                                "xHCI controller failing to respond");
+                       return;
+               }
+
                if (!ext_cap_offset)
                        /* We've reached the end of the extended capabilities */
                        goto hc_init;
+
                val = readl(base + ext_cap_offset);
                if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
                        break;