[PATCH v2 2/2] clk: qcom: gdsc: add RPM_ALWAYS_ON flag

From: Herman van Hazendonk

Date: Sat Jun 06 2026 - 08:35:20 EST


Some power domains need to stay powered across runtime PM even though
their clocks may still gate, and only collapse on full system suspend.
Add an RPM_ALWAYS_ON flag that maps to the existing
GENPD_FLAG_RPM_ALWAYS_ON on the underlying generic_pm_domain.

This is distinct from the existing ALWAYS_ON flag (which keeps the
domain permanently enabled and prevents collapse even during system
suspend) and from leaving the flag unset (which allows the domain to
collapse on every runtime-idle transition).

GENPD_FLAG_RPM_ALWAYS_ON, like GENPD_FLAG_ALWAYS_ON, requires the
underlying genpd to be in the ON state at pm_genpd_init() time --
the framework rejects registration otherwise. Fold RPM_ALWAYS_ON
into the gdsc_init() block that already force-enables ALWAYS_ON
GDSCs found powered down at sync time so the flag combination is
honoured consistently.

The first user is the upcoming MSM8x60 MMCC driver, which needs
RPM_ALWAYS_ON on the a2xx (Adreno 220) GFX3D footswitch: cold-cycling
the GPU rail on every runtime idle forces an a2xx_hw_init microcode
reload whose MMIO burst can stall the shared MMSS AXI fabric when it
coincides with an MDP display client-switch underrun, hard-hanging
the SoC. Letting the rail stay up during runtime PM (clocks still
gate, idle power is still saved) and only collapsing on system
suspend avoids the corner case while still allowing full power-down
during deep sleep.

Assisted-by: Claude:claude-opus-4-7 Sashiko:claude-haiku-4-5
Signed-off-by: Herman van Hazendonk <github.com@xxxxxxxxxx>
---
drivers/clk/qcom/gdsc.c | 21 ++++++++++++++++++---
drivers/clk/qcom/gdsc.h | 12 ++++++++++++
2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
index 1f80bebe88c7..0622bb23d17b 100644
--- a/drivers/clk/qcom/gdsc.c
+++ b/drivers/clk/qcom/gdsc.c
@@ -777,9 +777,22 @@ static int gdsc_init(struct gdsc *sc)
goto err_disable_supply;
}

- } else if (sc->flags & ALWAYS_ON) {
- /* If ALWAYS_ON GDSCs are not ON, turn them ON */
- gdsc_enable(&sc->pd);
+ } else if (sc->flags & (ALWAYS_ON | RPM_ALWAYS_ON)) {
+ /*
+ * Both GENPD_FLAG_ALWAYS_ON and GENPD_FLAG_RPM_ALWAYS_ON
+ * require the domain to be ON at pm_genpd_init() time --
+ * the framework rejects registration otherwise. Bring up
+ * any such GDSC that is currently off so the genpd flags
+ * we set below match the silicon state.
+ *
+ * Propagate the gdsc_enable() return so a failure here does
+ * not silently set on=true and leak a vote through the
+ * err_disable_supply path (which would unwind a vote that
+ * was never actually taken).
+ */
+ ret = gdsc_enable(&sc->pd);
+ if (ret)
+ return ret;
on = true;
}

@@ -790,6 +803,8 @@ static int gdsc_init(struct gdsc *sc)

if (sc->flags & ALWAYS_ON)
sc->pd.flags |= GENPD_FLAG_ALWAYS_ON;
+ if (sc->flags & RPM_ALWAYS_ON)
+ sc->pd.flags |= GENPD_FLAG_RPM_ALWAYS_ON;
if (!sc->pd.power_off)
sc->pd.power_off = gdsc_disable;
if (!sc->pd.power_on)
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
index 13ca09f93a01..27acf20e8d68 100644
--- a/drivers/clk/qcom/gdsc.h
+++ b/drivers/clk/qcom/gdsc.h
@@ -75,6 +75,18 @@ struct gdsc {
* transition completed after a fixed delay rather than polling status.
*/
#define LEGACY_FOOTSWITCH BIT(10)
+/*
+ * Keep the domain powered across runtime PM (its clocks may still gate via
+ * the clock framework) and only allow it to power-collapse on system
+ * suspend. Maps to GENPD_FLAG_RPM_ALWAYS_ON on the underlying genpd. Useful
+ * for blocks whose cold-start sequence is expensive enough that runtime
+ * power cycling causes user-visible latency or hardware corner-case bugs --
+ * e.g. the MSM8x60 a2xx (Adreno 220) graphics footswitch, whose first
+ * power-up after collapse forces a full microcode reload that can stall
+ * the shared MMSS AXI fabric when it coincides with an MDP display
+ * underrun.
+ */
+#define RPM_ALWAYS_ON BIT(11)
struct reset_controller_dev *rcdev;
unsigned int *resets;
unsigned int reset_count;

--
2.43.0