[PATCH] usb: xhci: Add XHCI_TT_SOFT_RETRY quirk for transaction errors on TT devices
From: Jie Deng
Date: Fri Oct 09 2026 - 23:26:08 EST
Low-speed devices behind a high-speed hub on some xHCI controllers stop
working after repeated lsusb -v: a transient USB transaction error on a
bulk/interrupt endpoint is followed by a hard reset recovery where every
command completes successfully (Reset EP with TSP=0 and Set TR Deq both
return COMP_SUCCESS), but the first new split transaction issued after
the restart fails again. After two such cycles the hub reports a port
connection change and the device drops off.
The affected silicon does not honor the xHCI spec (4.6.8) requirement
that a Reset Endpoint command with TSP=0 "reset any USB2 split
transaction state associated with the endpoint": the stale split state
survives the reset, so the next split transaction carries the old
transaction's identity and can never match the hub TT state, cleared
or not. The soft reset path (TSP=1), which per the spec maintains the
split transaction state and retries the last transaction on the next
doorbell, recovers these transient errors on the first retry.
Add a XHCI_TT_SOFT_RETRY quirk: when set, bulk/interrupt endpoints
behind a TT hub may use the existing soft retry machinery to retry a
USB Transaction Error (completion code 4) in place instead of going
through the hard reset path. Platforms without the quirk keep the
exact previous behavior. Isochronous and control endpoints never
enter this function, and the existing MAX_SOFT_RETRY bound still falls
back to the hard reset path for persistent errors. The quirk is meant
to be set by platform code for controllers with this silicon bug; no
in-tree platform code sets it yet.
On the affected controller every error reports comp code 4 (USB
Transaction Error), tt_info 0x301 and err_count 1, i.e. each in-place
retry succeeds immediately and the fallback is never taken.
Signed-off-by: Jie Deng <dengjie03@xxxxxxxxxx>
---
drivers/usb/host/xhci-ring.c | 14 +++++++++++++-
drivers/usb/host/xhci.h | 7 +++++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index ec278a9f9540..46ac1a52f6ed 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2540,9 +2540,21 @@ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
td->urb->actual_length = sum_trb_lengths(td, ep_trb);
goto finish_td;
case COMP_USB_TRANSACTION_ERROR:
+ /*
+ * Devices behind a TT hub normally skip the soft retry: retrying
+ * a split transaction usually requires clearing the hub TT buffer
+ * first, so the hard reset path handles the error. Controllers
+ * that cannot properly reset the split transaction state with a
+ * hard reset (XHCI_TT_SOFT_RETRY) recover these transient errors
+ * by retrying the same transaction in place instead. The soft
+ * reset preserves the transfer state and the retry completes,
+ * while the hard reset path abandons the split, the first new
+ * split fails again and the low-speed link eventually collapses.
+ */
if (xhci->quirks & XHCI_NO_SOFT_RETRY ||
(ep->err_count++ > MAX_SOFT_RETRY) ||
- le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
+ (le32_to_cpu(slot_ctx->tt_info) & TT_SLOT &&
+ !(xhci->quirks & XHCI_TT_SOFT_RETRY)))
break;
td->status = 0;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c7bfa7f028d3..df4702c5e8a4 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1647,6 +1647,13 @@ struct xhci_hcd {
#define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48)
#define XHCI_ETRON_HOST BIT_ULL(49)
#define XHCI_LIMIT_ENDPOINT_INTERVAL_9 BIT_ULL(50)
+/*
+ * Controller cannot reset the USB2 split transaction state with a Reset
+ * Endpoint command (TSP=0) as required by xHCI spec 4.6.8. Recover
+ * transaction errors on devices behind a TT hub by soft retry (TSP=1)
+ * instead of the hard reset path.
+ */
+#define XHCI_TT_SOFT_RETRY BIT_ULL(51)
unsigned int num_active_eps;
unsigned int limit_active_eps;
--
2.25.1