[PATCH 3/4] Bluetooth: qca: Power off after unregistering the HCI device
From: Michael McAllister
Date: Wed Oct 28 2026 - 18:50:18 EST
qca_serdev_remove() powers off the controller while HCI setup and
transmission can still be running. Leave power-off to qca_close(), which
runs after HCI unregistration and write-work cancellation, while the
serial transport is still available.
Use the serial port's open state for the power-pulse check. PROTO_READY
is already clear during protocol cleanup, even when the port is open.
The open-state check also skips serial I/O after a failed setup reopen.
Mark a newly allocated serdev controller as off until power-on is tried,
so registration failures do not send a power pulse before HCI allocation.
Signed-off-by: Michael McAllister <me@xxxxxxxxxxxxxxxxxxxxx>
---
drivers/bluetooth/hci_qca.c | 23 ++---------------------
1 file changed, 2 insertions(+), 21 deletions(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 3d9e6cd15..3d688ab75 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -619,6 +619,7 @@ static int qca_open(struct hci_uart *hu)
hu->priv = qca;
if (hu->serdev) {
+ set_bit(QCA_BT_OFF, &qca->flags);
qcadev = serdev_device_get_drvdata(hu->serdev);
switch (qcadev->btsoc_type) {
@@ -2252,11 +2253,7 @@ static void qca_power_off(struct hci_uart *hu)
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
- /* Both of these write to the serial port which may have
- * already been closed by hci_uart_close(), which closes
- * the port if HCI_QUIRK_NON_PERSISTENT_SETUP is set.
- */
- if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+ if (hu->serdev_open) {
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
}
@@ -2580,22 +2577,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
static void qca_serdev_remove(struct serdev_device *serdev)
{
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
- struct qca_power *power = qcadev->bt_power;
-
- switch (qcadev->btsoc_type) {
- case QCA_WCN3988:
- case QCA_WCN3990:
- case QCA_WCN3991:
- case QCA_WCN3998:
- case QCA_WCN6750:
- case QCA_WCN6855:
- case QCA_WCN7850:
- if (power->vregs_on)
- qca_power_off(&qcadev->serdev_hu);
- break;
- default:
- break;
- }
hci_uart_unregister_device(&qcadev->serdev_hu);
}