xhci: don't dereference a xhci member after removing xhci
[pandora-kernel.git] / drivers / usb / host / xhci-pci.c
index ef98b38..cc6aa66 100644 (file)
 /* Device for a quirk */
 #define PCI_VENDOR_ID_FRESCO_LOGIC     0x1b73
 #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
+#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400      0x1400
 
 #define PCI_VENDOR_ID_ETRON            0x1b6f
 #define PCI_DEVICE_ID_ASROCK_P67       0x7023
 
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI     0x8c31
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
+#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI            0x22b5
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI                0xa12f
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI       0x9d2f
+#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI             0x0aa8
+#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI             0x1aa8
+
 static const char hcd_name[] = "xhci_hcd";
 
 /* called after powerup, by probe or system-pm "wakeup" */
@@ -58,12 +67,22 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 
        /* Look for vendor-specific quirks */
        if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
-                       pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK) {
-               if (pdev->revision == 0x0) {
+                       (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK ||
+                        pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1400)) {
+               if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
+                               pdev->revision == 0x0) {
                        xhci->quirks |= XHCI_RESET_EP_QUIRK;
                        xhci_dbg(xhci, "QUIRK: Fresco Logic xHC needs configure"
                                        " endpoint cmd after reset endpoint\n");
                }
+               if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
+                               pdev->revision == 0x4) {
+                       xhci->quirks |= XHCI_SLOW_SUSPEND;
+                       xhci_dbg(xhci,
+                               "QUIRK: Fresco Logic xHC revision %u"
+                               "must be suspended extra slowly",
+                               pdev->revision);
+               }
                /* Fresco Logic confirms: all revisions of this chip do not
                 * support MSI, even though some of them claim to in their PCI
                 * capabilities.
@@ -72,6 +91,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u "
                                "has broken MSI implementation\n",
                                pdev->revision);
+               xhci->quirks |= XHCI_TRUST_TX_LENGTH;
        }
 
        if (pdev->vendor == PCI_VENDOR_ID_NEC)
@@ -83,18 +103,65 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
        /* AMD PLL quirk */
        if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
                xhci->quirks |= XHCI_AMD_PLL_FIX;
+
+       if (pdev->vendor == PCI_VENDOR_ID_AMD)
+               xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+               xhci->quirks |= XHCI_INTEL_HOST;
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+               xhci->quirks |= XHCI_AVOID_BEI;
        if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
                        pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
-               xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
                xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
                xhci->limit_active_eps = 64;
                xhci->quirks |= XHCI_SW_BW_CHECKING;
+               /*
+                * PPT desktop boards DH77EB and DH77DF will power back on after
+                * a few seconds of being shutdown.  The fix for this is to
+                * switch the ports from xHCI to EHCI on shutdown.  We can't use
+                * DMI information to find those particular boards (since each
+                * vendor will change the board name), so we have to key off all
+                * PPT chipsets.
+                */
+               xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+       }
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+               pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
+               xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+               xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
+       }
+       if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+               (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
+                pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
+                pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+                pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
+                pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
+               xhci->quirks |= XHCI_PME_STUCK_QUIRK;
        }
        if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
                        pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
                xhci->quirks |= XHCI_RESET_ON_RESUME;
                xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
+               xhci->quirks |= XHCI_TRUST_TX_LENGTH;
        }
+       if (pdev->vendor == PCI_VENDOR_ID_VIA)
+               xhci->quirks |= XHCI_RESET_ON_RESUME;
+}
+
+/*
+ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear
+ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
+ */
+static void xhci_pme_quirk(struct xhci_hcd *xhci)
+{
+       u32 val;
+       void __iomem *reg;
+
+       reg = (void __iomem *) xhci->cap_regs + 0x80a4;
+       val = readl(reg);
+       writel(val | BIT(28), reg);
+       readl(reg);
 }
 
 /* called during probe() after chip reset completes */
@@ -185,7 +252,13 @@ static void xhci_pci_remove(struct pci_dev *dev)
                usb_remove_hcd(xhci->shared_hcd);
                usb_put_hcd(xhci->shared_hcd);
        }
+
+       /* Workaround for spurious wakeups at shutdown with HSW */
+       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
+               pci_set_power_state(dev, PCI_D3hot);
+
        usb_hcd_pci_remove(dev);
+
        kfree(xhci);
 }
 
@@ -199,7 +272,10 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
                        xhci->shared_hcd->state != HC_STATE_SUSPENDED)
                return -EINVAL;
 
-       retval = xhci_suspend(xhci);
+       if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+               xhci_pme_quirk(xhci);
+
+       retval = xhci_suspend(xhci, do_wakeup);
 
        return retval;
 }
@@ -229,6 +305,9 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
        if (usb_is_intel_switchable_xhci(pdev))
                usb_enable_xhci_ports(pdev);
 
+       if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+               xhci_pme_quirk(xhci);
+
        retval = xhci_resume(xhci, hibernated);
        return retval;
 }
@@ -314,7 +393,7 @@ static struct pci_driver xhci_pci_driver = {
        /* suspend and resume implemented later */
 
        .shutdown =     usb_hcd_pci_shutdown,
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        .driver = {
                .pm = &usb_hcd_pci_pm_ops
        },