]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
xhci: turn off port power in shutdown
authorMathias Nyman <mathias.nyman@linux.intel.com>
Thu, 23 Jun 2022 11:19:43 +0000 (14:19 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jun 2022 06:59:50 +0000 (08:59 +0200)
commit 83810f84ecf11dfc5a9414a8b762c3501b328185 upstream.

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: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20220623111945.1557702-3-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 1eb3b5deb940ea1794af6027336576ba09d14a05..94adae8b19f00c709a056f10763158485399b43c 100644 (file)
@@ -566,7 +566,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
  * It will release and re-aquire the lock while calling ACPI
  * 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)
        __must_hold(&xhci->lock)
 {
index a1ed5e0d061280992369af71f913debcdc8360d3..997de5f294f15c0d319f154f73566e63ebdbf632 100644 (file)
@@ -775,6 +775,8 @@ static void xhci_stop(struct usb_hcd *hcd)
 void xhci_shutdown(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       unsigned long flags;
+       int i;
 
        if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
                usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
@@ -790,12 +792,21 @@ void xhci_shutdown(struct usb_hcd *hcd)
                del_timer_sync(&xhci->shared_hcd->rh_timer);
        }
 
-       spin_lock_irq(&xhci->lock);
+       spin_lock_irqsave(&xhci->lock, flags);
        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 */
        if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
                xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
-       spin_unlock_irq(&xhci->lock);
+       spin_unlock_irqrestore(&xhci->lock, flags);
 
        xhci_cleanup_msix(xhci);
 
index a46bbf5beffa91467579e1f20c08ce05803d917d..0c66424b34ba94fe2b9e22e39768a11f41f2d37f 100644 (file)
@@ -2162,6 +2162,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_find_raw_port_number(struct usb_hcd *hcd, int port1);
 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);