| From 036dae27f55403cf8ce25170f22e8e1dc7d05051 Mon Sep 17 00:00:00 2001 |
| From: Henry Lin <henryl@nvidia.com> |
| Date: Wed, 11 Dec 2019 16:20:04 +0200 |
| Subject: [PATCH] usb: xhci: only set D3hot for pci device |
| |
| commit f2c710f7dca8457e88b4ac9de2060f011254f9dd upstream. |
| |
| Xhci driver cannot call pci_set_power_state() on non-pci xhci host |
| controllers. For example, NVIDIA Tegra XHCI host controller which acts |
| as platform device with XHCI_SPURIOUS_WAKEUP quirk set in some platform |
| hits this issue during shutdown. |
| |
| Cc: <stable@vger.kernel.org> |
| Fixes: 638298dc66ea ("xhci: Fix spurious wakeups after S5 on Haswell") |
| Signed-off-by: Henry Lin <henryl@nvidia.com> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Link: https://lore.kernel.org/r/20191211142007.8847-4-mathias.nyman@linux.intel.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c |
| index c2fe218e051f..e9cc90d16620 100644 |
| --- a/drivers/usb/host/xhci-pci.c |
| +++ b/drivers/usb/host/xhci-pci.c |
| @@ -519,6 +519,18 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) |
| } |
| #endif /* CONFIG_PM */ |
| |
| +static void xhci_pci_shutdown(struct usb_hcd *hcd) |
| +{ |
| + struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); |
| + |
| + xhci_shutdown(hcd); |
| + |
| + /* Yet another workaround for spurious wakeups at shutdown with HSW */ |
| + if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) |
| + pci_set_power_state(pdev, PCI_D3hot); |
| +} |
| + |
| /*-------------------------------------------------------------------------*/ |
| |
| /* PCI driver selection metadata; PCI hotplugging uses this */ |
| @@ -554,6 +566,7 @@ static int __init xhci_pci_init(void) |
| #ifdef CONFIG_PM |
| xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; |
| xhci_pci_hc_driver.pci_resume = xhci_pci_resume; |
| + xhci_pci_hc_driver.shutdown = xhci_pci_shutdown; |
| #endif |
| return pci_register_driver(&xhci_pci_driver); |
| } |
| diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c |
| index 74d56b6776e5..9d6d18ce990c 100644 |
| --- a/drivers/usb/host/xhci.c |
| +++ b/drivers/usb/host/xhci.c |
| @@ -770,7 +770,7 @@ static void xhci_stop(struct usb_hcd *hcd) |
| * |
| * This will only ever be called with the main usb_hcd (the USB3 roothub). |
| */ |
| -static void xhci_shutdown(struct usb_hcd *hcd) |
| +void xhci_shutdown(struct usb_hcd *hcd) |
| { |
| struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| |
| @@ -789,11 +789,8 @@ static void xhci_shutdown(struct usb_hcd *hcd) |
| xhci_dbg_trace(xhci, trace_xhci_dbg_init, |
| "xhci_shutdown completed - status = %x", |
| readl(&xhci->op_regs->status)); |
| - |
| - /* Yet another workaround for spurious wakeups at shutdown with HSW */ |
| - if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) |
| - pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot); |
| } |
| +EXPORT_SYMBOL_GPL(xhci_shutdown); |
| |
| #ifdef CONFIG_PM |
| static void xhci_save_registers(struct xhci_hcd *xhci) |
| diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h |
| index fabbce1c542a..08be89398126 100644 |
| --- a/drivers/usb/host/xhci.h |
| +++ b/drivers/usb/host/xhci.h |
| @@ -2048,6 +2048,7 @@ int xhci_start(struct xhci_hcd *xhci); |
| int xhci_reset(struct xhci_hcd *xhci); |
| int xhci_run(struct usb_hcd *hcd); |
| int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); |
| +void xhci_shutdown(struct usb_hcd *hcd); |
| void xhci_init_driver(struct hc_driver *drv, |
| const struct xhci_driver_overrides *over); |
| int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); |
| -- |
| 2.7.4 |
| |