diff options
author | Udipto Goswami <quic_ugoswami@quicinc.com> | 2023-10-19 13:29:22 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-10-21 13:38:54 +0300 |
commit | 6ccb83d6c4972ebe6ae49de5eba051de3638362c (patch) | |
tree | 0c445a42f1c128a11fde186eb29f7cf9592f9825 /drivers/usb/host/xhci.c | |
parent | 47f503cf5f799ec02e5f4b7c3b9afe145eca2aef (diff) | |
download | linux-6ccb83d6c4972ebe6ae49de5eba051de3638362c.tar.xz |
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>
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 68920cb96044..119cbe2a3d65 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -82,6 +82,29 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us) } /* + * xhci_handshake_check_state - same as xhci_handshake but takes an additional + * exit_state parameter, and bails out with an error immediately when xhc_state + * has exit_state flag set. + */ +int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr, + u32 mask, u32 done, int usec, unsigned int exit_state) +{ + u32 result; + int ret; + + ret = readl_poll_timeout_atomic(ptr, result, + (result & mask) == done || + result == U32_MAX || + xhci->xhc_state & exit_state, + 1, usec); + + if (result == U32_MAX || xhci->xhc_state & exit_state) + return -ENODEV; + + return ret; +} + +/* * Disable interrupts and begin the xHCI halting process. */ void xhci_quiesce(struct xhci_hcd *xhci) @@ -201,7 +224,8 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us) if (xhci->quirks & XHCI_INTEL_HOST) udelay(1000); - ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us); + ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command, + CMD_RESET, 0, timeout_us, XHCI_STATE_REMOVING); if (ret) return ret; |