Re: [PATCH] drm/msm/adreno: keep the GPU timestamp monotonic across power collapse
From: Connor Abbott
Date: Fri Aug 28 2026 - 00:07:52 EST
On Thu, Aug 27, 2026 at 7:13 PM Dmitry Baryshkov
<dmitry.baryshkov@xxxxxxxxxxxxxxxx> wrote:
>
> The always-on counter behind MSM_PARAM_TIMESTAMP sits in the GPU power
> domain on a4xx, a5xx and the GMU-less a6xx parts, so it restarts from
> zero whenever the GPU is powered up again and the timestamp reported to
> userspace jumps backwards. On an a702 six reads three seconds apart all
> land in the 500..1200 tick range, stepping backwards twice, and on an
> a530 the OpenCL device timer conformance test fails because
> clGetDeviceAndHostTimer() returns an end time below the start time.
>
> Save the counter in the suspend path of the affected generations, while
> the GPU is still powered, and add the accumulated ticks to the value
> reported to userspace.
This is useless because the entire point of clGetDeviceAndHostTimer()
(and the similar thing in Vulkan) is to match what the GPU itself
returns, and now you've broken that by adding an offset.
Connor
>
> Assisted-by: LLM
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
> ---
> The always-on counter behind MSM_PARAM_TIMESTAMP sits in the GPU power
> domain on a4xx, a5xx and the GMU-less a6xx parts, so it restarts from zero
> whenever the GPU is powered up again and the timestamp userspace reads
> jumps backwards. Accumulate what the counter reached before each suspend
> and add it to what is reported afterwards.
>
> Measured on an a530 and an a702; the GMU parts keep their own counter alive
> and are left alone.
> ---
> drivers/gpu/drm/msm/adreno/a4xx_gpu.c | 2 ++
> drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 2 ++
> drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 3 +++
> drivers/gpu/drm/msm/adreno/adreno_gpu.c | 10 +++++++++-
> drivers/gpu/drm/msm/adreno/adreno_gpu.h | 3 +++
> 5 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
> index 6392126f48f2..7d9dd9460f5b 100644
> --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
> @@ -593,6 +593,8 @@ static int a4xx_pm_suspend(struct msm_gpu *gpu) {
> struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
> int ret;
>
> + adreno_save_timestamp(gpu);
> +
> ret = msm_gpu_pm_suspend(gpu);
> if (ret)
> return ret;
> diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> index f1df2514c613..c5552f1085e0 100644
> --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
> @@ -1423,6 +1423,8 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu)
> gpu_write(gpu, REG_A5XX_RBBM_BLOCK_SW_RESET_CMD, 0x00000000);
> }
>
> + adreno_save_timestamp(gpu);
> +
> ret = msm_gpu_pm_suspend(gpu);
> if (ret)
> return ret;
> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> index f9de9329dee3..106cc2ac55ff 100644
> --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
> @@ -2252,6 +2252,9 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu)
>
> trace_msm_gpu_suspend(0);
>
> + /* only the GMU-less parts come here, and their counter is in the GPU */
> + adreno_save_timestamp(gpu);
> +
> a6xx_llc_deactivate(a6xx_gpu);
>
> msm_devfreq_suspend(gpu);
> diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
> index 3370cd44382f..f83960b31901 100644
> --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
> +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
> @@ -226,6 +226,13 @@ adreno_iommu_create_vm(struct msm_gpu *gpu,
> return vm;
> }
>
> +void adreno_save_timestamp(struct msm_gpu *gpu)
> +{
> + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
> +
> + adreno_gpu->timestamp_offset += adreno_gpu->funcs->get_timestamp(gpu);
> +}
> +
> u64 adreno_private_vm_size(struct msm_gpu *gpu)
> {
> struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
> @@ -398,7 +405,8 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx,
> case MSM_PARAM_TIMESTAMP:
> if (adreno_gpu->funcs->get_timestamp) {
> pm_runtime_get_sync(&gpu->pdev->dev);
> - *value = adreno_gpu->funcs->get_timestamp(gpu);
> + *value = adreno_gpu->timestamp_offset +
> + adreno_gpu->funcs->get_timestamp(gpu);
> pm_runtime_put_autosuspend(&gpu->pdev->dev);
>
> return 0;
> diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> index 114a40f79ef3..db080a6d515c 100644
> --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
> @@ -207,6 +207,8 @@ struct adreno_gpu {
> uint16_t speedbin;
> const struct adreno_gpu_funcs *funcs;
>
> + u64 timestamp_offset;
> +
> struct completion fault_coredump_done;
>
> /* interesting register offsets to dump: */
> @@ -610,6 +612,7 @@ static inline int adreno_is_a840(struct adreno_gpu *gpu)
> /* Put vm_start above 32b to catch issues with not setting xyz_BASE_HI */
> #define ADRENO_VM_START 0x100000000ULL
> u64 adreno_private_vm_size(struct msm_gpu *gpu);
> +void adreno_save_timestamp(struct msm_gpu *gpu);
> int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx,
> uint32_t param, uint64_t *value, uint32_t *len);
> int adreno_set_param(struct msm_gpu *gpu, struct msm_context *ctx,
>
> ---
> base-commit: 140b13475302601368c0cf4e193e66126a49feb3
> change-id: 20260828-b4-adreno-timestamp-f4c846391b97
>
> Best regards,
> --
> With best wishes
> Dmitry
>