[PATCH 1/2] Bluetooth: hci_qca: Add WCN clock management for pwrseq-based power path
From: Yepuri Siddu
Date: Fri Sep 25 2026 - 02:14:53 EST
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);
+ 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