Re: [PATCH 1/2] Bluetooth: hci_qca: Add WCN clock management for pwrseq-based power path

From: Loic Poulain

Date: Tue Sep 29 2026 - 06:09:23 EST


On Fri, Sep 25, 2026 at 8:21 AM Yepuri Siddu
<yepuri.siddu@xxxxxxxxxxxxxxxx> wrote:
>
> RPMCC previously kept WCN clocks enabled via proxy votes, so the pwrseq
> power path in hci_qca did not need to explicitly manage the clock.
> With proxy vote removal, each consumer must explicitly enable and disable
> its required clocks.
>
> Extend the pwrseq-based power path to acquire and manage an optional
> WCN clock. In qca_serdev_probe(), acquire the clock using
> devm_clk_get_optional(). In qca_regulator_enable(), enable the clock
> after a successful pwrseq_enable() with proper rollback on failure.
> In qca_power_off(), disable the clock before pwrseq_disable().
>
> Targets that do not define a clock in DTS are unaffected since
> devm_clk_get_optional() returns NULL and all clock operations are
> guarded accordingly.
>
> Signed-off-by: Yepuri Siddu <yepuri.siddu@xxxxxxxxxxxxxxxx>
> ---
> drivers/bluetooth/hci_qca.c | 23 ++++++++++++++++++++---
> 1 file changed, 20 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index 7089e9b639b2..489a059e519a 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -2265,6 +2265,8 @@ static void qca_power_off(struct hci_uart *hu)
> }
>
> if (power && power->pwrseq) {
> + if (qcadev->susclk)
> + clk_disable_unprepare(qcadev->susclk);
> pwrseq_disable(power->pwrseq);
> set_bit(QCA_BT_OFF, &qca->flags);
> return;
> @@ -2324,8 +2326,17 @@ static int qca_regulator_enable(struct qca_serdev *qcadev)
> struct qca_power *power = qcadev->bt_power;
> int ret;
>
> - if (power->pwrseq)
> - return pwrseq_enable(power->pwrseq);
> + if (power->pwrseq) {
> + ret = pwrseq_enable(power->pwrseq);
> + if (ret)
> + return ret;
> + if (qcadev->susclk) {
> + ret = clk_prepare_enable(qcadev->susclk);

Why is susclk guarded by the pwrseq? They seem unrelated to me.

> + if (ret)
> + pwrseq_disable(power->pwrseq);
> + }
> + return ret;
> + }
>
> /* Already enabled */
> if (power->vregs_on)
> @@ -2472,8 +2483,14 @@ static int qca_serdev_probe(struct serdev_device *serdev)
> */
> if (IS_ERR(qcadev->bt_power->pwrseq))
> qcadev->bt_power->pwrseq = NULL;
> - else
> + else {
> + qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
> + if (IS_ERR(qcadev->susclk)) {
> + dev_err(&serdev->dev, "failed to acquire clk\n");
> + return PTR_ERR(qcadev->susclk);
> + }
> break;
> + }
> }
>
> qcadev->bt_power->dev = &serdev->dev;
>
> --
> 2.34.1
>
>