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

From: Loic Poulain

Date: Fri Jul 17 2026 - 08:33:53 EST


Hi Bartosz,

On Fri, Jul 17, 2026 at 11:35 AM Bartosz Golaszewski <brgl@xxxxxxxxxx> wrote:
>
> On Thu, 16 Jul 2026 18:18:25 +0200, Loic Poulain
> <loic.poulain@xxxxxxxxxxxxxxxx> said:
> > 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 | 41 +++++++++++++++++++++++++++++++----------
> > 1 file changed, 31 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> > index 25c4387136245fca25e375951f960d7240116781..22f6185a946ef04c45e41b2b34126e3f58d603ca 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,28 @@ static int qca_init_regulators(struct qca_power *qca,
> > return 0;
> > }
> >
> > +static void qca_serdev_put_pwrseq(void *data)
> > +{
> > + pwrseq_put(data);
> > +}
> > +
> > +static int qca_serdev_get_m2_pwrseq(struct qca_serdev *qcadev)
> > +{
> > + struct serdev_device *serdev = qcadev->serdev_hu.serdev;
> > + struct pwrseq_desc *pwrseq;
> > +
> > + if (!of_graph_is_present(dev_of_node(&serdev->ctrl->dev)))
> > + return 0;
> > +
> > + pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
>
> There's devm_pwrseq_get() you can use instead of scheduling your own action.

The reason is discussed here:
https://lore.kernel.org/all/20260716083141.GA4101439@xxxxxxxxxx/

We want the lifetime of the pwrseq descriptor to be tied to the serdev
consumer device rather than the underlying serdev controller.
Maybe a short comment explaining this relationship would be helpful.

Regards,
Loic

>
> > + if (IS_ERR(pwrseq))
> > + return PTR_ERR(pwrseq);
> > +
> > + qcadev->bt_power.pwrseq = pwrseq;
> > +
> > + return devm_add_action_or_reset(&serdev->dev, qca_serdev_put_pwrseq, pwrseq);
> > +}
> > +
> > static int qca_serdev_probe(struct serdev_device *serdev)
> > {
> > struct qca_serdev *qcadev;
> > @@ -2417,6 +2442,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 +2455,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")) {
> > /*
> >
> > --
> > 2.34.1
> >
> >
>
> Bart