Skip to content

Commit 6ccb83d

Browse files
Udipto Goswamigregkh
authored andcommitted
usb: xhci: Implement xhci_handshake_check_state() helper
In some situations where xhci removal happens parallel to xhci_handshake, we encounter a scenario where the xhci_handshake can't succeed, and it polls until timeout. If xhci_handshake runs until timeout it can on some platforms result in a long wait which might lead to a watchdog timeout. Add a helper that checks xhci status during the handshake, and exits if set state is entered. Use this helper in places where xhci_handshake is called unlocked and has a long timeout. For example xhci command timeout and xhci reset. [commit message and code comment rewording -Mathias] Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20231019102924.2797346-18-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 47f503c commit 6ccb83d

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

drivers/usb/host/xhci-ring.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,9 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
450450
* In the future we should distinguish between -ENODEV and -ETIMEDOUT
451451
* and try to recover a -ETIMEDOUT with a host controller reset.
452452
*/
453-
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
454-
CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
453+
ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring,
454+
CMD_RING_RUNNING, 0, 5 * 1000 * 1000,
455+
XHCI_STATE_REMOVING);
455456
if (ret < 0) {
456457
xhci_err(xhci, "Abort failed to stop command ring: %d\n", ret);
457458
xhci_halt(xhci);

drivers/usb/host/xhci.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,29 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us)
8181
return ret;
8282
}
8383

84+
/*
85+
* xhci_handshake_check_state - same as xhci_handshake but takes an additional
86+
* exit_state parameter, and bails out with an error immediately when xhc_state
87+
* has exit_state flag set.
88+
*/
89+
int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr,
90+
u32 mask, u32 done, int usec, unsigned int exit_state)
91+
{
92+
u32 result;
93+
int ret;
94+
95+
ret = readl_poll_timeout_atomic(ptr, result,
96+
(result & mask) == done ||
97+
result == U32_MAX ||
98+
xhci->xhc_state & exit_state,
99+
1, usec);
100+
101+
if (result == U32_MAX || xhci->xhc_state & exit_state)
102+
return -ENODEV;
103+
104+
return ret;
105+
}
106+
84107
/*
85108
* Disable interrupts and begin the xHCI halting process.
86109
*/
@@ -201,7 +224,8 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us)
201224
if (xhci->quirks & XHCI_INTEL_HOST)
202225
udelay(1000);
203226

204-
ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us);
227+
ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command,
228+
CMD_RESET, 0, timeout_us, XHCI_STATE_REMOVING);
205229
if (ret)
206230
return ret;
207231

drivers/usb/host/xhci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,6 +2084,8 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
20842084
/* xHCI host controller glue */
20852085
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
20862086
int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us);
2087+
int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr,
2088+
u32 mask, u32 done, int usec, unsigned int exit_state);
20872089
void xhci_quiesce(struct xhci_hcd *xhci);
20882090
int xhci_halt(struct xhci_hcd *xhci);
20892091
int xhci_start(struct xhci_hcd *xhci);

0 commit comments

Comments
 (0)