Commit 83810f84 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: turn off port power in shutdown

If ports are not turned off in shutdown then runtime suspended
self-powered USB devices may survive in U3 link state over S5.

During subsequent boot, if firmware sends an IPC command to program
the port in DISCONNECT state, it will time out, causing significant
delay in the boot time.

Turning off roothub port power is also recommended in xhci
specification 4.19.4 "Port Power" in the additional note.

Cc: stable@vger.kernel.org
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20220623111945.1557702-3-mathias.nyman@linux.intel.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a8089250
...@@ -652,7 +652,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) ...@@ -652,7 +652,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
* It will release and re-aquire the lock while calling ACPI * It will release and re-aquire the lock while calling ACPI
* method. * method.
*/ */
static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
u16 index, bool on, unsigned long *flags) u16 index, bool on, unsigned long *flags)
__must_hold(&xhci->lock) __must_hold(&xhci->lock)
{ {
......
...@@ -791,6 +791,8 @@ static void xhci_stop(struct usb_hcd *hcd) ...@@ -791,6 +791,8 @@ static void xhci_stop(struct usb_hcd *hcd)
void xhci_shutdown(struct usb_hcd *hcd) void xhci_shutdown(struct usb_hcd *hcd)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags;
int i;
if (xhci->quirks & XHCI_SPURIOUS_REBOOT) if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev)); usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
...@@ -806,12 +808,21 @@ void xhci_shutdown(struct usb_hcd *hcd) ...@@ -806,12 +808,21 @@ void xhci_shutdown(struct usb_hcd *hcd)
del_timer_sync(&xhci->shared_hcd->rh_timer); del_timer_sync(&xhci->shared_hcd->rh_timer);
} }
spin_lock_irq(&xhci->lock); spin_lock_irqsave(&xhci->lock, flags);
xhci_halt(xhci); xhci_halt(xhci);
/* Power off USB2 ports*/
for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
xhci_set_port_power(xhci, xhci->main_hcd, i, false, &flags);
/* Power off USB3 ports*/
for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
xhci_set_port_power(xhci, xhci->shared_hcd, i, false, &flags);
/* Workaround for spurious wakeups at shutdown with HSW */ /* Workaround for spurious wakeups at shutdown with HSW */
if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
xhci_reset(xhci, XHCI_RESET_SHORT_USEC); xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
spin_unlock_irq(&xhci->lock); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_cleanup_msix(xhci); xhci_cleanup_msix(xhci);
......
...@@ -2196,6 +2196,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, ...@@ -2196,6 +2196,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1); int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd); struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index,
bool on, unsigned long *flags);
void xhci_hc_died(struct xhci_hcd *xhci); void xhci_hc_died(struct xhci_hcd *xhci);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment