[PATCH 2/3] Bluetooth: hci_qca: Support QCA2066 on M.2 connector via pwrseq
From: Loic Poulain
Date: Wed May 20 2026 - 07:04:28 EST
For QCA2066 (and other QCA chips) on M.2 connectors, the UART enable
is controlled by the W_DISABLE2# signal managed by the pcie-m2 power
sequencer rather than a dedicated BT enable GPIO.
When the serdev controller has an OF graph (indicating it is connected
to an M.2 connector), acquire the 'uart' pwrseq target from the
connector's power sequencer and use it to control BT power instead of
the bt-enable GPIO.
Also allocate bt_power unconditionally for all SOC types since the
pwrseq path is independent of the SOC type switch.
Signed-off-by: Loic Poulain <loic.poulain@xxxxxxxxxxxxxxxx>
---
drivers/bluetooth/hci_qca.c | 33 +++++++++++++--------------------
1 file changed, 13 insertions(+), 20 deletions(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index b5439b9956cfb0497e6ba6ccd9ed61224d23a9dd..de5cba7b7f44e280a48dad5d670fa2758d3268d0 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1873,6 +1873,9 @@ static int qca_power_on(struct hci_dev *hdev)
/* Controller needs time to bootup. */
msleep(150);
}
+
+ if (qcadev->bt_power && qcadev->bt_power->pwrseq)
+ pwrseq_power_on(qcadev->bt_power->pwrseq);
}
clear_bit(QCA_BT_OFF, &qca->flags);
@@ -2415,25 +2418,9 @@ static int qca_serdev_probe(struct serdev_device *serdev)
else
qcadev->btsoc_type = QCA_ROME;
- switch (qcadev->btsoc_type) {
- case QCA_QCA6390:
- case QCA_WCN3950:
- case QCA_WCN3988:
- case QCA_WCN3990:
- case QCA_WCN3991:
- case QCA_WCN3998:
- case QCA_WCN6750:
- case QCA_WCN6855:
- case QCA_WCN7850:
- qcadev->bt_power = devm_kzalloc(&serdev->dev,
- sizeof(struct qca_power),
- GFP_KERNEL);
- if (!qcadev->bt_power)
- return -ENOMEM;
- break;
- default:
- break;
- }
+ qcadev->bt_power = devm_kzalloc(&serdev->dev, sizeof(struct qca_power), GFP_KERNEL);
+ if (!qcadev->bt_power)
+ return -ENOMEM;
switch (qcadev->btsoc_type) {
case QCA_WCN3950:
@@ -2543,7 +2530,13 @@ static int qca_serdev_probe(struct serdev_device *serdev)
return PTR_ERR(qcadev->bt_en);
}
- if (!qcadev->bt_en)
+ if (of_graph_is_present(dev_of_node(&serdev->ctrl->dev))) {
+ qcadev->bt_power->pwrseq = devm_pwrseq_get(&serdev->ctrl->dev, "uart");
+ if (IS_ERR(qcadev->bt_power->pwrseq))
+ return PTR_ERR(qcadev->bt_power->pwrseq);
+ }
+
+ if (!qcadev->bt_en && !qcadev->bt_power->pwrseq)
bt_en_available = false;
qcadev->susclk = devm_clk_get_optional_enabled_with_rate(
--
2.34.1