[PATCH 3/8] drm/msm/a6xx: Expose GMU IFPC entry count in debugfs

From: Konrad Dybcio

Date: Fri Oct 30 2026 - 06:12:25 EST


From: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>

Add a read-only debugfs file, ifpc_count, that reports how many times
the GMU has entered IFPC.

The count is accumulated from the GMU XOCLK_4 power counter. The
counter is selected in hw_init, and the delta is folded into the total
from the busy callback, so the value advances whenever devfreq samples
the GPU.

Signed-off-by: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/adreno/a6xx_debugfs.c | 23 +++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 30 ++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/adreno/a6xx_gpu.h | 10 ++++++++++
drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 9 +++++++++
5 files changed, 73 insertions(+)

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 1e246616f83f..afd42e5bda00 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -30,6 +30,7 @@ adreno-y := \
adreno/a8xx_preempt.o \

adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \
+ adreno/a6xx_debugfs.o \

adreno-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o

diff --git a/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c b/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c
new file mode 100644
index 000000000000..a3f622cb189f
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a6xx_debugfs.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/types.h>
+#include <linux/debugfs.h>
+
+#include <drm/drm_file.h>
+
+#include "a6xx_gpu.h"
+
+void a6xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ if (!minor)
+ return;
+
+ if (!(adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC))
+ return;
+
+ debugfs_create_u32("ifpc_count", 0444, minor->debugfs_root,
+ &a6xx_gpu->ifpc_count);
+}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 48d3ee4d55d9..e7c3da5d3ac6 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1338,6 +1338,11 @@ static int hw_init(struct msm_gpu *gpu)
GENMASK(7, 0), 0x4);
}

+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC) {
+ a6xx_gpu->ifpc_counter_last =
+ gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L);
+ }
+
/* Select CP0 to always count cycles */
gpu_write(gpu, REG_A6XX_CP_PERFCTR_CP_SEL(0), PERF_CP_ALWAYS_COUNT);

@@ -2334,6 +2339,17 @@ static void a6xx_destroy(struct msm_gpu *gpu)
kfree(a6xx_gpu);
}

+void a6xx_ifpc_count_update(struct a6xx_gpu *a6xx_gpu, u32 cur)
+{
+ /* A counter below the last sample means the GMU restarted it */
+ if (cur >= a6xx_gpu->ifpc_counter_last)
+ a6xx_gpu->ifpc_count += cur - a6xx_gpu->ifpc_counter_last;
+ else
+ a6xx_gpu->ifpc_count += cur;
+
+ a6xx_gpu->ifpc_counter_last = cur;
+}
+
static u64 a6xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
@@ -2347,6 +2363,11 @@ static u64 a6xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);

+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_ifpc_count_update(a6xx_gpu,
+ gmu_read(&a6xx_gpu->gmu,
+ REG_A6XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L));
+
return busy_cycles;
}

@@ -2818,6 +2839,9 @@ const struct adreno_gpu_funcs a6xx_gpu_funcs = {
#if defined(CONFIG_DRM_MSM_GPU_STATE)
.gpu_state_get = a6xx_gpu_state_get,
.gpu_state_put = a6xx_gpu_state_put,
+#endif
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
@@ -2889,6 +2913,9 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = {
#if defined(CONFIG_DRM_MSM_GPU_STATE)
.gpu_state_get = a6xx_gpu_state_get,
.gpu_state_put = a6xx_gpu_state_put,
+#endif
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
@@ -2921,6 +2948,9 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = {
.gpu_busy = a8xx_gpu_busy,
.gpu_get_freq = a6xx_gmu_get_freq,
.gpu_set_freq = a6xx_gpu_set_freq,
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a6xx_debugfs_init,
+#endif
.create_vm = a6xx_create_vm,
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 79912a33e17c..8f0cb168a535 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -139,6 +139,10 @@ struct a6xx_gpu {
spinlock_t aperture_lock;

u32 slice_mask;
+
+ /* Total GMU IFPC entries, accumulated from the XOCLK_4 counter */
+ u32 ifpc_count;
+ u32 ifpc_counter_last;
};

#define to_a6xx_gpu(x) container_of(x, struct a6xx_gpu, base)
@@ -254,6 +258,12 @@ struct a7xx_cp_smmu_info {
#define A6XX_PROTECT_RDONLY(_reg, _len) \
((((_len) & 0x3FFF) << 18) | ((_reg) & 0x3FFFF))

+#ifdef CONFIG_DEBUG_FS
+void a6xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor);
+#endif
+
+void a6xx_ifpc_count_update(struct a6xx_gpu *a6xx_gpu, u32 cur);
+
extern const struct adreno_gpu_funcs a6xx_gpu_funcs;
extern const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs;
extern const struct adreno_gpu_funcs a7xx_gpu_funcs;
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
index f892731cd3d3..ba81a1c9c551 100644
--- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -680,6 +680,10 @@ static int hw_init(struct msm_gpu *gpu)
gmu_write(&a6xx_gpu->gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_1,
FIELD_PREP(GENMASK(7, 0), 0x4));

+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_gpu->ifpc_counter_last =
+ gmu_read(gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L);
+
/* Select CP0 to always count cycles */
gpu_write(gpu, REG_A8XX_CP_PERFCTR_CP_SEL(0), 1);

@@ -1354,6 +1358,11 @@ u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);

+ if (adreno_gpu->info->quirks & ADRENO_QUIRK_IFPC)
+ a6xx_ifpc_count_update(a6xx_gpu,
+ gmu_read(&a6xx_gpu->gmu,
+ REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L));
+
return busy_cycles;
}


--
2.56.0