git: a24166d23d2e - main - mtw(4): Fix warm reboot initialization failures for MT7601U

From: Adrian Chadd <adrian_at_FreeBSD.org>
Date: Wed, 14 Jan 2026 03:15:22 UTC
The branch main has been updated by adrian:

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

commit a24166d23d2e93e787165064fac75215d4383c65
Author:     Abdelkader Boudih <oss@seuros.com>
AuthorDate: 2026-01-14 02:35:53 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-01-14 02:40:20 +0000

    mtw(4): Fix warm reboot initialization failures for MT7601U
    
      The mtw(4) driver works correctly on initial boot, but fails to initialize
      the MT7601U WiFi adapter after a warm reboot.
    
      Users must either physically unplug and replug the USB adapter, or perform a
      full power cycle to restore functionality, if usb power is always powered
      (only a replug works)
    
      The root cause is that warm reboot does not power-cycle USB devices,
      leaving the MT7601U in a stale state from the previous session.
    
      The MCU retains its ready flag and the device ignores initialization
      commands, resulting in timeout waiting for MCU to initialize errors.
    
      At the OS Level, pinging 1.1.1.1 will work, but the speed will be very
      slow. In addition in debug mode, we see thousand of error logs.
    
      This patch addresses the issue by:
    
      * Performing USB re-enumeration on attach to reset the device state
      * Detecting when the MCU is already marked ready (stale from previous
        session) and forcing a reset of the MCU before loading firmware
      * Increasing the firmware load timeout from 3s to 10s to accommodate
        slower initialization after reset
      * Increasing MCU ready poll attempts from 100 to 300 with longer delays
        to handle devices that take longer to become ready after reset
    
      Note: The increase was random, lower value might work.
    
    Test Plan:
    
      Tested on MacBook Pro (late-2015) and a MacMini with MediaTek MT7601U
      USB adapter across multiple warm reboot cycles.
    
      With the mac-mini and a another desktop, the issue happens only if
      connected via an always powered usb hub port in the monitor.
      The laptop don't power cycle it power.
    
    Differential Revision:  https://reviews.freebsd.org/D54659
    Reviewed by:    adrian
---
 sys/dev/usb/wlan/if_mtw.c | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/sys/dev/usb/wlan/if_mtw.c b/sys/dev/usb/wlan/if_mtw.c
index 8384c0a2d9fc..9d256056f6b2 100644
--- a/sys/dev/usb/wlan/if_mtw.c
+++ b/sys/dev/usb/wlan/if_mtw.c
@@ -64,6 +64,7 @@
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
+#include <dev/usb/usb_request.h>
 
 #include "usbdevs.h"
 
@@ -525,6 +526,15 @@ mtw_attach(device_t self)
 	sc->sc_dev = self;
 	sc->sc_sent = 0;
 
+	/*
+	 * Reset the device to clear any stale state left over from
+	 * a previous warm reboot. Some MT7601U devices fail otherwise.
+	 */
+	error = usbd_req_re_enumerate(uaa->device, NULL);
+	if (error != 0)
+		device_printf(self, "USB re-enumerate failed, continuing\n");
+	DELAY(100000);	/* 100ms settle time */
+
 	mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
                  MTX_NETWORK_LOCK, MTX_DEF);
 
@@ -585,7 +595,7 @@ mtw_attach(device_t self)
 	sc->mac_rev = tmp & 0xffff;
 
 	mtw_load_microcode(sc);
-	ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 3 * hz);
+	ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 10 * hz);
 	if (ret == EWOULDBLOCK || sc->fwloading != 1) {
 		device_printf(sc->sc_dev,
 		    "timeout waiting for MCU to initialize\n");
@@ -1105,11 +1115,22 @@ mtw_load_microcode(void *arg)
 	//	int ntries;
 	int dlen, ilen;
 	device_printf(sc->sc_dev, "version:0x%hx\n", sc->asic_ver);
-	/* is firmware already running? */
+	/*
+	 * Firmware may still be running from a previous warm reboot.
+	 * Force a reset of the MCU to ensure a clean state.
+	 */
 	mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp);
 	if (tmp == MTW_MCU_READY) {
-		return;
+		device_printf(sc->sc_dev, "MCU already running, resetting\n");
+		mtw_write(sc, MTW_MCU_RESET_CTL, MTW_RESET);
+		DELAY(10000);
+		mtw_write(sc, MTW_MCU_RESET_CTL, 0);
+		DELAY(10000);
+		/* Clear ready flag */
+		mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, 0);
+		DELAY(1000);
 	}
+
 	if (sc->asic_ver == 0x7612) {
 		fwname = "mtw-mt7662u_rom_patch";
 
@@ -2856,7 +2877,7 @@ mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error)
 			}
 
 			mtw_delay(sc, 10);
-			for (ntries = 0; ntries < 100; ntries++) {
+			for (ntries = 0; ntries < 300; ntries++) {
 				if ((error = mtw_read_cfg(sc, MTW_MCU_DMA_ADDR,
 					 &tmp)) != 0) {
 					device_printf(sc->sc_dev,
@@ -2870,9 +2891,9 @@ mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error)
 					break;
 				}
 
-				mtw_delay(sc, 10);
+				mtw_delay(sc, 30);
 			}
-			if (ntries == 100)
+			if (ntries == 300)
 				sc->fwloading = 0;
 			wakeup(&sc->fwloading);
 			return;