[PATCH v3 08/11] Bluetooth: hci_qca: Support QCA2066 on M.2 connector via pwrseq

From: Loic Poulain

Date: Fri Jul 10 2026 - 06:00:20 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. This is factored out into qca_serdev_get_m2_pwrseq().

Reviewed-by: Manivannan Sadhasivam <mani@xxxxxxxxxx>
Signed-off-by: Loic Poulain <loic.poulain@xxxxxxxxxxxxxxxx>
---
drivers/bluetooth/hci_qca.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 24dc5bf6d192be7e1d500df6bdf36e6184f9eaf7..f6529d359ffbd97f8a3c4c0593680ab200215b36 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1872,6 +1872,9 @@ static int qca_power_on(struct hci_dev *hdev)
/* Controller needs time to bootup. */
msleep(150);
}
+
+ if (qcadev->bt_power.pwrseq)
+ pwrseq_power_on(qcadev->bt_power.pwrseq);
}

clear_bit(QCA_BT_OFF, &qca->flags);
@@ -2387,6 +2390,20 @@ static int qca_init_regulators(struct qca_power *qca,
return 0;
}

+static int qca_serdev_get_m2_pwrseq(struct qca_serdev *qcadev)
+{
+ struct serdev_device *serdev = qcadev->serdev_hu.serdev;
+
+ if (!of_graph_is_present(dev_of_node(&serdev->ctrl->dev)))
+ return 0;
+
+ 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);
+
+ return 0;
+}
+
static int qca_serdev_probe(struct serdev_device *serdev)
{
struct qca_serdev *qcadev;
@@ -2417,6 +2434,10 @@ static int qca_serdev_probe(struct serdev_device *serdev)
else
qcadev->btsoc_type = QCA_ROME;

+ err = qca_serdev_get_m2_pwrseq(qcadev);
+ if (err)
+ return err;
+
switch (qcadev->btsoc_type) {
case QCA_WCN3950:
case QCA_WCN3988:
@@ -2426,17 +2447,9 @@ static int qca_serdev_probe(struct serdev_device *serdev)
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
- /*
- * OF graph link is only present for BT devices attached through
- * the M.2 Key E connector.
- */
- 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);
+ /* M.2 connector modules are powered by the pwrseq acquired above. */
+ if (qcadev->bt_power.pwrseq)
break;
- }

if (!device_property_present(&serdev->dev, "enable-gpios")) {
/*
@@ -2528,7 +2541,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
return PTR_ERR(qcadev->susclk);
}
}
-
+
if (qcadev->bt_power.pwrseq) {
bt_en_available = pwrseq_power_is_controllable(qcadev->bt_power.pwrseq);
if (!bt_en_available) {

--
2.34.1