]> git.ipfire.org Git - thirdparty/kernel/stable.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>
Sat, 2 Jul 2022 14:27:34 +0000 (16:27 +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 1bb48e53449e6e941c5730321baba8e99f430c60..e40370ed7d8a5ff1eef0a8d0e7cb0e110630abdf 100644 (file)
@@ -565,7 +565,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)
 {
        struct xhci_hub *rhub;
index a9e72fee87a77b3ac78d7e927b3405d30452ae19..677587479d260ed26ef403a9a2a365f04511e04c 100644 (file)
@@ -774,6 +774,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));
@@ -789,12 +791,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 3e6cb257fde5a1ef14f62e89e72da84a161d3fde..3b29dfc6f79e34eb5e780f145736537cea55088f 100644 (file)
@@ -2145,6 +2145,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);