[PATCH 5/5] usb: hub: tolerate transient disconnects during reset

Ahmad Fatoum a.fatoum at barebox.org
Fri Jun 26 10:31:41 PDT 2026


Port over U-Boot commit 74c0d756dea14855bec47f380b6ccb557be5db7c:

|   usb: hub: don't check CONNECTION in hub_port_reset()
|
|   One specific USB 3.0 device behaves strangely when reset by
|   usb_new_device()'s call to hub_port_reset(). For some reason, the device
|   appears to briefly drop off the bus when this second bus reset is
|   executed, yet if we retry this loop, it'll eventually come back after
|   another two resets.
|
|   If USB bus reset is executed over and over within usb_new_device()'s call
|   to hub_port_reset(), I see the following sequence of results, which
|   repeats as long as you want:
|
|   1) STAT_C_CONNECTION = 1 STAT_CONNECTION = 0  USB_PORT_STAT_ENABLE 0
|   2) STAT_C_CONNECTION = 1 STAT_CONNECTION = 1  USB_PORT_STAT_ENABLE 0
|   3) STAT_C_CONNECTION = 1 STAT_CONNECTION = 1  USB_PORT_STAT_ENABLE 1
|
|   The device in question is a SanDisk Ultra USB 3.0 16GB memory stick with
|   USB VID/PID 0x0781/0x5581.
|
|   In order to allow this device to work with U-Boot, ignore the
|   {C_,}CONNECTION bits in the status/change registers, and only use the
|   ENABLE bit to determine if the reset was successful.
|
|   To be honest, extensive investigation has failed to determine why this
|   problem occurs. I'd love to know! I don't know if it's caused by:
|   * A HW bug in the device
|   * A HW bug in the Tegra USB controller
|   * A SW bug in the U-Boot Tegra USB driver
|   * A SW bug in the U-Boot USB core
|
|   This issue only occurs when the device's USB3 pins are attached to the
|   host; if only the USB2 pins are connected the issue does not occur. The
|   USB3 controller on Tegra is in reset, so is not actively communicating
|   with the device at all - a USB3 analyzer confirms this. Slightly
|   unplugging the device (so the USB3 pins don't contact) or using a USB2
|   cable or hub as an intermediary avoids the problem. For some reason,
|   the Linux kernel (either on the same Tegra board, or on an x86 host)
|   has no issue with the device, and I observe no disconnections during
|   reset.
|
|   This change won't affect any USB device that already works, since such
|   devices could not currently be triggering the error return this patch
|   removes, or they wouldn't be working currently.
|
|   However, this patch is quite reliable in practice, hence I hope it's
|   acceptable to solve the problem.
|
|   The only potential fallout I can see from this patch is:
|
|   * A broken device that triggers C_CONNECTION/!CONNECTION now causes the
|     loop in hub_port_reset() to run multiple times. If it never succeeds,
|     this will cause "usb start" to take roughly 1s extra to execute.
|
|   * If the user unplugs a device while hub_port_reset() is executing, and
|     very quickly swaps in a new device, hub_port_reset() might succeed on
|     the new device. This would mean that any information cached about the
|     original device (from the descriptor read in usb_new_device(), which
|     simply caches the max packet size) might be invalid, which would cause
|     problems talking to the new device. However, without this change, the
|     new device wouldn't work anyway, so this is probably not much of a
|     loss.
|
|   Signed-off-by: Stephen Warren <swarren at nvidia.com>

Assisted-by: Codex:gpt-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum at barebox.org>
---
 drivers/usb/core/hub.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index e969fc466a8e..3820b4cc9067 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -208,9 +208,22 @@ static int hub_port_reset(struct usb_device *hub, int port,
 			(portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0,
 			(portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0);
 
-		if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
-		    !(portstatus & USB_PORT_STAT_CONNECTION))
-			return -1;
+		/*
+		 * Perhaps we should check for the following here:
+		 * - C_CONNECTION hasn't been set.
+		 * - CONNECTION is still set.
+		 *
+		 * Doing so would ensure that the device is still connected
+		 * to the bus, and hasn't been unplugged or replaced while the
+		 * USB bus reset was going on.
+		 *
+		 * However, if we do that, then (at least) a San Disk Ultra
+		 * USB 3.0 16GB device fails to reset on (at least) an NVIDIA
+		 * Tegra Jetson TK1 board. For some reason, the device appears
+		 * to briefly drop off the bus when this second bus reset is
+		 * executed, yet if we retry this loop, it'll eventually come
+		 * back after another reset or two.
+		 */
 
 		if (portstatus & USB_PORT_STAT_ENABLE)
 			break;
-- 
2.47.3




More information about the barebox mailing list