Re: [PATCH 2/3] Bluetooth: hci_qca: Support QCA2066 on M.2 connector via pwrseq

From: Dmitry Baryshkov

Date: Wed May 20 2026 - 08:36:47 EST


On Wed, May 20, 2026 at 01:01:43PM +0200, Loic Poulain wrote:
> 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;

This builds bt_power for all devices even though it wasn't the case
beforehand. As such, you can drop all further `if (qcadev->bt_power)`
checks in the driver. But, you also need to check that this won't break
support for other (older) chips.

>
> 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))) {

And this breaks support for pwrseq for non-M.2 BT devices. There is no
OF graph in such a case.

> + 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
>

--
With best wishes
Dmitry