Re: [BUG] rtw88_8723de: Wi-Fi and Bluetooth fail after warm reboot

Lovekesh Solanki <[email protected]> Sat, 1 Aug 2026 02:00:17 +0530
Newsgroups org.kernel.vger.linux-usb,org.kernel.vger.linux-kernel,org.kernel.vger.linux-wireless
Message-ID <am0AOC9ZmP81kGcT@eggarch>
Thanks for additional testing and logs,

On Fri, Jul 31, 2026 at 03:58:32AM -0600, Snake Pilot wrote:
>   Module unloaded       Extra delay    Wi-Fi              Bluetooth
>   ------------------------------------------------------------------------
>   rtw88_8723de          0 seconds      failed (-16)       failed (-71)
>   rtw88_8723de          1 second       failed (-16)       failed (-71)
>   rtw88_8723de          3 seconds      failed (-16)       failed (-71)
>   rtw88_8723de          5 seconds      failed (-16)       failed (-71)
> 
>   btusb                 0 seconds      worked             worked
>   btusb                 1 second       worked             worked
>   btusb                 3 seconds      worked             worked
>   btusb                 5 seconds      worked             worked
I'd say this rules out anything in rtw88 and we should focus on btusb,
since unloading it fixed both functions.

> No Wi-Fi interface appeared, and Bluetooth remained absent. Thus, the PCI bus
> reset did not recover the device after the failure had occurred.
Yeah and with -71 above seems like chip isn't answering read descriptors
at all.

The previous boot journal there's no other bluetooth activity logged
after boot, so a graceful bluetooth shutdown doesn't seem to be
happening on a warm reboot, but it does work when you explicitely unbind
it.

Problem could be in the power state chip may be left in a state warm
reboot cannot clear.

A somewhat similar issue with the Qualcomm QCA6390 combo 7e7bbddd029b
("qca6390 enable failure after warm reboot"), where they fixed it by
simply making sure chip got proper reset/shutdown command before
restart. This could also work for our case but needs confirmation.

I'm attaching a small patch that just adds extra logging around the 
shutdown path for this chip to see what's been invoked and what not
before a warm reboot and if it even completes.

Could you apply it and perform a simple warm reboot and send the
journalctl -k -b -1 after it?

Regards, 
Lovekesh
diag.patch (text/plain, 3.9 KB)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 7f54d2d..b78feb7 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -1402,6 +1402,8 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
 	struct sk_buff *skb;
 	int ret;
 
+	bt_dev_info(hdev, "diag: btrtl_shutdown_realtek: sending HCI_OP_RESET");
+
 	/* According to the vendor driver, BT must be reset on close to avoid
 	 * firmware crash.
 	 */
@@ -1411,6 +1413,7 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
 		bt_dev_err(hdev, "HCI reset during shutdown failed");
 		return ret;
 	}
+	bt_dev_info(hdev, "diag: btrtl_shutdown_realtek: HCI_OP_RESET completed OK");
 	kfree_skb(skb);
 
 	return 0;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 184e95c..382ff81 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -4470,6 +4470,8 @@ static void btusb_disconnect(struct usb_interface *intf)
 	struct btusb_data *data = usb_get_intfdata(intf);
 	struct hci_dev *hdev;
 
+	bt_dev_info(data ? data->hdev : NULL, "diag: btusb_disconnect called for intf %p", intf);
+
 	BT_DBG("intf %p", intf);
 
 	if (!data)
@@ -4682,6 +4684,15 @@ static void btusb_coredump(struct device *dev)
 }
 #endif
 
+static void btusb_shutdown(struct device *dev)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct btusb_data *data = usb_get_intfdata(intf);
+
+	pr_info("btusb: diag: device_shutdown() reached btusb for intf %p, hdev up=%d\n",
+		intf, data && data->hdev ? test_bit(HCI_UP, &data->hdev->flags) : -1);
+}
+
 static struct usb_driver btusb_driver = {
 	.name		= "btusb",
 	.probe		= btusb_probe,
@@ -4692,11 +4703,12 @@ static struct usb_driver btusb_driver = {
 	.supports_autosuspend = 1,
 	.disable_hub_initiated_lpm = 1,
 
-#ifdef CONFIG_DEV_COREDUMP
 	.driver = {
+#ifdef CONFIG_DEV_COREDUMP
 		.coredump = btusb_coredump,
-	},
 #endif
+		.shutdown = btusb_shutdown,
+	},
 };
 
 module_usb_driver(btusb_driver);
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index eaa928b..49c5d07 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -166,12 +166,16 @@ static int rtw_pwr_cmd_polling(struct rtw_dev *rtwdev,
 
 	/* if PCIE, toggle BIT_PFM_WOWL and try again */
 	value = rtw_read8(rtwdev, REG_SYS_PW_CTRL);
+	rtw_info(rtwdev, "diag: pre-toggle REG_SYS_PW_CTRL=0x%02x, poll offset=0x%x want=0x%x\n",
+		 value, offset, cmd->value & cmd->mask);
 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D)
 		rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL);
 	rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL);
 	rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL);
 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D)
 		rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL);
+	rtw_info(rtwdev, "diag: post-toggle REG_SYS_PW_CTRL=0x%02x, retry poll read=0x%02x\n",
+		 rtw_read8(rtwdev, REG_SYS_PW_CTRL), rtw_read8(rtwdev, offset));
 
 	if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value))
 		return 0;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c8d1412..17dfc92 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5416,11 +5416,19 @@ static int hci_dev_shutdown(struct hci_dev *hdev)
 	bool was_userchannel =
 		hci_dev_test_and_clear_flag(hdev, HCI_USER_CHANNEL);
 
+	bt_dev_info(hdev, "diag: hci_dev_shutdown: HCI_UP=%d HCI_UNREGISTER=%d shutdown_cb=%ps",
+		    test_bit(HCI_UP, &hdev->flags),
+		    hci_dev_test_flag(hdev, HCI_UNREGISTER),
+		    hdev->shutdown);
+
 	if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
 	    test_bit(HCI_UP, &hdev->flags)) {
 		/* Execute vendor specific shutdown routine */
-		if (hdev->shutdown)
+		if (hdev->shutdown) {
+			bt_dev_info(hdev, "diag: calling vendor shutdown callback");
 			err = hdev->shutdown(hdev);
+			bt_dev_info(hdev, "diag: vendor shutdown returned %d", err);
+		}
 	}
 
 	if (was_userchannel)