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

* Re: [PATCH] thunderbolt: Assert downstream port reset on shutdown
  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
  1 sibling, 0 replies; 3+ messages in thread
From: Mario Limonciello @ 2026-06-03 19:02 UTC (permalink / raw)
  To: Basavaraj Natikar, andreas.noever, westeri, YehezkelShB,
	linux-usb
  Cc: Sanath S, Vivek Das Mohapatra, Kovacs, Alexander



On 6/3/26 13:01, Basavaraj Natikar wrote:
> 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>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> ---
>   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);


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

* Re: [PATCH] thunderbolt: Assert downstream port reset on shutdown
  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
  1 sibling, 0 replies; 3+ messages in thread
From: Mika Westerberg @ 2026-06-04  5:03 UTC (permalink / raw)
  To: Basavaraj Natikar
  Cc: andreas.noever, westeri, YehezkelShB, linux-usb, Sanath S

Hi,

On Wed, Jun 03, 2026 at 11:31:46PM +0530, Basavaraj Natikar wrote:
> On shutdown the connection manager tears down the switch tree without

router tree

> 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

router tree

> (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");
> +	}

But now this tears down the topology also when the driver is unloaded? If
you want to do that in shutdown there is ->shutdown hook for that.

>  	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	[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