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

From: Dmitry Baryshkov

Date: Thu Jul 02 2026 - 10:40:30 EST


On Thu, Jul 02, 2026 at 04:17:43PM +0200, Manivannan Sadhasivam wrote:
> On Thu, Jul 02, 2026 at 03:14:49PM +0300, Dmitry Baryshkov wrote:
> > On Thu, Jul 02, 2026 at 12:46:15PM +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
> >
> > Can we just fold it into the main struct?
> >
> > > pwrseq path is independent of the SOC type switch.
> > >
> > > Signed-off-by: Loic Poulain <loic.poulain@xxxxxxxxxxxxxxxx>
> > > ---
> > > drivers/bluetooth/hci_qca.c | 81 ++++++++++++++++++++++++---------------------
> > > 1 file changed, 43 insertions(+), 38 deletions(-)
> > >
> > > diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> > > index e09debdb00a1b8e74ccd5de6147e240e533b4594..b04593a96e14ac9e87ae76fa00eda308e81dea25 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);
> > > @@ -2256,7 +2259,7 @@ static void qca_power_off(struct hci_uart *hu)
> > > break;
> > > }
> > >
> > > - if (power && power->pwrseq) {
> > > + if (power->pwrseq) {
> > > pwrseq_power_off(power->pwrseq);
> > > set_bit(QCA_BT_OFF, &qca->flags);
> > > return;
> > > @@ -2387,6 +2390,35 @@ static int qca_init_regulators(struct qca_power *qca,
> > > return 0;
> > > }
> > >
> > > +/*
> > > + * Acquire the M.2 connector power sequencer.
> > > + *
> > > + * An OF graph link on the serdev controller is only present when the BT
> > > + * device is attached through an M.2 Key E connector. In that case the UART
> > > + * enable (W_DISABLE2#) is driven by the pcie-m2 power sequencer instead of a
> > > + * dedicated BT enable GPIO, so grab the "uart" pwrseq target from it.
> > > + *
> > > + * Returns 0 if no M.2 connector is present (nothing to do), a negative errno
> > > + * on error, otherwise 0 with qcadev->bt_power->pwrseq populated.
> > > + */
> > > +static int qca_serdev_get_m2_pwrseq(struct qca_serdev *qcadev, bool *bt_en_available)
> > > +{
> > > + struct serdev_device *serdev = qcadev->serdev_hu.serdev;
> > > + struct device *dev;
> > > +
> > > + 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);
> > > +
> > > + dev = pwrseq_to_device(qcadev->bt_power->pwrseq);
> > > + *bt_en_available = device_property_present(dev, "w-disable2-gpios");
> >
> > I think here you are looking into the exact details of the other of the
> > graph. There might be other devices on that side, while the code now
> > assumes M.2. Or, consider having an M.2 controller which handles
> > W_DISABLE2# internally rather than through the GPIO.
> >
>
> This code only deals with M.2 connector in specific, so I'm not sure why we need
> to worry about *other* kind of devices. Let's worry about them when they show up
> (with graph interface ofc).

I don't think we want to go through the drivers using M.2 connectors in
such a case. In the end, the contract should be that there is a power
sequencer on the other side of the graph, but the specifics of the
connector should be abstracted out. Do you know, if in the x86 world the
W_DISABLE2# is a GPIO or is controleed by the hub.

--
With best wishes
Dmitry