mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH] usb: dwc2: host: rework wait_for_chhltd to handle ACK and retry on errors
@ 2026-03-31  3:48 chalianis1
  0 siblings, 0 replies; only message in thread
From: chalianis1 @ 2026-03-31  3:48 UTC (permalink / raw)
  To: s.hauer; +Cc: barebox, Chali Anis

From: Chali Anis <chalianis1@gmail.com>

This fixes read corruptions observed on RPi4 when accessing storage
devices behind a USB hub.

Rework the logic inspired by coreboot's dwc2 driver:

- Check HCINT for XFERCOMPL or ACK first, before waiting for CHHLTD,
  to handle the case where DMA has already completed by the time we
  get here; as done in coreboot, both are treated as a valid transfer
  completion since the device acknowledged the transaction
- Wait for CHHLTD after detecting completion and re-read HCINT to get a
  consistent snapshot, ensuring the channel has fully halted and is in a
  known state before the caller starts a new transaction, avoiding
  premature channel teardown; unrecoverable errors (STALL, BABBLE) and
  timeouts are handled at this stage to guarantee a clean state for the
  next transaction
- Add XACTERR handling, returning -EAGAIN to let the caller retry the
  whole transaction

Note: the retry loop and the speculative pre-read interact in a non-obvious
way, however this approach leads to a more consistent and robust operation
of the controller. Despite the complexity, this has been stress-tested by
reading a kernel image over USB and verifying its checksum over 500
consecutive iterations without a single failure, demonstrating reliable
operation in practice even if the code deserves further cleanup.

Signed-off-by: Chali Anis <chalianis1@gmail.com>
---
 drivers/usb/dwc2/host.c | 50 +++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c
index 93994f0be3be..2fb3d936cd31 100644
--- a/drivers/usb/dwc2/host.c
+++ b/drivers/usb/dwc2/host.c
@@ -129,35 +129,41 @@ static void dwc2_endpoint_reset(struct dwc2 *dwc2, int in, int devnum, int ep)
 
 static int wait_for_chhltd(struct dwc2 *dwc2, u8 hc, uint32_t *sub, u8 *tgl)
 {
-	int ret;
+	int ret, retry = 5;
 	uint32_t hcint, hctsiz, hcchar;
+	bool done = false;
 
-	ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000);
-	if (ret) {
-		hcchar = dwc2_readl(dwc2, HCCHAR(hc));
-		dwc2_writel(dwc2, hcchar | HCCHAR_CHDIS, HCCHAR(hc));
-		dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000);
-		return ret;
-	}
-
-	hcint = dwc2_readl(dwc2, HCINT(hc));
+	do {
+		hcint = dwc2_readl(dwc2, HCINT(hc));
+		if (hcint & (HCINTMSK_XFERCOMPL | HCINTMSK_ACK)) {
+			hctsiz = dwc2_readl(dwc2, HCTSIZ(hc));
+			*sub = (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT;
+			*tgl = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
+			dwc2_dbg(dwc2, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__,
+				hcint, *sub, *tgl);
+			done = true;
+		}
 
-	if (hcint & HCINTMSK_AHBERR)
-		dwc2_err(dwc2, "%s: AHB error during internal DMA access\n",
+		if (hcint & HCINTMSK_AHBERR)
+			dwc2_err(dwc2, "%s: AHB error during internal DMA access\n",
 			   __func__);
 
-	if (hcint & HCINTMSK_XFERCOMPL) {
-		hctsiz = dwc2_readl(dwc2, HCTSIZ(hc));
-		*sub = (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT;
-		*tgl = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
+		/* We wait for CHHLTD, and trap any exception for next transactions */
+		ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000);
+		hcint = dwc2_readl(dwc2, HCINT(hc));
+		if (ret || hcint & (HCINTMSK_STALL | HCINTMSK_BBLERR)) {
+			hcchar = dwc2_readl(dwc2, HCCHAR(hc));
+			dwc2_writel(dwc2, hcchar | HCCHAR_CHDIS, HCCHAR(hc));
+			return dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000);
+		}
 
-		dwc2_dbg(dwc2, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__,
-			 hcint, *sub, *tgl);
-		return 0;
-	}
+		if (done)
+			return 0;
+
+		if (hcint & (HCINTMSK_NAK | HCINTMSK_FRMOVRUN | HCINTMSK_XACTERR))
+			return -EAGAIN;
 
-	if (hcint & (HCINTMSK_NAK | HCINTMSK_FRMOVRUN))
-		return -EAGAIN;
+	} while (retry-- > 0);
 
 	dwc2_dbg(dwc2, "%s: Unknown channel status: (HCINT=%08x)\n", __func__,
 		 hcint);



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-03-31  3:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-03-31  3:48 [PATCH] usb: dwc2: host: rework wait_for_chhltd to handle ACK and retry on errors chalianis1

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox