Expose GPU utilization via /sys/kernel/debug/dri/<N>/gpu_load. The file reports busy_time_ns, idle_time_ns and gpu_load %.
Values are snapshotted at the end of each devfreq sampling window (~50 ms) rather than read from live accumulators, avoiding load swings over short observation intervals. The snapshot is cleared on suspend to avoid stale data when the GPU is powered off. Signed-off-by: Guangliu Ding <[email protected]> --- Add /sys/kernel/debug/dri/<N>/gpu_load to expose GPU utilisation from the last devfreq sampling window (~50 ms). The file reports busy_time_ns, idle_time_ns and load percentage. Tested on NXP i.MX95 EVK (Mali-G310 V2). --- drivers/gpu/drm/panthor/panthor_devfreq.c | 80 +++++++++++++++++++++++++++++++ drivers/gpu/drm/panthor/panthor_devfreq.h | 6 +++ drivers/gpu/drm/panthor/panthor_drv.c | 2 + 3 files changed, 88 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c index 2249b41ca4af..1b3f58ddfbd3 100644 --- a/drivers/gpu/drm/panthor/panthor_devfreq.c +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c @@ -1,12 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 or MIT /* Copyright 2019 Collabora ltd. */ +/* Copyright 2026 NXP */ #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/devfreq.h> #include <linux/devfreq_cooling.h> +#include <linux/math64.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> +#include <drm/drm_file.h> #include <drm/drm_managed.h> #include <drm/drm_print.h> @@ -43,6 +47,22 @@ struct panthor_devfreq { * and panthor_devfreq_record_{busy,idle}(). */ spinlock_t lock; + +#ifdef CONFIG_DEBUG_FS + /** + * @last_busy_ns: Busy time in nanoseconds of the last completed devfreq window. + * Updated by panthor_devfreq_get_dev_status() before resetting the counters. + * Protected by @lock. + */ + u64 last_busy_ns; + + /** + * @last_total_ns: Total time in nanoseconds of the last completed devfreq window. + * Updated by panthor_devfreq_get_dev_status() before resetting the counters. + * Protected by @lock. + */ + u64 last_total_ns; +#endif }; static void panthor_devfreq_update_utilization(struct panthor_devfreq *pdevfreq) @@ -101,6 +121,11 @@ static int panthor_devfreq_get_dev_status(struct device *dev, status->busy_time = ktime_to_ns(pdevfreq->busy_time); +#ifdef CONFIG_DEBUG_FS + pdevfreq->last_busy_ns = status->busy_time; + pdevfreq->last_total_ns = status->total_time; +#endif + panthor_devfreq_reset(pdevfreq); spin_unlock_irqrestore(&pdevfreq->lock, irqflags); @@ -283,6 +308,17 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev) return; drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq)); + +#ifdef CONFIG_DEBUG_FS + { + unsigned long irqflags; + + spin_lock_irqsave(&pdevfreq->lock, irqflags); + pdevfreq->last_busy_ns = 0; + pdevfreq->last_total_ns = 0; + spin_unlock_irqrestore(&pdevfreq->lock, irqflags); + } +#endif } void panthor_devfreq_record_busy(struct panthor_device *ptdev) @@ -332,3 +368,47 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev) return freq; } + +#ifdef CONFIG_DEBUG_FS +static int panthor_devfreq_gpu_load_show(struct seq_file *m, void *unused) +{ + struct panthor_device *ptdev = m->private; + struct panthor_devfreq *pdevfreq = ptdev->devfreq; + unsigned long irqflags; + unsigned int gpu_load; + u64 total_ns; + u64 busy_ns; + + if (!pdevfreq->devfreq) { + seq_puts(m, "devfreq not initialized\n"); + return 0; + } + + spin_lock_irqsave(&pdevfreq->lock, irqflags); + busy_ns = pdevfreq->last_busy_ns; + total_ns = pdevfreq->last_total_ns; + spin_unlock_irqrestore(&pdevfreq->lock, irqflags); + + busy_ns = min(busy_ns, total_ns); + gpu_load = total_ns ? (unsigned int)div64_u64(busy_ns * 100ULL, total_ns) : 0; + + seq_printf(m, "busy_time_ns: %llu idle_time_ns: %llu gpu_load: %u%%\n", + busy_ns, total_ns - busy_ns, gpu_load); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(panthor_devfreq_gpu_load); + +/** + * panthor_devfreq_debugfs_init() - Initialize devfreq debugfs entries + * @minor: DRM minor. + */ +void panthor_devfreq_debugfs_init(struct drm_minor *minor) +{ + struct panthor_device *ptdev = container_of(minor->dev, + struct panthor_device, base); + + debugfs_create_file("gpu_load", 0444, minor->debugfs_root, ptdev, + &panthor_devfreq_gpu_load_fops); +} +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.h b/drivers/gpu/drm/panthor/panthor_devfreq.h index f8e29e02f66c..4552569abfe4 100644 --- a/drivers/gpu/drm/panthor/panthor_devfreq.h +++ b/drivers/gpu/drm/panthor/panthor_devfreq.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 or MIT */ /* Copyright 2019 Collabora ltd. */ +/* Copyright 2026 NXP */ #ifndef __PANTHOR_DEVFREQ_H__ #define __PANTHOR_DEVFREQ_H__ @@ -20,4 +21,9 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev); unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev); +#ifdef CONFIG_DEBUG_FS +struct drm_minor; +void panthor_devfreq_debugfs_init(struct drm_minor *minor); +#endif + #endif /* __PANTHOR_DEVFREQ_H__ */ diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index e8dc4096c1d2..4093a6d21337 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -2,6 +2,7 @@ /* Copyright 2018 Marty E. Plummer <[email protected]> */ /* Copyright 2019 Linaro, Ltd., Rob Herring <[email protected]> */ /* Copyright 2019 Collabora ltd. */ +/* Copyright 2026 NXP */ #ifdef CONFIG_ARM_ARCH_TIMER #include <asm/arch_timer.h> @@ -1761,6 +1762,7 @@ static void panthor_debugfs_init(struct drm_minor *minor) { panthor_mmu_debugfs_init(minor); panthor_gem_debugfs_init(minor); + panthor_devfreq_debugfs_init(minor); } #endif --- base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda change-id: 20260706-master-bd0eef99ca8e Best regards, -- Guangliu Ding <[email protected]>
