]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: hci_qca: Fix BT not getting powered-off on rmmod
authorHans de Goede <johannes.goede@oss.qualcomm.com>
Thu, 12 Feb 2026 14:17:21 +0000 (15:17 +0100)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 10 Apr 2026 14:23:02 +0000 (10:23 -0400)
The BT core skips calling the hci_dev's shutdown method when the HCI
is unregistered. This means that qca_power_off() was not getting called
leaving BT powered on.

This causes regulators / pwrseq providers to not get disabled which also
causes problem when re-loading the module because regulators and pwrseq
providers have an enablecount which now has never dropped to 0, causing
the BT to not get properly reset between rmmod and re-load which causes
initialization failure on the re-load.

Fix this by calling qca_power_off() from qca_close() when BT has not
already been powered off through a qca_hci_shutdown() call.

hci_ldisc.c will call qca_close() after freeing the hdev, so this
means that qca_power_off() can now no longer deref hu->hdev, change
the logging in qca_power_off() to no longer use hu->hdev.

Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
drivers/bluetooth/hci_qca.c

index e66d1736acf6e1cc3c38929ae1cfff0e1b919f36..d6e78201a6753bd3f81ae0b06dc8590bc0a277ce 100644 (file)
@@ -722,6 +722,10 @@ static int qca_close(struct hci_uart *hu)
 
        BT_DBG("hu %p qca close", hu);
 
+       /* BT core skips qca_hci_shutdown() which calls qca_power_off() on rmmod */
+       if (!test_bit(QCA_BT_OFF, &qca->flags))
+               qca_power_off(hu);
+
        serial_clock_vote(HCI_IBS_VOTE_STATS_UPDATE, hu);
 
        skb_queue_purge(&qca->tx_wait_q);
@@ -2260,7 +2264,7 @@ static void qca_power_off(struct hci_uart *hu)
                qca_regulator_disable(qcadev);
                if (qcadev->sw_ctrl) {
                        sw_ctrl_state = gpiod_get_value_cansleep(qcadev->sw_ctrl);
-                       bt_dev_dbg(hu->hdev, "SW_CTRL is %d", sw_ctrl_state);
+                       BT_DBG("SW_CTRL is %d", sw_ctrl_state);
                }
                break;