From: Udipto Goswami Date: Thu, 19 Oct 2023 10:29:22 +0000 (+0300) Subject: usb: xhci: Implement xhci_handshake_check_state() helper X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=6ccb83d6c4972ebe6ae49de5eba051de3638362c;p=linux.git 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 Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231019102924.2797346-18-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 73057f01e4aa5..f3b5e6345858c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -450,8 +450,9 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * In the future we should distinguish between -ENODEV and -ETIMEDOUT * and try to recover a -ETIMEDOUT with a host controller reset. */ - ret = xhci_handshake(&xhci->op_regs->cmd_ring, - CMD_RING_RUNNING, 0, 5 * 1000 * 1000); + ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring, + CMD_RING_RUNNING, 0, 5 * 1000 * 1000, + XHCI_STATE_REMOVING); if (ret < 0) { xhci_err(xhci, "Abort failed to stop command ring: %d\n", ret); xhci_halt(xhci); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 68920cb960443..119cbe2a3d654 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -81,6 +81,29 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us) return ret; } +/* + * 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. */ @@ -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; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 813d55468c00f..5768ce804caa7 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2084,6 +2084,8 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci, /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us); +int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr, + u32 mask, u32 done, int usec, unsigned int exit_state); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_start(struct xhci_hcd *xhci);