From: Akhil P Oommen <[email protected]>

[ Upstream commit f195421318bd00151b3a111af6f315a25c3438a8 ]

CP_ALWAYS_ON counter falls under GX domain which is collapsed during
IFPC. So switch to GMU_ALWAYS_ON counter for any CPU reads since it is
not impacted by IFPC. Both counters are clocked by same xo clock source.

Signed-off-by: Akhil P Oommen <[email protected]>
Patchwork: https://patchwork.freedesktop.org/patch/673373/
Signed-off-by: Rob Clark <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
---

LLM Generated explanations, may be completely bogus:

YES

- Problem fixed
  - The CP_ALWAYS_ON counter lives in the GPU GX power domain and
    stops/gets reset during IFPC (inter‑frame power collapse). This
    makes CPU-side reads unreliable or forces the driver to wake the GX
    domain just to read a timestamp, which is fragile and power-
    inefficient.
  - The commit switches CPU reads to the GMU AO (always-on) counter,
    which is not impacted by IFPC and is clocked from the same XO clock,
    preserving timing semantics.

- What changed
  - Added a safe 64-bit read helper for the GMU AO counter with a hi-lo-
    hi read to avoid torn reads:
    drivers/gpu/drm/msm/adreno/a6xx_gpu.c:19
    - Reads `REG_A6XX_GMU_ALWAYS_ON_COUNTER_{H,L}` via `gmu_read()` and
      rechecks the high word to ensure atomicity.
  - Replaced CP counter reads in CPU-side tracepoints:
    - a6xx_submit now traces with GMU AO:
      drivers/gpu/drm/msm/adreno/a6xx_gpu.c:392
    - a7xx_submit now traces with GMU AO:
      drivers/gpu/drm/msm/adreno/a6xx_gpu.c:592
  - Simplified timestamp retrieval to avoid waking GX or using OOB
    votes:
    - a6xx_gmu_get_timestamp now returns the GMU AO counter directly:
      drivers/gpu/drm/msm/adreno/a6xx_gpu.c:2314
    - This removes previous lock/OOB sequences to temporarily block IFPC
      just to read `REG_A6XX_CP_ALWAYS_ON_COUNTER`.
  - Importantly, GPU-side emissions that snapshot the CP always-on
    counter via CP_REG_TO_MEM remain unchanged (they run when the GPU is
    active and safe): for example the stats reads in submit paths still
    use `REG_A6XX_CP_ALWAYS_ON_COUNTER` (e.g.,
    drivers/gpu/drm/msm/adreno/a6xx_gpu.c:372-375).

- Why this is a good stable candidate
  - Real bug impact: CPU reads of CP_ALWAYS_ON during IFPC can be stale,
    zero, or require disruptive OOB votes that wake the domain; this can
    cause incorrect timestamps (MSM_PARAM_TIMESTAMP), spurious power-
    ups, and trace anomalies. Moving to GMU AO fixes this by design.
  - Small, contained, and low risk:
    - All changes are local to the MSM Adreno a6xx/a7xx driver and a
      single source file.
    - No ABI or feature changes; only the source of the timestamp for
      CPU reads changes.
    - The helper uses a standard hi-lo-hi pattern to ensure a correct
      64-bit read.
    - The GMU AO counter is already described in the hardware XML and
      used elsewhere (e.g., other GMU counters), and the driver already
      depends on GMU MMIO.
  - Maintains timing consistency:
    - Both CP_ALWAYS_ON and GMU_ALWAYS_ON are clocked from XO (19.2
      MHz); userspace semantics are preserved. See the driver also
      treating GMU counters as 19.2 MHz (e.g.,
      drivers/gpu/drm/msm/adreno/a6xx_gpu.c:2361-2369).

- Side effects and regressions
  - Positive: avoids GMU OOB perfcounter votes and GMU lock/handshake
    just to read a timestamp, reducing the chance of deadlocks or long-
    latency paths during IFPC.
  - No architectural changes; no changes to command submission ordering
    or power sequencing.
  - Tracepoints now log the GMU AO value; this improves reliability
    during IFPC without affecting functionality.

- Dependencies present
  - `gmu_read()` and the `REG_A6XX_GMU_ALWAYS_ON_COUNTER_{H,L}` macros
    are already in-tree (drivers/gpu/drm/msm/adreno/a6xx_gmu.h:122 and
    drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml:131-132).
  - The patch updates only `drivers/gpu/drm/msm/adreno/a6xx_gpu.c`, and
    aligns with existing GMU usage patterns.

Conclusion: This is a targeted, safe bug fix that improves timestamp
reliability and avoids unnecessary power domain manipulations during
IFPC. It meets stable backport criteria.

 drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 30 ++++++++++++++-------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 536da1acf615e..1e363af319488 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -16,6 +16,19 @@
 
 #define GPU_PAS_ID 13
 
+static u64 read_gmu_ao_counter(struct a6xx_gpu *a6xx_gpu)
+{
+       u64 count_hi, count_lo, temp;
+
+       do {
+               count_hi = gmu_read(&a6xx_gpu->gmu, 
REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
+               count_lo = gmu_read(&a6xx_gpu->gmu, 
REG_A6XX_GMU_ALWAYS_ON_COUNTER_L);
+               temp = gmu_read(&a6xx_gpu->gmu, 
REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
+       } while (unlikely(count_hi != temp));
+
+       return (count_hi << 32) | count_lo;
+}
+
 static bool fence_status_check(struct msm_gpu *gpu, u32 offset, u32 value, u32 
status, u32 mask)
 {
        /* Success if !writedropped0/1 */
@@ -376,8 +389,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit)
        OUT_RING(ring, upper_32_bits(rbmemptr(ring, fence)));
        OUT_RING(ring, submit->seqno);
 
-       trace_msm_gpu_submit_flush(submit,
-               gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER));
+       trace_msm_gpu_submit_flush(submit, read_gmu_ao_counter(a6xx_gpu));
 
        a6xx_flush(gpu, ring);
 }
@@ -577,8 +589,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct 
msm_gem_submit *submit)
        }
 
 
-       trace_msm_gpu_submit_flush(submit,
-               gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER));
+       trace_msm_gpu_submit_flush(submit, read_gmu_ao_counter(a6xx_gpu));
 
        a6xx_flush(gpu, ring);
 
@@ -2260,16 +2271,7 @@ static int a6xx_gmu_get_timestamp(struct msm_gpu *gpu, 
uint64_t *value)
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
 
-       mutex_lock(&a6xx_gpu->gmu.lock);
-
-       /* Force the GPU power on so we can read this register */
-       a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET);
-
-       *value = gpu_read64(gpu, REG_A6XX_CP_ALWAYS_ON_COUNTER);
-
-       a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET);
-
-       mutex_unlock(&a6xx_gpu->gmu.lock);
+       *value = read_gmu_ao_counter(a6xx_gpu);
 
        return 0;
 }
-- 
2.51.0

Reply via email to