commit
b8cb91e058cd0c0f02059c1207293c5b31d350fa upstream.
The xhci in Intel Sunrisepoint and Cherryview platforms need a driver
workaround for a Stuck PME that might either block PME events in suspend,
or create spurious PME events preventing runtime suspend.
Workaround is to clear a internal PME flag, BIT(28) in a vendor specific
PMCTRL register at offset 0x80a4, in both suspend resume callbacks
Without this, xhci connected usb devices might never be able to wake up the
system from suspend, or prevent device from going to suspend (xhci d3)
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#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
static const char hcd_name[] = "xhci_hcd";
static const char hcd_name[] = "xhci_hcd";
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
xhci->quirks |= XHCI_AVOID_BEI;
}
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
xhci->quirks |= XHCI_AVOID_BEI;
}
+ 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)) {
+ 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;
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_RESET_ON_RESUME;
}
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 */
static int xhci_pci_setup(struct usb_hcd *hcd)
{
/* called during probe() after chip reset completes */
static int xhci_pci_setup(struct usb_hcd *hcd)
{
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+ xhci_pme_quirk(xhci);
+
retval = xhci_suspend(xhci, do_wakeup);
return retval;
retval = xhci_suspend(xhci, do_wakeup);
return retval;
if (usb_is_intel_switchable_xhci(pdev))
usb_enable_xhci_ports(pdev);
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;
}
retval = xhci_resume(xhci, hibernated);
return retval;
}
#define XHCI_AVOID_BEI (1 << 15)
#define XHCI_SLOW_SUSPEND (1 << 17)
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
#define XHCI_AVOID_BEI (1 << 15)
#define XHCI_SLOW_SUSPEND (1 << 17)
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
+#define XHCI_PME_STUCK_QUIRK (1 << 20)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */