Re: [PATCH v3 08/11] Bluetooth: hci_qca: Support QCA2066 on M.2 connector via pwrseq
From: Chen-Yu Tsai
Date: Thu Jul 16 2026 - 04:35:29 EST
On Fri, Jul 10, 2026 at 11:57:34AM +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. 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");
Note that this ultimately ties the lifecycle of the returned pwrseq
descriptor to the uart, which is probably not what you want.
I ended up manually doing pwrseq_get() with devm_add_action_or_reset()
so that the descriptor is released when the serdev driver is unbound.
This was for the Realtek driver. I haven't sent patches yet.
> + 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");
It's already incorrect here. But I think the original patch that
introduces this hasn't landed?
ChenYu
> - 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
>