| From b8cb91e058cd0c0f02059c1207293c5b31d350fa Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Fri, 6 Mar 2015 17:23:19 +0200 |
| Subject: xhci: Workaround for PME stuck issues in Intel xhci |
| |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| |
| 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> |
| |
| --- |
| drivers/usb/host/xhci-pci.c | 30 ++++++++++++++++++++++++++++++ |
| drivers/usb/host/xhci.h | 1 + |
| 2 files changed, 31 insertions(+) |
| |
| --- a/drivers/usb/host/xhci-pci.c |
| +++ b/drivers/usb/host/xhci-pci.c |
| @@ -37,6 +37,9 @@ |
| |
| #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"; |
| |
| @@ -133,6 +136,12 @@ static void xhci_pci_quirks(struct devic |
| pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { |
| xhci->quirks |= XHCI_SPURIOUS_REBOOT; |
| } |
| + 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_EJ168) { |
| xhci->quirks |= XHCI_RESET_ON_RESUME; |
| @@ -159,6 +168,21 @@ static void xhci_pci_quirks(struct devic |
| "QUIRK: Resetting 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) |
| { |
| @@ -283,6 +307,9 @@ static int xhci_pci_suspend(struct usb_h |
| if (xhci->quirks & XHCI_COMP_MODE_QUIRK) |
| pdev->no_d3cold = true; |
| |
| + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) |
| + xhci_pme_quirk(xhci); |
| + |
| return xhci_suspend(xhci, do_wakeup); |
| } |
| |
| @@ -313,6 +340,9 @@ static int xhci_pci_resume(struct usb_hc |
| if (pdev->vendor == PCI_VENDOR_ID_INTEL) |
| usb_enable_intel_xhci_ports(pdev); |
| |
| + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) |
| + xhci_pme_quirk(xhci); |
| + |
| retval = xhci_resume(xhci, hibernated); |
| return retval; |
| } |
| --- a/drivers/usb/host/xhci.h |
| +++ b/drivers/usb/host/xhci.h |
| @@ -1564,6 +1564,7 @@ struct xhci_hcd { |
| #define XHCI_SPURIOUS_WAKEUP (1 << 18) |
| /* For controllers with a broken beyond repair streams implementation */ |
| #define XHCI_BROKEN_STREAMS (1 << 19) |
| +#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 */ |