diff options
| author | Roy Luo <royluo@google.com> | 2025-05-22 19:09:12 +0000 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2025-06-19 12:41:35 +0200 | 
| commit | 7aed15379db9c6ec67999cdaf5c443b7be06ea73 (patch) | |
| tree | d59873b872239df662f09dfb423cdf143d3affd9 /drivers/usb/host/xhci.c | |
| parent | 3eff494f6e17abf932699483f133a708ac0355dc (diff) | |
Revert "usb: xhci: Implement xhci_handshake_check_state() helper"
This reverts commit 6ccb83d6c4972ebe6ae49de5eba051de3638362c.
Commit 6ccb83d6c497 ("usb: xhci: Implement xhci_handshake_check_state()
helper") was introduced to workaround watchdog timeout issues on some
platforms, allowing xhci_reset() to bail out early without waiting
for the reset to complete.
Skipping the xhci handshake during a reset is a dangerous move. The
xhci specification explicitly states that certain registers cannot
be accessed during reset in section 5.4.1 USB Command Register (USBCMD),
Host Controller Reset (HCRST) field:
"This bit is cleared to '0' by the Host Controller when the reset
process is complete. Software cannot terminate the reset process
early by writinga '0' to this bit and shall not write any xHC
Operational or Runtime registers until while HCRST is '1'."
This behavior causes a regression on SNPS DWC3 USB controller with
dual-role capability. When the DWC3 controller exits host mode and
removes xhci while a reset is still in progress, and then tries to
configure its hardware for device mode, the ongoing reset leads to
register access issues; specifically, all register reads returns 0.
These issues extend beyond the xhci register space (which is expected
during a reset) and affect the entire DWC3 IP block, causing the DWC3
device mode to malfunction.
Cc: stable <stable@kernel.org>
Fixes: 6ccb83d6c497 ("usb: xhci: Implement xhci_handshake_check_state() helper")
Signed-off-by: Roy Luo <royluo@google.com>
Link: https://lore.kernel.org/r/20250522190912.457583-3-royluo@google.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, 1 insertions, 25 deletions
| diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4637d026716a..8a819e853288 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -85,29 +85,6 @@ 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) @@ -227,8 +204,7 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us)  	if (xhci->quirks & XHCI_INTEL_HOST)  		udelay(1000); -	ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command, -				CMD_RESET, 0, timeout_us, XHCI_STATE_REMOVING); +	ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us);  	if (ret)  		return ret; | 
