Linux USB
 help / color / mirror / Atom feed
* [PATCH] thunderbolt: Assert downstream port reset on shutdown
@ 2026-06-03 18:01 Basavaraj Natikar
  2026-06-03 19:02 ` Mario Limonciello
  2026-06-04  5:03 ` Mika Westerberg
  0 siblings, 2 replies; 3+ messages in thread
From: Basavaraj Natikar @ 2026-06-03 18:01 UTC (permalink / raw)
  To: andreas.noever, westeri, YehezkelShB, linux-usb
  Cc: Basavaraj Natikar, Sanath S

On shutdown the connection manager tears down the switch tree without
signalling connected devices. Thunderbolt 3 devices directly connected
to a USB4 host never receive a disconnect indication and during shutdown
this can cause polling the dead link for up to 60 seconds. On some
platforms this behavior leads to a warm reset instead of a shutdown due
to this timeout.

Fix this by asserting PORT_CS_19.DPR on each connected downstream port
before tearing down the switch tree. This drives SBTX low unconditionally
(USB4 spec section 6.9), causing the device to detect SBRX low and
transition to Uninitialized Unplugged state immediately.

Co-developed-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
---
 drivers/thunderbolt/switch.c |  2 +-
 drivers/thunderbolt/tb.c     | 11 +++++++++++
 drivers/thunderbolt/tb.h     |  1 +
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index c2ad58b19e7b..52812908818b 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -704,7 +704,7 @@ int tb_port_disable(struct tb_port *port)
 	return __tb_port_enable(port, false);
 }
 
-static int tb_port_reset(struct tb_port *port)
+int tb_port_reset(struct tb_port *port)
 {
 	if (tb_switch_is_usb4(port->sw))
 		return port->cap_usb4 ? usb4_port_reset(port) : 0;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index c69c323e6952..ca57b5181422 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -2935,6 +2935,7 @@ static void tb_stop(struct tb *tb)
 	struct tb_cm *tcm = tb_priv(tb);
 	struct tb_tunnel *tunnel;
 	struct tb_tunnel *n;
+	struct tb_port *port;
 
 	cancel_delayed_work(&tcm->remove_work);
 	/* tunnels are only present after everything has been initialized */
@@ -2948,6 +2949,16 @@ static void tb_stop(struct tb *tb)
 			tb_tunnel_deactivate(tunnel);
 		tb_tunnel_put(tunnel);
 	}
+	/*
+	 * Assert DPR to drive SBTX low, signalling disconnect and avoiding
+	 * ~60 s of link polling before warm reset on shutdown.
+	 */
+	tb_switch_for_each_port(tb->root_switch, port) {
+		if (!tb_port_is_null(port) || !tb_port_has_remote(port))
+			continue;
+		if (tb_port_reset(port))
+			tb_port_dbg(port, "DPR on shutdown failed, continuing\n");
+	}
 	tb_switch_remove(tb->root_switch);
 	tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
 }
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 217c3114bec8..875eb538eacf 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1102,6 +1102,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
 int tb_port_unlock(struct tb_port *port);
 int tb_port_enable(struct tb_port *port);
 int tb_port_disable(struct tb_port *port);
+int tb_port_reset(struct tb_port *port);
 int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-06-04  5:03 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 18:01 [PATCH] thunderbolt: Assert downstream port reset on shutdown Basavaraj Natikar
2026-06-03 19:02 ` Mario Limonciello
2026-06-04  5:03 ` Mika Westerberg

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