git: efdb82413963 - main - thunderbolt: Reset controllers

From: Aymeric Wibo <obiwac_at_FreeBSD.org>
Date: Wed, 12 Aug 2026 21:53:29 UTC
The branch main has been updated by obiwac:

URL: https://cgit.FreeBSD.org/src/commit/?id=efdb82413963bea5f4bf2ae52006cceeb6944a3b

commit efdb82413963bea5f4bf2ae52006cceeb6944a3b
Author:     Aymeric Wibo <obiwac@FreeBSD.org>
AuthorDate: 2026-08-12 17:40:56 +0000
Commit:     Aymeric Wibo <obiwac@FreeBSD.org>
CommitDate: 2026-08-12 21:53:13 +0000

    thunderbolt: Reset controllers
    
    Reset routine for both v1.0 and v2.0 routes, chosen depending on version
    reported in caps.
    
    Reviewed by:    imp
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D49452
---
 sys/dev/thunderbolt/nhi.c        | 63 ++++++++++++++++++++++++++++++++++++++++
 sys/dev/thunderbolt/router_var.h |  4 +++
 2 files changed, 67 insertions(+)

diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c
index 4b45560923c3..75eb4fe6a5d9 100644
--- a/sys/dev/thunderbolt/nhi.c
+++ b/sys/dev/thunderbolt/nhi.c
@@ -235,6 +235,67 @@ nhi_outmail_cmd(struct nhi_softc *sc, uint32_t *val)
 	return (0);
 }
 
+static int
+nhi_reset_v1(struct nhi_softc *sc)
+{
+
+	/* See section 2.4 of HCM guide v2. */
+	nhi_write_reg(sc, ROUTER_HIR, 1);
+	pause_sbt("nhi", ustosbt(10 * 1000), 0, C_HARDCLOCK);
+	return (0);
+}
+
+static int
+nhi_reset_v2(struct nhi_softc *sc)
+{
+	uint32_t reg;
+
+	/*
+	 * TODO "The Connection Manager shall disable all Transmit Descriptor
+	 * Rings and wait for at least 1 millisecond prior to setting the Host
+	 * Router Reset bit to 1b. After the Connection Manager sets the Host
+	 * Router Reset bit to 1b, it shall not access the Receive Descriptor
+	 * Rings until the Host Router Reset bit is set to 0b."
+	 */
+	/* See section 3.5 of HCM guide v2. */
+	nhi_write_reg(sc, ROUTER_HRR, 1);
+	/*
+	 * "The Host Router is required to complete its reset within 500ms
+	 * after the Host Router Reset bit is set to 1b."
+	 */
+	for (size_t i = 0; i < 10 && reg; i++) {
+		/*
+		 * Wait at least 50 ms after writing before reading this
+		 * register.  If this is 1, it means that we are still
+		 * resetting.
+		 */
+		pause_sbt("nhi", ustosbt(50 * 1000), 0, 0);
+		reg = nhi_read_reg(sc, ROUTER_HRR);
+	}
+	if (reg == 0) {
+		tb_debug(sc, DBG_INIT|DBG_EXTRA,
+		    "Succeeded in resetting host router\n");
+		return (0);
+	}
+	tb_printf(sc, "Host router reset timed out\n");
+	return (ETIMEDOUT);
+}
+
+static int
+nhi_reset(struct nhi_softc *sc)
+{
+
+	tb_debug(sc, DBG_INIT, "Resetting host router\n");
+
+	switch (sc->ver) {
+	case NHI_VER_1_0:
+		return (nhi_reset_v1(sc));
+	case NHI_VER_2_0:
+		return (nhi_reset_v2(sc));
+	}
+	return (ENXIO);
+}
+
 int
 nhi_attach(struct nhi_softc *sc)
 {
@@ -272,6 +333,8 @@ nhi_attach(struct nhi_softc *sc)
 	}
 	sc->path_count = val;
 
+	nhi_reset(sc);
+
 	SLIST_INIT(&sc->ring_list);
 
 	error = nhi_pci_configure_interrupts(sc);
diff --git a/sys/dev/thunderbolt/router_var.h b/sys/dev/thunderbolt/router_var.h
index 8366ede852e7..06c41e636121 100644
--- a/sys/dev/thunderbolt/router_var.h
+++ b/sys/dev/thunderbolt/router_var.h
@@ -87,6 +87,10 @@ struct router_cfg_cap {
 	uint16_t	vsec_len;
 };
 
+/* Router reset registers */
+#define ROUTER_HRR	0x39898
+#define ROUTER_HIR	0x39858
+
 int tb_router_attach(struct router_softc *, tb_route_t);
 int tb_router_attach_root(struct nhi_softc *, tb_route_t);
 int tb_router_detach(struct router_softc *);