From 5f40cba7d4fa343cf074d1a764683c1392f3134e Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Thu, 11 Jun 2026 11:11:57 +0530 Subject: thunderbolt: Assert downstream port reset on shutdown On shutdown the connection manager tears down the router tree without signalling connected devices. A Thunderbolt 3 device directly connected to a USB4 host never receives 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 router tree. This drives SBTX low (USB4 spec section 6.9), causing the device to detect SBRX low and transition to Uninitialized Unplugged state immediately. Always do this on system shutdown/reboot by forcing host_reset in the PCI ->shutdown callback. On plain driver unload only do it when the host router was actually reset on load (host_reset=1), since in that case the tunnels are not preserved across reload anyway; with host_reset=0 the tunnels are kept alive across unload/reload so the links are left intact. Restrict the reset to Thunderbolt 3 devices. Reviewed-by: Mario Limonciello (AMD) Co-developed-by: Sanath S Signed-off-by: Sanath S Signed-off-by: Basavaraj Natikar Signed-off-by: Mika Westerberg --- include/linux/thunderbolt.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index feb1af175cfd..cb1621c6b703 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -514,6 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc); * @hop_count: Number of rings (end point hops) supported by NHI. * @quirks: NHI specific quirks if any * @domain_released: Completed when domain has been fully released + * @host_reset: Host router was reset on driver load, or forced on system + * shutdown/reboot. When set, tb_stop() asserts DPR on connected + * downstream ports to signal disconnect before tearing down the + * router tree. Only Thunderbolt 3 devices are reset; USB4 + * routers are skipped. */ struct tb_nhi { spinlock_t lock; @@ -528,6 +533,7 @@ struct tb_nhi { u32 hop_count; unsigned long quirks; struct completion domain_released; + bool host_reset; }; /** -- cgit v1.2.3 From 45f104755af82afe24e104adeab69fe390fbeeca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 18 Jun 2026 12:14:50 +0200 Subject: thunderbolt: Stop passing matched device ID to .probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No driver makes use of that parameter, so drop it and don't spend the effort to determine the matching entry. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Mika Westerberg --- include/linux/thunderbolt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index cb1621c6b703..0a9ac4bfea67 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -465,7 +465,7 @@ static inline struct tb_service *tb_to_service(struct device *dev) */ struct tb_service_driver { struct device_driver driver; - int (*probe)(struct tb_service *svc, const struct tb_service_id *id); + int (*probe)(struct tb_service *svc); void (*remove)(struct tb_service *svc); void (*shutdown)(struct tb_service *svc); const struct tb_service_id *id_table; -- cgit v1.2.3 From f5cb175ed728488aacd8a631cb0a815bfb06664e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 18 Jun 2026 07:22:49 +0300 Subject: thunderbolt: Make interrupt optional for rings For some use-cases it does make sense to poll the rings directly instead of relying on the interrupt. For this reason add a new flag RING_FLAG_NO_INTERRUPT that can be used to allocate ring in polled mode. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Mika Westerberg --- include/linux/thunderbolt.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 0a9ac4bfea67..a4e3e8248ace 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -598,6 +598,8 @@ struct tb_ring { #define RING_FLAG_FRAME BIT(1) /* Enable end-to-end flow control */ #define RING_FLAG_E2E BIT(2) +/* Do not enable interrupt for the ring */ +#define RING_FLAG_NO_INTERRUPT BIT(3) struct ring_frame; typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); -- cgit v1.2.3