diff options
Diffstat (limited to 'drivers/usb/host/xhci-pci.c')
| -rw-r--r-- | drivers/usb/host/xhci-pci.c | 57 | 
1 files changed, 54 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 4a4cb1d91ac8..5590eac2b22d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -23,10 +23,15 @@  #include <linux/pci.h>  #include <linux/slab.h>  #include <linux/module.h> +#include <linux/acpi.h>  #include "xhci.h"  #include "xhci-trace.h" +#define PORT2_SSIC_CONFIG_REG2	0x883c +#define PROG_DONE		(1 << 30) +#define SSIC_PORT_UNUSED	(1 << 31) +  /* Device for a quirk */  #define PCI_VENDOR_ID_FRESCO_LOGIC	0x1b73  #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK	0x1000 @@ -176,20 +181,63 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)  }  /* + * In some Intel xHCI controllers, in order to get D3 working, + * through a vendor specific SSIC CONFIG register at offset 0x883c, + * SSIC PORT need to be marked as "unused" before putting xHCI + * into D3. After D3 exit, the SSIC port need to be marked as "used". + * Without this change, xHCI might not enter D3 state.   * 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) +static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend)  { +	struct xhci_hcd	*xhci = hcd_to_xhci(hcd); +	struct pci_dev		*pdev = to_pci_dev(hcd->self.controller);  	u32 val;  	void __iomem *reg; +	if (pdev->vendor == PCI_VENDOR_ID_INTEL && +		 pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { + +		reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; + +		/* Notify SSIC that SSIC profile programming is not done */ +		val = readl(reg) & ~PROG_DONE; +		writel(val, reg); + +		/* Mark SSIC port as unused(suspend) or used(resume) */ +		val = readl(reg); +		if (suspend) +			val |= SSIC_PORT_UNUSED; +		else +			val &= ~SSIC_PORT_UNUSED; +		writel(val, reg); + +		/* Notify SSIC that SSIC profile programming is done */ +		val = readl(reg) | PROG_DONE; +		writel(val, reg); +		readl(reg); +	} +  	reg = (void __iomem *) xhci->cap_regs + 0x80a4;  	val = readl(reg);  	writel(val | BIT(28), reg);  	readl(reg);  } +#ifdef CONFIG_ACPI +static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) +{ +	static const u8 intel_dsm_uuid[] = { +		0xb7, 0x0c, 0x34, 0xac,	0x01, 0xe9, 0xbf, 0x45, +		0xb7, 0xe6, 0x2b, 0x34, 0xec, 0x93, 0x1e, 0x23, +	}; +	acpi_evaluate_dsm(ACPI_HANDLE(&dev->dev), intel_dsm_uuid, 3, 1, NULL); +} +#else +	static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } +#endif /* CONFIG_ACPI */ +  /* called during probe() after chip reset completes */  static int xhci_pci_setup(struct usb_hcd *hcd)  { @@ -263,6 +311,9 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)  			HCC_MAX_PSA(xhci->hcc_params) >= 4)  		xhci->shared_hcd->can_do_streams = 1; +	if (xhci->quirks & XHCI_PME_STUCK_QUIRK) +		xhci_pme_acpi_rtd3_enable(dev); +  	/* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */  	pm_runtime_put_noidle(&dev->dev); @@ -307,7 +358,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)  		pdev->no_d3cold = true;  	if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -		xhci_pme_quirk(xhci); +		xhci_pme_quirk(hcd, true);  	return xhci_suspend(xhci, do_wakeup);  } @@ -340,7 +391,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)  		usb_enable_intel_xhci_ports(pdev);  	if (xhci->quirks & XHCI_PME_STUCK_QUIRK) -		xhci_pme_quirk(xhci); +		xhci_pme_quirk(hcd, false);  	retval = xhci_resume(xhci, hibernated);  	return retval;  |