Skip to content

Commit d26c00e

Browse files
matnymangregkh
authored andcommitted
xhci: Fix repeated xhci wake after suspend due to uncleared internal wake state
If port terminations are detected in suspend, but link never reaches U0 then xHCI may have an internal uncleared wake state that will cause an immediate wake after suspend. This wake state is normally cleared when driver clears the PORT_CSC bit, which is set after a device is enabled and in U0. Write 1 to clear PORT_CSC for ports that don't have anything connected when suspending. This makes sure any pending internal wake states in xHCI are cleared. Cc: [email protected] Tested-by: Mika Westerberg <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b71c669 commit d26c00e

File tree

1 file changed

+30
-32
lines changed

1 file changed

+30
-32
lines changed

drivers/usb/host/xhci.c

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -883,44 +883,42 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
883883
xhci_set_cmd_ring_deq(xhci);
884884
}
885885

886-
static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
886+
/*
887+
* Disable port wake bits if do_wakeup is not set.
888+
*
889+
* Also clear a possible internal port wake state left hanging for ports that
890+
* detected termination but never successfully enumerated (trained to 0U).
891+
* Internal wake causes immediate xHCI wake after suspend. PORT_CSC write done
892+
* at enumeration clears this wake, force one here as well for unconnected ports
893+
*/
894+
895+
static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
896+
struct xhci_hub *rhub,
897+
bool do_wakeup)
887898
{
888-
struct xhci_port **ports;
889-
int port_index;
890899
unsigned long flags;
891900
u32 t1, t2, portsc;
901+
int i;
892902

893903
spin_lock_irqsave(&xhci->lock, flags);
894904

895-
/* disable usb3 ports Wake bits */
896-
port_index = xhci->usb3_rhub.num_ports;
897-
ports = xhci->usb3_rhub.ports;
898-
while (port_index--) {
899-
t1 = readl(ports[port_index]->addr);
900-
portsc = t1;
901-
t1 = xhci_port_state_to_neutral(t1);
902-
t2 = t1 & ~PORT_WAKE_BITS;
903-
if (t1 != t2) {
904-
writel(t2, ports[port_index]->addr);
905-
xhci_dbg(xhci, "disable wake bits port %d-%d, portsc: 0x%x, write: 0x%x\n",
906-
xhci->usb3_rhub.hcd->self.busnum,
907-
port_index + 1, portsc, t2);
908-
}
909-
}
905+
for (i = 0; i < rhub->num_ports; i++) {
906+
portsc = readl(rhub->ports[i]->addr);
907+
t1 = xhci_port_state_to_neutral(portsc);
908+
t2 = t1;
909+
910+
/* clear wake bits if do_wake is not set */
911+
if (!do_wakeup)
912+
t2 &= ~PORT_WAKE_BITS;
913+
914+
/* Don't touch csc bit if connected or connect change is set */
915+
if (!(portsc & (PORT_CSC | PORT_CONNECT)))
916+
t2 |= PORT_CSC;
910917

911-
/* disable usb2 ports Wake bits */
912-
port_index = xhci->usb2_rhub.num_ports;
913-
ports = xhci->usb2_rhub.ports;
914-
while (port_index--) {
915-
t1 = readl(ports[port_index]->addr);
916-
portsc = t1;
917-
t1 = xhci_port_state_to_neutral(t1);
918-
t2 = t1 & ~PORT_WAKE_BITS;
919918
if (t1 != t2) {
920-
writel(t2, ports[port_index]->addr);
921-
xhci_dbg(xhci, "disable wake bits port %d-%d, portsc: 0x%x, write: 0x%x\n",
922-
xhci->usb2_rhub.hcd->self.busnum,
923-
port_index + 1, portsc, t2);
919+
writel(t2, rhub->ports[i]->addr);
920+
xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n",
921+
rhub->hcd->self.busnum, i + 1, portsc, t2);
924922
}
925923
}
926924
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -983,8 +981,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
983981
return -EINVAL;
984982

985983
/* Clear root port wake on bits if wakeup not allowed. */
986-
if (!do_wakeup)
987-
xhci_disable_port_wake_on_bits(xhci);
984+
xhci_disable_hub_port_wake(xhci, &xhci->usb3_rhub, do_wakeup);
985+
xhci_disable_hub_port_wake(xhci, &xhci->usb2_rhub, do_wakeup);
988986

989987
if (!HCD_HW_ACCESSIBLE(hcd))
990988
return 0;

0 commit comments

Comments
 (0)