* [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:06 ` Boris Brezillon
2026-06-08 16:06 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
` (17 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
When CONFIG_NVMEM is disabled, reading the optional "shader-present"
nvmem cell can return -EOPNOTSUPP.
Treat this the same as the cell being absent so panthor can continue
probing on platforms that do not provide shader-present fuses through
nvmem.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_hw.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 7e315708ca7c..4c96573b649a 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -182,7 +182,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
&contents);
if (!ret)
ptdev->gpu_info.shader_present = contents;
- else if (ret == -ENOENT)
+ else if (ret == -ENOENT || ret == -EOPNOTSUPP)
return 0;
else
return dev_err_probe(ptdev->base.dev, ret,
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
2026-05-28 15:05 ` [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
@ 2026-06-08 13:06 ` Boris Brezillon
2026-06-08 16:06 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:06 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:29 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> When CONFIG_NVMEM is disabled, reading the optional "shader-present"
> nvmem cell can return -EOPNOTSUPP.
>
> Treat this the same as the cell being absent so panthor can continue
> probing on platforms that do not provide shader-present fuses through
> nvmem.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panthor/panthor_hw.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 7e315708ca7c..4c96573b649a 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -182,7 +182,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
> &contents);
> if (!ret)
> ptdev->gpu_info.shader_present = contents;
> - else if (ret == -ENOENT)
> + else if (ret == -ENOENT || ret == -EOPNOTSUPP)
> return 0;
> else
> return dev_err_probe(ptdev->base.dev, ret,
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
2026-05-28 15:05 ` [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
2026-06-08 13:06 ` Boris Brezillon
@ 2026-06-08 16:06 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-08 16:06 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> When CONFIG_NVMEM is disabled, reading the optional "shader-present"
> nvmem cell can return -EOPNOTSUPP.
>
> Treat this the same as the cell being absent so panthor can continue
> probing on platforms that do not provide shader-present fuses through
> nvmem.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Do we need a fixes too?
Fixes: c40b50c3cfbe ("drm/panthor: Implement reading shader_present from
nvmem")
Thanks,
Steve
> ---
> drivers/gpu/drm/panthor/panthor_hw.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 7e315708ca7c..4c96573b649a 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -182,7 +182,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
> &contents);
> if (!ret)
> ptdev->gpu_info.shader_present = contents;
> - else if (ret == -ENOENT)
> + else if (ret == -ENOENT || ret == -EOPNOTSUPP)
> return 0;
> else
> return dev_err_probe(ptdev->base.dev, ret,
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:07 ` Boris Brezillon
2026-06-10 15:50 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields Karunika Choo
` (16 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
panthor_device.h should describe the panthor device state, not provide
generic MMIO helpers.
Move the register accessors and poll-timeout wrappers to a dedicated
panthor_device_io.h header and include it from the users that need MMIO
access. This makes the dependency explicit and avoids pulling the full
device definition into code that only needs register helpers.
No functional change.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_device.h | 72 ------------------
drivers/gpu/drm/panthor/panthor_device_io.h | 81 +++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_fw.c | 1 +
drivers/gpu/drm/panthor/panthor_gpu.c | 1 +
drivers/gpu/drm/panthor/panthor_hw.c | 1 +
drivers/gpu/drm/panthor/panthor_mmu.c | 1 +
drivers/gpu/drm/panthor/panthor_pwr.c | 1 +
7 files changed, 86 insertions(+), 72 deletions(-)
create mode 100644 drivers/gpu/drm/panthor/panthor_device_io.h
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 4e4607bca7cc..c376e52e8564 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -6,7 +6,6 @@
#ifndef __PANTHOR_DEVICE_H__
#define __PANTHOR_DEVICE_H__
-#include <linux/atomic.h>
#include <linux/io-pgtable.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
@@ -630,75 +629,4 @@ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq
extern struct workqueue_struct *panthor_cleanup_wq;
-static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
-{
- writel(data, iomem + reg);
-}
-
-static inline u32 gpu_read(void __iomem *iomem, u32 reg)
-{
- return readl(iomem + reg);
-}
-
-static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
-{
- return readl_relaxed(iomem + reg);
-}
-
-static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
-{
- gpu_write(iomem, reg, lower_32_bits(data));
- gpu_write(iomem, reg + 4, upper_32_bits(data));
-}
-
-static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
-{
- return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
-}
-
-static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
-{
- return (gpu_read_relaxed(iomem, reg) |
- ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
-}
-
-static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
-{
- u32 lo, hi1, hi2;
- do {
- hi1 = gpu_read(iomem, reg + 4);
- lo = gpu_read(iomem, reg);
- hi2 = gpu_read(iomem, reg + 4);
- } while (hi1 != hi2);
- return lo | ((u64)hi2 << 32);
-}
-
-#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
- read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
- iomem, reg)
-
-#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
- timeout_us) \
- read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
- false, iomem, reg)
-
-#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
- read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
- iomem, reg)
-
-#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
- timeout_us) \
- read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
- false, iomem, reg)
-
-#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
- timeout_us) \
- read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
- timeout_us, false, iomem, reg)
-
-#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
- timeout_us) \
- read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
- false, iomem, reg)
-
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_device_io.h b/drivers/gpu/drm/panthor/panthor_device_io.h
new file mode 100644
index 000000000000..54e206c6aaa5
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_device_io.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_DEVICE_IO_H__
+#define __PANTHOR_DEVICE_IO_H__
+
+#include <linux/atomic.h>
+#include <linux/io.h>
+
+static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
+{
+ writel(data, iomem + reg);
+}
+
+static inline u32 gpu_read(void __iomem *iomem, u32 reg)
+{
+ return readl(iomem + reg);
+}
+
+static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
+{
+ return readl_relaxed(iomem + reg);
+}
+
+static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
+{
+ gpu_write(iomem, reg, lower_32_bits(data));
+ gpu_write(iomem, reg + 4, upper_32_bits(data));
+}
+
+static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
+{
+ return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
+}
+
+static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
+{
+ return (gpu_read_relaxed(iomem, reg) |
+ ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
+}
+
+static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
+{
+ u32 lo, hi1, hi2;
+ do {
+ hi1 = gpu_read(iomem, reg + 4);
+ lo = gpu_read(iomem, reg);
+ hi2 = gpu_read(iomem, reg + 4);
+ } while (hi1 != hi2);
+ return lo | ((u64)hi2 << 32);
+}
+
+#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
+ read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
+ iomem, reg)
+
+#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
+ timeout_us) \
+ read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
+ false, iomem, reg)
+
+#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
+ read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
+ iomem, reg)
+
+#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
+ timeout_us) \
+ read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
+ false, iomem, reg)
+
+#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
+ timeout_us) \
+ read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
+ timeout_us, false, iomem, reg)
+
+#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
+ timeout_us) \
+ read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
+ false, iomem, reg)
+
+#endif /* __PANTHOR_DEVICE_IO_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 986151681b24..52f176644aa6 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -19,6 +19,7 @@
#include <drm/drm_print.h>
#include "panthor_device.h"
+#include "panthor_device_io.h"
#include "panthor_fw.h"
#include "panthor_fw_regs.h"
#include "panthor_gem.h"
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index e52c5675981f..b63a33fe155e 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -18,6 +18,7 @@
#include <drm/drm_print.h>
#include "panthor_device.h"
+#include "panthor_device_io.h"
#include "panthor_gpu.h"
#include "panthor_gpu_regs.h"
#include "panthor_hw.h"
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 4c96573b649a..5cf54028f606 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -7,6 +7,7 @@
#include <drm/drm_print.h>
#include "panthor_device.h"
+#include "panthor_device_io.h"
#include "panthor_gpu.h"
#include "panthor_gpu_regs.h"
#include "panthor_hw.h"
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 452d0b6d4668..48127313332f 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -31,6 +31,7 @@
#include <linux/sizes.h>
#include "panthor_device.h"
+#include "panthor_device_io.h"
#include "panthor_gem.h"
#include "panthor_gpu.h"
#include "panthor_gpu_regs.h"
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
index 7c7f424a1436..1cba093a2452 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -11,6 +11,7 @@
#include <drm/drm_print.h>
#include "panthor_device.h"
+#include "panthor_device_io.h"
#include "panthor_gpu_regs.h"
#include "panthor_hw.h"
#include "panthor_pwr.h"
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h
2026-05-28 15:05 ` [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
@ 2026-06-08 13:07 ` Boris Brezillon
2026-06-10 15:50 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:07 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:30 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> panthor_device.h should describe the panthor device state, not provide
> generic MMIO helpers.
>
> Move the register accessors and poll-timeout wrappers to a dedicated
> panthor_device_io.h header and include it from the users that need MMIO
> access. This makes the dependency explicit and avoids pulling the full
> device definition into code that only needs register helpers.
>
> No functional change.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panthor/panthor_device.h | 72 ------------------
> drivers/gpu/drm/panthor/panthor_device_io.h | 81 +++++++++++++++++++++
> drivers/gpu/drm/panthor/panthor_fw.c | 1 +
> drivers/gpu/drm/panthor/panthor_gpu.c | 1 +
> drivers/gpu/drm/panthor/panthor_hw.c | 1 +
> drivers/gpu/drm/panthor/panthor_mmu.c | 1 +
> drivers/gpu/drm/panthor/panthor_pwr.c | 1 +
> 7 files changed, 86 insertions(+), 72 deletions(-)
> create mode 100644 drivers/gpu/drm/panthor/panthor_device_io.h
>
> diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
> index 4e4607bca7cc..c376e52e8564 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.h
> +++ b/drivers/gpu/drm/panthor/panthor_device.h
> @@ -6,7 +6,6 @@
> #ifndef __PANTHOR_DEVICE_H__
> #define __PANTHOR_DEVICE_H__
>
> -#include <linux/atomic.h>
> #include <linux/io-pgtable.h>
> #include <linux/regulator/consumer.h>
> #include <linux/pm_runtime.h>
> @@ -630,75 +629,4 @@ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq
>
> extern struct workqueue_struct *panthor_cleanup_wq;
>
> -static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
> -{
> - writel(data, iomem + reg);
> -}
> -
> -static inline u32 gpu_read(void __iomem *iomem, u32 reg)
> -{
> - return readl(iomem + reg);
> -}
> -
> -static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
> -{
> - return readl_relaxed(iomem + reg);
> -}
> -
> -static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
> -{
> - gpu_write(iomem, reg, lower_32_bits(data));
> - gpu_write(iomem, reg + 4, upper_32_bits(data));
> -}
> -
> -static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
> -{
> - return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
> -}
> -
> -static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
> -{
> - return (gpu_read_relaxed(iomem, reg) |
> - ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
> -}
> -
> -static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
> -{
> - u32 lo, hi1, hi2;
> - do {
> - hi1 = gpu_read(iomem, reg + 4);
> - lo = gpu_read(iomem, reg);
> - hi2 = gpu_read(iomem, reg + 4);
> - } while (hi1 != hi2);
> - return lo | ((u64)hi2 << 32);
> -}
> -
> -#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> - read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
> - iomem, reg)
> -
> -#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> -#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> - read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
> - iomem, reg)
> -
> -#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> -#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
> - timeout_us, false, iomem, reg)
> -
> -#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> #endif
> diff --git a/drivers/gpu/drm/panthor/panthor_device_io.h b/drivers/gpu/drm/panthor/panthor_device_io.h
> new file mode 100644
> index 000000000000..54e206c6aaa5
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_device_io.h
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0 or MIT */
> +/* Copyright 2026 ARM Limited. All rights reserved. */
> +
> +#ifndef __PANTHOR_DEVICE_IO_H__
> +#define __PANTHOR_DEVICE_IO_H__
> +
> +#include <linux/atomic.h>
> +#include <linux/io.h>
> +
> +static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
> +{
> + writel(data, iomem + reg);
> +}
> +
> +static inline u32 gpu_read(void __iomem *iomem, u32 reg)
> +{
> + return readl(iomem + reg);
> +}
> +
> +static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
> +{
> + return readl_relaxed(iomem + reg);
> +}
> +
> +static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
> +{
> + gpu_write(iomem, reg, lower_32_bits(data));
> + gpu_write(iomem, reg + 4, upper_32_bits(data));
> +}
> +
> +static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
> +{
> + return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
> +}
> +
> +static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
> +{
> + return (gpu_read_relaxed(iomem, reg) |
> + ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
> +}
> +
> +static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
> +{
> + u32 lo, hi1, hi2;
> + do {
> + hi1 = gpu_read(iomem, reg + 4);
> + lo = gpu_read(iomem, reg);
> + hi2 = gpu_read(iomem, reg + 4);
> + } while (hi1 != hi2);
> + return lo | ((u64)hi2 << 32);
> +}
> +
> +#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> + read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
> + iomem, reg)
> +
> +#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> + read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
> + iomem, reg)
> +
> +#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
> + timeout_us, false, iomem, reg)
> +
> +#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#endif /* __PANTHOR_DEVICE_IO_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 986151681b24..52f176644aa6 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -19,6 +19,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_fw.h"
> #include "panthor_fw_regs.h"
> #include "panthor_gem.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index e52c5675981f..b63a33fe155e 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 4c96573b649a..5cf54028f606 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -7,6 +7,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 452d0b6d4668..48127313332f 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -31,6 +31,7 @@
> #include <linux/sizes.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gem.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
> index 7c7f424a1436..1cba093a2452 100644
> --- a/drivers/gpu/drm/panthor/panthor_pwr.c
> +++ b/drivers/gpu/drm/panthor/panthor_pwr.c
> @@ -11,6 +11,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> #include "panthor_pwr.h"
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h
2026-05-28 15:05 ` [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
2026-06-08 13:07 ` Boris Brezillon
@ 2026-06-10 15:50 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-10 15:50 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> panthor_device.h should describe the panthor device state, not provide
> generic MMIO helpers.
>
> Move the register accessors and poll-timeout wrappers to a dedicated
> panthor_device_io.h header and include it from the users that need MMIO
> access. This makes the dependency explicit and avoids pulling the full
> device definition into code that only needs register helpers.
>
> No functional change.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> drivers/gpu/drm/panthor/panthor_device.h | 72 ------------------
> drivers/gpu/drm/panthor/panthor_device_io.h | 81 +++++++++++++++++++++
> drivers/gpu/drm/panthor/panthor_fw.c | 1 +
> drivers/gpu/drm/panthor/panthor_gpu.c | 1 +
> drivers/gpu/drm/panthor/panthor_hw.c | 1 +
> drivers/gpu/drm/panthor/panthor_mmu.c | 1 +
> drivers/gpu/drm/panthor/panthor_pwr.c | 1 +
> 7 files changed, 86 insertions(+), 72 deletions(-)
> create mode 100644 drivers/gpu/drm/panthor/panthor_device_io.h
>
> diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
> index 4e4607bca7cc..c376e52e8564 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.h
> +++ b/drivers/gpu/drm/panthor/panthor_device.h
> @@ -6,7 +6,6 @@
> #ifndef __PANTHOR_DEVICE_H__
> #define __PANTHOR_DEVICE_H__
>
> -#include <linux/atomic.h>
> #include <linux/io-pgtable.h>
> #include <linux/regulator/consumer.h>
> #include <linux/pm_runtime.h>
> @@ -630,75 +629,4 @@ static inline void panthor_ ## __name ## _irq_disable_events(struct panthor_irq
>
> extern struct workqueue_struct *panthor_cleanup_wq;
>
> -static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
> -{
> - writel(data, iomem + reg);
> -}
> -
> -static inline u32 gpu_read(void __iomem *iomem, u32 reg)
> -{
> - return readl(iomem + reg);
> -}
This is a bit ugly at the moment because further down in this file
you've still got calls to gpu_read() (in the PANTHOR_IRQ_HANDLER macro).
This of course does build but means that macros are broken unless
panthor_device_io.h is also included.
I think once Boris' change[1] to the IRQ macro machinery lands this will
resolve itself.
Thanks,
Steve
[1]
https://lore.kernel.org/all/20260512-panthor-signal-from-irq-v2-3-95c614a739cb@collabora.com/
> -
> -static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
> -{
> - return readl_relaxed(iomem + reg);
> -}
> -
> -static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
> -{
> - gpu_write(iomem, reg, lower_32_bits(data));
> - gpu_write(iomem, reg + 4, upper_32_bits(data));
> -}
> -
> -static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
> -{
> - return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
> -}
> -
> -static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
> -{
> - return (gpu_read_relaxed(iomem, reg) |
> - ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
> -}
> -
> -static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
> -{
> - u32 lo, hi1, hi2;
> - do {
> - hi1 = gpu_read(iomem, reg + 4);
> - lo = gpu_read(iomem, reg);
> - hi2 = gpu_read(iomem, reg + 4);
> - } while (hi1 != hi2);
> - return lo | ((u64)hi2 << 32);
> -}
> -
> -#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> - read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
> - iomem, reg)
> -
> -#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> -#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> - read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
> - iomem, reg)
> -
> -#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> -#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
> - timeout_us, false, iomem, reg)
> -
> -#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
> - timeout_us) \
> - read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
> - false, iomem, reg)
> -
> #endif
> diff --git a/drivers/gpu/drm/panthor/panthor_device_io.h b/drivers/gpu/drm/panthor/panthor_device_io.h
> new file mode 100644
> index 000000000000..54e206c6aaa5
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_device_io.h
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0 or MIT */
> +/* Copyright 2026 ARM Limited. All rights reserved. */
> +
> +#ifndef __PANTHOR_DEVICE_IO_H__
> +#define __PANTHOR_DEVICE_IO_H__
> +
> +#include <linux/atomic.h>
> +#include <linux/io.h>
> +
> +static inline void gpu_write(void __iomem *iomem, u32 reg, u32 data)
> +{
> + writel(data, iomem + reg);
> +}
> +
> +static inline u32 gpu_read(void __iomem *iomem, u32 reg)
> +{
> + return readl(iomem + reg);
> +}
> +
> +static inline u32 gpu_read_relaxed(void __iomem *iomem, u32 reg)
> +{
> + return readl_relaxed(iomem + reg);
> +}
> +
> +static inline void gpu_write64(void __iomem *iomem, u32 reg, u64 data)
> +{
> + gpu_write(iomem, reg, lower_32_bits(data));
> + gpu_write(iomem, reg + 4, upper_32_bits(data));
> +}
> +
> +static inline u64 gpu_read64(void __iomem *iomem, u32 reg)
> +{
> + return (gpu_read(iomem, reg) | ((u64)gpu_read(iomem, reg + 4) << 32));
> +}
> +
> +static inline u64 gpu_read64_relaxed(void __iomem *iomem, u32 reg)
> +{
> + return (gpu_read_relaxed(iomem, reg) |
> + ((u64)gpu_read_relaxed(iomem, reg + 4) << 32));
> +}
> +
> +static inline u64 gpu_read64_counter(void __iomem *iomem, u32 reg)
> +{
> + u32 lo, hi1, hi2;
> + do {
> + hi1 = gpu_read(iomem, reg + 4);
> + lo = gpu_read(iomem, reg);
> + hi2 = gpu_read(iomem, reg + 4);
> + } while (hi1 != hi2);
> + return lo | ((u64)hi2 << 32);
> +}
> +
> +#define gpu_read_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> + read_poll_timeout(gpu_read, val, cond, delay_us, timeout_us, false, \
> + iomem, reg)
> +
> +#define gpu_read_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#define gpu_read64_poll_timeout(iomem, reg, val, cond, delay_us, timeout_us) \
> + read_poll_timeout(gpu_read64, val, cond, delay_us, timeout_us, false, \
> + iomem, reg)
> +
> +#define gpu_read64_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read64, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#define gpu_read_relaxed_poll_timeout_atomic(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout_atomic(gpu_read_relaxed, val, cond, delay_us, \
> + timeout_us, false, iomem, reg)
> +
> +#define gpu_read64_relaxed_poll_timeout(iomem, reg, val, cond, delay_us, \
> + timeout_us) \
> + read_poll_timeout(gpu_read64_relaxed, val, cond, delay_us, timeout_us, \
> + false, iomem, reg)
> +
> +#endif /* __PANTHOR_DEVICE_IO_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 986151681b24..52f176644aa6 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -19,6 +19,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_fw.h"
> #include "panthor_fw_regs.h"
> #include "panthor_gem.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index e52c5675981f..b63a33fe155e 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> @@ -18,6 +18,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 4c96573b649a..5cf54028f606 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -7,6 +7,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 452d0b6d4668..48127313332f 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -31,6 +31,7 @@
> #include <linux/sizes.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gem.h"
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
> index 7c7f424a1436..1cba093a2452 100644
> --- a/drivers/gpu/drm/panthor/panthor_pwr.c
> +++ b/drivers/gpu/drm/panthor/panthor_pwr.c
> @@ -11,6 +11,7 @@
> #include <drm/drm_print.h>
>
> #include "panthor_device.h"
> +#include "panthor_device_io.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> #include "panthor_pwr.h"
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 01/18] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 02/18] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:08 ` Boris Brezillon
2026-06-10 15:57 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
` (15 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Prepare for Mali v15, where GPU_ID expands to 64 bits and its field
layout changes.
Introduce a small panthor_gpu_id structure and parse the GPU_ID fields
once during hardware initialization. Convert existing users to consume
the cached fields instead of extracting them repeatedly from the raw
register value.
This centralizes GPU_ID decoding and makes it easier to support both the
existing and v15 formats.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_device.h | 33 ++++++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_fw.c | 4 +--
drivers/gpu/drm/panthor/panthor_gpu.c | 2 +-
drivers/gpu/drm/panthor/panthor_hw.c | 26 +++++++++++++------
drivers/gpu/drm/panthor/panthor_hw.h | 3 +--
5 files changed, 55 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index c376e52e8564..8b2a9bb426fc 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -104,6 +104,36 @@ struct panthor_irq {
atomic_t state;
};
+/**
+ * struct panthor_gpu_id - Parsed GPU_ID fields
+ */
+struct panthor_gpu_id {
+ struct {
+ /** @arch.major: Architecture major revision */
+ u8 major;
+
+ /** @arch.minor: Architecture minor revision */
+ u8 minor;
+
+ /** @arch.rev: Architecture patch revision */
+ u8 rev;
+ } arch;
+
+ /** @prod_major: Product identifier */
+ u8 prod_major;
+
+ struct {
+ /** @ver.major: Major release version number */
+ u8 major;
+
+ /** @ver.minor: Minor release version number */
+ u8 minor;
+
+ /** @ver.status: Status of GPU release */
+ u8 status;
+ } ver;
+};
+
/**
* enum panthor_device_profiling_mode - Profiling state
*/
@@ -160,6 +190,9 @@ struct panthor_device {
/** @csif_info: Command stream interface information. */
struct drm_panthor_csif_info csif_info;
+ /** @gpu_id: Parsed GPU_ID fields */
+ struct panthor_gpu_id gpu_id;
+
/** @hw: GPU-specific data. */
struct panthor_hw *hw;
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 52f176644aa6..784d0a25beb2 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -799,8 +799,8 @@ static int panthor_fw_load(struct panthor_device *ptdev)
int ret;
snprintf(fw_path, sizeof(fw_path), "arm/mali/arch%d.%d/%s",
- (u32)GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id),
- (u32)GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id),
+ (u32)ptdev->gpu_id.arch.major,
+ (u32)ptdev->gpu_id.arch.minor,
CSF_FW_NAME);
ret = request_firmware(&fw, fw_path, ptdev->base.dev);
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index b63a33fe155e..bb5e64188a15 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -74,7 +74,7 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev)
if (!data || !data->asn_hash_enable)
return;
- if (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) < 11) {
+ if (ptdev->gpu_id.arch.major < 11) {
drm_err(&ptdev->base, "Custom ASN hash not supported by the device");
return;
}
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 5cf54028f606..3570e2889584 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -120,9 +120,9 @@ void panthor_hw_power_status_unregister(void)
static char *get_gpu_model_name(struct panthor_device *ptdev)
{
- const u32 gpu_id = ptdev->gpu_info.gpu_id;
- const u32 product_id = GPU_PROD_ID_MAKE(GPU_ARCH_MAJOR(gpu_id),
- GPU_PROD_MAJOR(gpu_id));
+ const struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
+ const u32 product_id = GPU_PROD_ID_MAKE(gpu_id->arch.major,
+ gpu_id->prod_major);
const bool ray_intersection = !!(ptdev->gpu_info.gpu_features &
GPU_FEATURES_RAY_INTERSECTION);
const u8 shader_core_count = hweight64(ptdev->gpu_info.shader_present);
@@ -246,13 +246,13 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
if (ret)
return ret;
- major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
- minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
- status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
+ major = ptdev->gpu_id.ver.major;
+ minor = ptdev->gpu_id.ver.minor;
+ status = ptdev->gpu_id.ver.status;
drm_info(&ptdev->base,
"%s id 0x%x major 0x%x minor 0x%x status 0x%x",
- get_gpu_model_name(ptdev), ptdev->gpu_info.gpu_id >> 16,
+ get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
major, minor, status);
drm_info(&ptdev->base,
@@ -274,7 +274,7 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
static int panthor_hw_bind_device(struct panthor_device *ptdev)
{
struct panthor_hw *hdev = NULL;
- const u32 arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
+ const u32 arch_major = ptdev->gpu_id.arch.major;
int i = 0;
for (i = 0; i < ARRAY_SIZE(panthor_hw_match); i++) {
@@ -296,10 +296,20 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
{
+ struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
+
if (!ptdev->gpu_info.gpu_id)
return -ENXIO;
+ gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
+ gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
+ gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
+ gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
+ gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
+ gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
+ gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
+
return 0;
}
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index f797663893b2..2a666d8cdb98 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -5,7 +5,6 @@
#define __PANTHOR_HW_H__
#include "panthor_device.h"
-#include "panthor_gpu_regs.h"
/**
* struct panthor_hw_ops - HW operations that are specific to a GPU
@@ -58,7 +57,7 @@ static inline void panthor_hw_l2_power_off(struct panthor_device *ptdev)
static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
{
- return GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14;
+ return ptdev->gpu_id.arch.major >= 14;
}
#endif /* __PANTHOR_HW_H__ */
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields
2026-05-28 15:05 ` [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields Karunika Choo
@ 2026-06-08 13:08 ` Boris Brezillon
2026-06-10 15:57 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:08 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:31 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> Prepare for Mali v15, where GPU_ID expands to 64 bits and its field
> layout changes.
>
> Introduce a small panthor_gpu_id structure and parse the GPU_ID fields
> once during hardware initialization. Convert existing users to consume
> the cached fields instead of extracting them repeatedly from the raw
> register value.
>
> This centralizes GPU_ID decoding and makes it easier to support both the
> existing and v15 formats.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panthor/panthor_device.h | 33 ++++++++++++++++++++++++
> drivers/gpu/drm/panthor/panthor_fw.c | 4 +--
> drivers/gpu/drm/panthor/panthor_gpu.c | 2 +-
> drivers/gpu/drm/panthor/panthor_hw.c | 26 +++++++++++++------
> drivers/gpu/drm/panthor/panthor_hw.h | 3 +--
> 5 files changed, 55 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
> index c376e52e8564..8b2a9bb426fc 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.h
> +++ b/drivers/gpu/drm/panthor/panthor_device.h
> @@ -104,6 +104,36 @@ struct panthor_irq {
> atomic_t state;
> };
>
> +/**
> + * struct panthor_gpu_id - Parsed GPU_ID fields
> + */
> +struct panthor_gpu_id {
> + struct {
> + /** @arch.major: Architecture major revision */
> + u8 major;
> +
> + /** @arch.minor: Architecture minor revision */
> + u8 minor;
> +
> + /** @arch.rev: Architecture patch revision */
> + u8 rev;
> + } arch;
> +
> + /** @prod_major: Product identifier */
> + u8 prod_major;
> +
> + struct {
> + /** @ver.major: Major release version number */
> + u8 major;
> +
> + /** @ver.minor: Minor release version number */
> + u8 minor;
> +
> + /** @ver.status: Status of GPU release */
> + u8 status;
> + } ver;
> +};
> +
> /**
> * enum panthor_device_profiling_mode - Profiling state
> */
> @@ -160,6 +190,9 @@ struct panthor_device {
> /** @csif_info: Command stream interface information. */
> struct drm_panthor_csif_info csif_info;
>
> + /** @gpu_id: Parsed GPU_ID fields */
> + struct panthor_gpu_id gpu_id;
> +
> /** @hw: GPU-specific data. */
> struct panthor_hw *hw;
>
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 52f176644aa6..784d0a25beb2 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -799,8 +799,8 @@ static int panthor_fw_load(struct panthor_device *ptdev)
> int ret;
>
> snprintf(fw_path, sizeof(fw_path), "arm/mali/arch%d.%d/%s",
> - (u32)GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id),
> - (u32)GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id),
> + (u32)ptdev->gpu_id.arch.major,
> + (u32)ptdev->gpu_id.arch.minor,
> CSF_FW_NAME);
>
> ret = request_firmware(&fw, fw_path, ptdev->base.dev);
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index b63a33fe155e..bb5e64188a15 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> @@ -74,7 +74,7 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev)
> if (!data || !data->asn_hash_enable)
> return;
>
> - if (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) < 11) {
> + if (ptdev->gpu_id.arch.major < 11) {
> drm_err(&ptdev->base, "Custom ASN hash not supported by the device");
> return;
> }
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 5cf54028f606..3570e2889584 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -120,9 +120,9 @@ void panthor_hw_power_status_unregister(void)
>
> static char *get_gpu_model_name(struct panthor_device *ptdev)
> {
> - const u32 gpu_id = ptdev->gpu_info.gpu_id;
> - const u32 product_id = GPU_PROD_ID_MAKE(GPU_ARCH_MAJOR(gpu_id),
> - GPU_PROD_MAJOR(gpu_id));
> + const struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> + const u32 product_id = GPU_PROD_ID_MAKE(gpu_id->arch.major,
> + gpu_id->prod_major);
> const bool ray_intersection = !!(ptdev->gpu_info.gpu_features &
> GPU_FEATURES_RAY_INTERSECTION);
> const u8 shader_core_count = hweight64(ptdev->gpu_info.shader_present);
> @@ -246,13 +246,13 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
> if (ret)
> return ret;
>
> - major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> - minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> - status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> + major = ptdev->gpu_id.ver.major;
> + minor = ptdev->gpu_id.ver.minor;
> + status = ptdev->gpu_id.ver.status;
>
> drm_info(&ptdev->base,
> "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
> - get_gpu_model_name(ptdev), ptdev->gpu_info.gpu_id >> 16,
> + get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
> major, minor, status);
>
> drm_info(&ptdev->base,
> @@ -274,7 +274,7 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
> static int panthor_hw_bind_device(struct panthor_device *ptdev)
> {
> struct panthor_hw *hdev = NULL;
> - const u32 arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> + const u32 arch_major = ptdev->gpu_id.arch.major;
> int i = 0;
>
> for (i = 0; i < ARRAY_SIZE(panthor_hw_match); i++) {
> @@ -296,10 +296,20 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
>
> static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
> {
> + struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
> +
> if (!ptdev->gpu_info.gpu_id)
> return -ENXIO;
>
> + gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
> + gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
> + gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> +
> return 0;
> }
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
> index f797663893b2..2a666d8cdb98 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.h
> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
> @@ -5,7 +5,6 @@
> #define __PANTHOR_HW_H__
>
> #include "panthor_device.h"
> -#include "panthor_gpu_regs.h"
>
> /**
> * struct panthor_hw_ops - HW operations that are specific to a GPU
> @@ -58,7 +57,7 @@ static inline void panthor_hw_l2_power_off(struct panthor_device *ptdev)
>
> static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
> {
> - return GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14;
> + return ptdev->gpu_id.arch.major >= 14;
> }
>
> #endif /* __PANTHOR_HW_H__ */
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields
2026-05-28 15:05 ` [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields Karunika Choo
2026-06-08 13:08 ` Boris Brezillon
@ 2026-06-10 15:57 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-10 15:57 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> Prepare for Mali v15, where GPU_ID expands to 64 bits and its field
> layout changes.
>
> Introduce a small panthor_gpu_id structure and parse the GPU_ID fields
> once during hardware initialization. Convert existing users to consume
> the cached fields instead of extracting them repeatedly from the raw
> register value.
>
> This centralizes GPU_ID decoding and makes it easier to support both the
> existing and v15 formats.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> drivers/gpu/drm/panthor/panthor_device.h | 33 ++++++++++++++++++++++++
> drivers/gpu/drm/panthor/panthor_fw.c | 4 +--
> drivers/gpu/drm/panthor/panthor_gpu.c | 2 +-
> drivers/gpu/drm/panthor/panthor_hw.c | 26 +++++++++++++------
> drivers/gpu/drm/panthor/panthor_hw.h | 3 +--
> 5 files changed, 55 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
> index c376e52e8564..8b2a9bb426fc 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.h
> +++ b/drivers/gpu/drm/panthor/panthor_device.h
> @@ -104,6 +104,36 @@ struct panthor_irq {
> atomic_t state;
> };
>
> +/**
> + * struct panthor_gpu_id - Parsed GPU_ID fields
> + */
> +struct panthor_gpu_id {
> + struct {
> + /** @arch.major: Architecture major revision */
> + u8 major;
> +
> + /** @arch.minor: Architecture minor revision */
> + u8 minor;
> +
> + /** @arch.rev: Architecture patch revision */
> + u8 rev;
> + } arch;
> +
> + /** @prod_major: Product identifier */
> + u8 prod_major;
> +
> + struct {
> + /** @ver.major: Major release version number */
> + u8 major;
> +
> + /** @ver.minor: Minor release version number */
> + u8 minor;
> +
> + /** @ver.status: Status of GPU release */
> + u8 status;
> + } ver;
NIT: I'm not sure what the point of these nested structs is - it seems
inconsistent to have arch.major but prod_major. Is there any good reason
not to just collapse this:
struct panthor_gpu_id {
/** @arch_major: Architecture major revision */
u8 arch_major;
/** @arch_minor: Architecture minor revision */
u8 arch_minor;
/** @arch_rev: Architecture patch revision */
u8 arch_rev;
/** @prod_major: Product identifier */
u8 prod_major;
/** @ver_major: Major release version number */
u8 ver_major;
/** @ver_minor: Minor release version number */
u8 ver_minor;
/** @ver_status: Status of GPU release */
u8 ver_status;
};
Thanks,
Steve
> +};
> +
> /**
> * enum panthor_device_profiling_mode - Profiling state
> */
> @@ -160,6 +190,9 @@ struct panthor_device {
> /** @csif_info: Command stream interface information. */
> struct drm_panthor_csif_info csif_info;
>
> + /** @gpu_id: Parsed GPU_ID fields */
> + struct panthor_gpu_id gpu_id;
> +
> /** @hw: GPU-specific data. */
> struct panthor_hw *hw;
>
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 52f176644aa6..784d0a25beb2 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -799,8 +799,8 @@ static int panthor_fw_load(struct panthor_device *ptdev)
> int ret;
>
> snprintf(fw_path, sizeof(fw_path), "arm/mali/arch%d.%d/%s",
> - (u32)GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id),
> - (u32)GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id),
> + (u32)ptdev->gpu_id.arch.major,
> + (u32)ptdev->gpu_id.arch.minor,
> CSF_FW_NAME);
>
> ret = request_firmware(&fw, fw_path, ptdev->base.dev);
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index b63a33fe155e..bb5e64188a15 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> @@ -74,7 +74,7 @@ static void panthor_gpu_l2_config_set(struct panthor_device *ptdev)
> if (!data || !data->asn_hash_enable)
> return;
>
> - if (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) < 11) {
> + if (ptdev->gpu_id.arch.major < 11) {
> drm_err(&ptdev->base, "Custom ASN hash not supported by the device");
> return;
> }
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 5cf54028f606..3570e2889584 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -120,9 +120,9 @@ void panthor_hw_power_status_unregister(void)
>
> static char *get_gpu_model_name(struct panthor_device *ptdev)
> {
> - const u32 gpu_id = ptdev->gpu_info.gpu_id;
> - const u32 product_id = GPU_PROD_ID_MAKE(GPU_ARCH_MAJOR(gpu_id),
> - GPU_PROD_MAJOR(gpu_id));
> + const struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> + const u32 product_id = GPU_PROD_ID_MAKE(gpu_id->arch.major,
> + gpu_id->prod_major);
> const bool ray_intersection = !!(ptdev->gpu_info.gpu_features &
> GPU_FEATURES_RAY_INTERSECTION);
> const u8 shader_core_count = hweight64(ptdev->gpu_info.shader_present);
> @@ -246,13 +246,13 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
> if (ret)
> return ret;
>
> - major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> - minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> - status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> + major = ptdev->gpu_id.ver.major;
> + minor = ptdev->gpu_id.ver.minor;
> + status = ptdev->gpu_id.ver.status;
>
> drm_info(&ptdev->base,
> "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
> - get_gpu_model_name(ptdev), ptdev->gpu_info.gpu_id >> 16,
> + get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
> major, minor, status);
>
> drm_info(&ptdev->base,
> @@ -274,7 +274,7 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
> static int panthor_hw_bind_device(struct panthor_device *ptdev)
> {
> struct panthor_hw *hdev = NULL;
> - const u32 arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> + const u32 arch_major = ptdev->gpu_id.arch.major;
> int i = 0;
>
> for (i = 0; i < ARRAY_SIZE(panthor_hw_match); i++) {
> @@ -296,10 +296,20 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
>
> static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
> {
> + struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
> +
> if (!ptdev->gpu_info.gpu_id)
> return -ENXIO;
>
> + gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
> + gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
> + gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> + gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> +
> return 0;
> }
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
> index f797663893b2..2a666d8cdb98 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.h
> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
> @@ -5,7 +5,6 @@
> #define __PANTHOR_HW_H__
>
> #include "panthor_device.h"
> -#include "panthor_gpu_regs.h"
>
> /**
> * struct panthor_hw_ops - HW operations that are specific to a GPU
> @@ -58,7 +57,7 @@ static inline void panthor_hw_l2_power_off(struct panthor_device *ptdev)
>
> static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
> {
> - return GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14;
> + return ptdev->gpu_id.arch.major >= 14;
> }
>
> #endif /* __PANTHOR_HW_H__ */
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (2 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 03/18] drm/panthor: Parse and store GPU_ID fields Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:28 ` Boris Brezillon
2026-06-10 16:02 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description Karunika Choo
` (14 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Mali v15 exposes a 64-bit GPU_ID register with a different field layout
from earlier GPUs.
Add the register definitions and decoding helpers for the new format,
read GPU_WIDE_ID when the compatibility value indicates a v15 GPU, and
populate both the cached GPU_ID fields and the uAPI gpu_id_hi field.
This allows userspace and the driver to identify v15 GPUs correctly.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
.../drm/panthor/panthor_gpu_discover_regs.h | 17 +++++++++
drivers/gpu/drm/panthor/panthor_hw.c | 38 ++++++++++++++-----
include/uapi/drm/panthor_drm.h | 10 +++++
3 files changed, 56 insertions(+), 9 deletions(-)
create mode 100644 drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
new file mode 100644
index 000000000000..54bb104902ee
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_GPU_DISCOVER_REGS_H__
+#define __PANTHOR_GPU_DISCOVER_REGS_H__
+
+#define GPU_WIDE_ID 0x0
+#define GPU_WIDE_COMPAT 0xF
+#define GPU_WIDE_ARCH_MAJOR(x) (((x) & GENMASK(63, 56)) >> 56)
+#define GPU_WIDE_ARCH_MINOR(x) (((x) & GENMASK(55, 48)) >> 48)
+#define GPU_WIDE_ARCH_REV(x) (((x) & GENMASK(47, 40)) >> 40)
+#define GPU_WIDE_PROD_MAJOR(x) (((x) & GENMASK(39, 32)) >> 32)
+#define GPU_WIDE_VER_MAJOR(x) (((x) & GENMASK(23, 16)) >> 16)
+#define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
+#define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
+
+#endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 3570e2889584..833d10cbb2d6 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -9,6 +9,7 @@
#include "panthor_device.h"
#include "panthor_device_io.h"
#include "panthor_gpu.h"
+#include "panthor_gpu_discover_regs.h"
#include "panthor_gpu_regs.h"
#include "panthor_hw.h"
#include "panthor_pwr.h"
@@ -297,18 +298,37 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
{
struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
- ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
+ u32 gpu_id32 = gpu_read(ptdev->iomem, GPU_ID);
- if (!ptdev->gpu_info.gpu_id)
+ if (!gpu_id32)
return -ENXIO;
- gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
- gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
- gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
- gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
- gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
- gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
- gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
+ ptdev->gpu_info.gpu_id = gpu_id32;
+
+ gpu_id->arch.major = GPU_ARCH_MAJOR(gpu_id32);
+ gpu_id->arch.minor = GPU_ARCH_MINOR(gpu_id32);
+ gpu_id->arch.rev = GPU_ARCH_REV(gpu_id32);
+ gpu_id->prod_major = GPU_PROD_MAJOR(gpu_id32);
+ gpu_id->ver.major = GPU_VER_MAJOR(gpu_id32);
+ gpu_id->ver.minor = GPU_VER_MINOR(gpu_id32);
+ gpu_id->ver.status = GPU_VER_STATUS(gpu_id32);
+
+ if (GPU_ARCH_MAJOR(gpu_id32) == GPU_WIDE_COMPAT) {
+ u64 gpu_id64 = gpu_read64(ptdev->iomem, GPU_WIDE_ID);
+ if (!gpu_id64)
+ return -ENXIO;
+
+ ptdev->gpu_info.gpu_wide_id = gpu_id64;
+ ptdev->gpu_info.gpu_id = 0;
+
+ gpu_id->arch.major = GPU_WIDE_ARCH_MAJOR(gpu_id64);
+ gpu_id->arch.minor = GPU_WIDE_ARCH_MINOR(gpu_id64);
+ gpu_id->arch.rev = GPU_WIDE_ARCH_REV(gpu_id64);
+ gpu_id->prod_major = GPU_WIDE_PROD_MAJOR(gpu_id64);
+ gpu_id->ver.major = GPU_WIDE_VER_MAJOR(gpu_id64);
+ gpu_id->ver.minor = GPU_WIDE_VER_MINOR(gpu_id64);
+ gpu_id->ver.status = GPU_WIDE_VER_STATUS(gpu_id64);
+ }
return 0;
}
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index 0e455d91e77d..04fc9f133152 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -373,6 +373,16 @@ struct drm_panthor_gpu_info {
/** @gpu_features: Bitmask describing supported GPU-wide features */
__u64 gpu_features;
+
+ /** @gpu_wide_id: 64-bit GPU_ID for v15 onwards. */
+ __u64 gpu_wide_id;
+#define DRM_PANTHOR_WIDE_ARCH_MAJOR(x) (((x) >> 56) & 0xff)
+#define DRM_PANTHOR_WIDE_ARCH_MINOR(x) (((x) >> 48) & 0xff)
+#define DRM_PANTHOR_WIDE_ARCH_REV(x) (((x) >> 40) & 0xff)
+#define DRM_PANTHOR_WIDE_PRODUCT_MAJOR(x) (((x) >> 32) & 0xff)
+#define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
+#define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
+#define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
};
/**
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs
2026-05-28 15:05 ` [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
@ 2026-06-08 13:28 ` Boris Brezillon
2026-06-10 16:02 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:28 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:32 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> Mali v15 exposes a 64-bit GPU_ID register with a different field layout
> from earlier GPUs.
>
> Add the register definitions and decoding helpers for the new format,
> read GPU_WIDE_ID when the compatibility value indicates a v15 GPU, and
> populate both the cached GPU_ID fields and the uAPI gpu_id_hi field.
>
> This allows userspace and the driver to identify v15 GPUs correctly.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> .../drm/panthor/panthor_gpu_discover_regs.h | 17 +++++++++
> drivers/gpu/drm/panthor/panthor_hw.c | 38 ++++++++++++++-----
> include/uapi/drm/panthor_drm.h | 10 +++++
> 3 files changed, 56 insertions(+), 9 deletions(-)
> create mode 100644 drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> new file mode 100644
> index 000000000000..54bb104902ee
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 or MIT */
> +/* Copyright 2026 ARM Limited. All rights reserved. */
> +
> +#ifndef __PANTHOR_GPU_DISCOVER_REGS_H__
> +#define __PANTHOR_GPU_DISCOVER_REGS_H__
> +
> +#define GPU_WIDE_ID 0x0
> +#define GPU_WIDE_COMPAT 0xF
> +#define GPU_WIDE_ARCH_MAJOR(x) (((x) & GENMASK(63, 56)) >> 56)
> +#define GPU_WIDE_ARCH_MINOR(x) (((x) & GENMASK(55, 48)) >> 48)
> +#define GPU_WIDE_ARCH_REV(x) (((x) & GENMASK(47, 40)) >> 40)
> +#define GPU_WIDE_PROD_MAJOR(x) (((x) & GENMASK(39, 32)) >> 32)
> +#define GPU_WIDE_VER_MAJOR(x) (((x) & GENMASK(23, 16)) >> 16)
> +#define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
> +#define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
> +
> +#endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 3570e2889584..833d10cbb2d6 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -9,6 +9,7 @@
> #include "panthor_device.h"
> #include "panthor_device_io.h"
> #include "panthor_gpu.h"
> +#include "panthor_gpu_discover_regs.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> #include "panthor_pwr.h"
> @@ -297,18 +298,37 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
> static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
> {
> struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> - ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
> + u32 gpu_id32 = gpu_read(ptdev->iomem, GPU_ID);
>
> - if (!ptdev->gpu_info.gpu_id)
> + if (!gpu_id32)
> return -ENXIO;
>
> - gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
> - gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
> - gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> + ptdev->gpu_info.gpu_id = gpu_id32;
> +
> + gpu_id->arch.major = GPU_ARCH_MAJOR(gpu_id32);
> + gpu_id->arch.minor = GPU_ARCH_MINOR(gpu_id32);
> + gpu_id->arch.rev = GPU_ARCH_REV(gpu_id32);
> + gpu_id->prod_major = GPU_PROD_MAJOR(gpu_id32);
> + gpu_id->ver.major = GPU_VER_MAJOR(gpu_id32);
> + gpu_id->ver.minor = GPU_VER_MINOR(gpu_id32);
> + gpu_id->ver.status = GPU_VER_STATUS(gpu_id32);
> +
> + if (GPU_ARCH_MAJOR(gpu_id32) == GPU_WIDE_COMPAT) {
> + u64 gpu_id64 = gpu_read64(ptdev->iomem, GPU_WIDE_ID);
I know we currently assume that DISCOVER regs on v15 and GPU regs on
older gens are starting at zero, but I'd prefer to make this explicit
with something like:
/* DISCOVER register block always starts at offset
* zero, so does the GPU register block on older
* GPUs.
*/
void __iomem *discover = ptdev->iomem;
u64 gpu_id64 = gpu_read64(discover, GPU_WIDE_ID);
> + if (!gpu_id64)
> + return -ENXIO;
> +
> + ptdev->gpu_info.gpu_wide_id = gpu_id64;
> + ptdev->gpu_info.gpu_id = 0;
> +
> + gpu_id->arch.major = GPU_WIDE_ARCH_MAJOR(gpu_id64);
> + gpu_id->arch.minor = GPU_WIDE_ARCH_MINOR(gpu_id64);
> + gpu_id->arch.rev = GPU_WIDE_ARCH_REV(gpu_id64);
> + gpu_id->prod_major = GPU_WIDE_PROD_MAJOR(gpu_id64);
> + gpu_id->ver.major = GPU_WIDE_VER_MAJOR(gpu_id64);
> + gpu_id->ver.minor = GPU_WIDE_VER_MINOR(gpu_id64);
> + gpu_id->ver.status = GPU_WIDE_VER_STATUS(gpu_id64);
> + }
I'd probably move the !has_wide_id() logic to an else branch instead of
assigning unconditionally, and then fixing up if has_wide_id() is true:
if (GPU_ARCH_MAJOR(gpu_id32) == GPU_WIDE_COMPAT) {
u64 gpu_id64 = gpu_read64(ptdev->iomem, GPU_WIDE_ID);
if (!gpu_id64)
return -ENXIO;
ptdev->gpu_info.gpu_wide_id = gpu_id64;
gpu_id->arch.major = GPU_WIDE_ARCH_MAJOR(gpu_id64);
gpu_id->arch.minor = GPU_WIDE_ARCH_MINOR(gpu_id64);
gpu_id->arch.rev = GPU_WIDE_ARCH_REV(gpu_id64);
gpu_id->prod_major = GPU_WIDE_PROD_MAJOR(gpu_id64);
gpu_id->ver.major = GPU_WIDE_VER_MAJOR(gpu_id64);
gpu_id->ver.minor = GPU_WIDE_VER_MINOR(gpu_id64);
gpu_id->ver.status = GPU_WIDE_VER_STATUS(gpu_id64);
} else {
ptdev->gpu_info.gpu_id = gpu_id32;
gpu_id->arch.major = GPU_ARCH_MAJOR(gpu_id32);
gpu_id->arch.minor = GPU_ARCH_MINOR(gpu_id32);
gpu_id->arch.rev = GPU_ARCH_REV(gpu_id32);
gpu_id->prod_major = GPU_PROD_MAJOR(gpu_id32);
gpu_id->ver.major = GPU_VER_MAJOR(gpu_id32);
gpu_id->ver.minor = GPU_VER_MINOR(gpu_id32);
gpu_id->ver.status = GPU_VER_STATUS(gpu_id32);
}
>
> return 0;
> }
> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index 0e455d91e77d..04fc9f133152 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -373,6 +373,16 @@ struct drm_panthor_gpu_info {
>
> /** @gpu_features: Bitmask describing supported GPU-wide features */
> __u64 gpu_features;
> +
> + /** @gpu_wide_id: 64-bit GPU_ID for v15 onwards. */
Specify that it's zero on v14- HW, and also update the gpu_id doc to
mention it will be zero on v15+.
> + __u64 gpu_wide_id;
> +#define DRM_PANTHOR_WIDE_ARCH_MAJOR(x) (((x) >> 56) & 0xff)
> +#define DRM_PANTHOR_WIDE_ARCH_MINOR(x) (((x) >> 48) & 0xff)
> +#define DRM_PANTHOR_WIDE_ARCH_REV(x) (((x) >> 40) & 0xff)
> +#define DRM_PANTHOR_WIDE_PRODUCT_MAJOR(x) (((x) >> 32) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
> };
>
> /**
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs
2026-05-28 15:05 ` [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
2026-06-08 13:28 ` Boris Brezillon
@ 2026-06-10 16:02 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-10 16:02 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> Mali v15 exposes a 64-bit GPU_ID register with a different field layout
> from earlier GPUs.
>
> Add the register definitions and decoding helpers for the new format,
> read GPU_WIDE_ID when the compatibility value indicates a v15 GPU, and
> populate both the cached GPU_ID fields and the uAPI gpu_id_hi field.
s/gpu_id_hi/gpu_wide_id/
>
> This allows userspace and the driver to identify v15 GPUs correctly.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> .../drm/panthor/panthor_gpu_discover_regs.h | 17 +++++++++
> drivers/gpu/drm/panthor/panthor_hw.c | 38 ++++++++++++++-----
> include/uapi/drm/panthor_drm.h | 10 +++++
> 3 files changed, 56 insertions(+), 9 deletions(-)
> create mode 100644 drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> new file mode 100644
> index 000000000000..54bb104902ee
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 or MIT */
> +/* Copyright 2026 ARM Limited. All rights reserved. */
> +
> +#ifndef __PANTHOR_GPU_DISCOVER_REGS_H__
> +#define __PANTHOR_GPU_DISCOVER_REGS_H__
> +
> +#define GPU_WIDE_ID 0x0
> +#define GPU_WIDE_COMPAT 0xF
> +#define GPU_WIDE_ARCH_MAJOR(x) (((x) & GENMASK(63, 56)) >> 56)
> +#define GPU_WIDE_ARCH_MINOR(x) (((x) & GENMASK(55, 48)) >> 48)
> +#define GPU_WIDE_ARCH_REV(x) (((x) & GENMASK(47, 40)) >> 40)
> +#define GPU_WIDE_PROD_MAJOR(x) (((x) & GENMASK(39, 32)) >> 32)
> +#define GPU_WIDE_VER_MAJOR(x) (((x) & GENMASK(23, 16)) >> 16)
> +#define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
> +#define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
I think you need GENMASK_U64 here - otherwise this is going to cause
problems on a 32 bit build (yes I know, but apparently people do still
use 32 bits - at least as a COMPILE_TEST target).
> +
> +#endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 3570e2889584..833d10cbb2d6 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -9,6 +9,7 @@
> #include "panthor_device.h"
> #include "panthor_device_io.h"
> #include "panthor_gpu.h"
> +#include "panthor_gpu_discover_regs.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_hw.h"
> #include "panthor_pwr.h"
> @@ -297,18 +298,37 @@ static int panthor_hw_bind_device(struct panthor_device *ptdev)
> static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
> {
> struct panthor_gpu_id *gpu_id = &ptdev->gpu_id;
> - ptdev->gpu_info.gpu_id = gpu_read(ptdev->iomem, GPU_ID);
> + u32 gpu_id32 = gpu_read(ptdev->iomem, GPU_ID);
>
> - if (!ptdev->gpu_info.gpu_id)
> + if (!gpu_id32)
> return -ENXIO;
>
> - gpu_id->arch.major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->arch.minor = GPU_ARCH_MINOR(ptdev->gpu_info.gpu_id);
> - gpu_id->arch.rev = GPU_ARCH_REV(ptdev->gpu_info.gpu_id);
> - gpu_id->prod_major = GPU_PROD_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.major = GPU_VER_MAJOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.minor = GPU_VER_MINOR(ptdev->gpu_info.gpu_id);
> - gpu_id->ver.status = GPU_VER_STATUS(ptdev->gpu_info.gpu_id);
> + ptdev->gpu_info.gpu_id = gpu_id32;
> +
> + gpu_id->arch.major = GPU_ARCH_MAJOR(gpu_id32);
> + gpu_id->arch.minor = GPU_ARCH_MINOR(gpu_id32);
> + gpu_id->arch.rev = GPU_ARCH_REV(gpu_id32);
> + gpu_id->prod_major = GPU_PROD_MAJOR(gpu_id32);
> + gpu_id->ver.major = GPU_VER_MAJOR(gpu_id32);
> + gpu_id->ver.minor = GPU_VER_MINOR(gpu_id32);
> + gpu_id->ver.status = GPU_VER_STATUS(gpu_id32);
> +
> + if (GPU_ARCH_MAJOR(gpu_id32) == GPU_WIDE_COMPAT) {
> + u64 gpu_id64 = gpu_read64(ptdev->iomem, GPU_WIDE_ID);
> + if (!gpu_id64)
> + return -ENXIO;
> +
> + ptdev->gpu_info.gpu_wide_id = gpu_id64;
> + ptdev->gpu_info.gpu_id = 0;
> +
> + gpu_id->arch.major = GPU_WIDE_ARCH_MAJOR(gpu_id64);
> + gpu_id->arch.minor = GPU_WIDE_ARCH_MINOR(gpu_id64);
> + gpu_id->arch.rev = GPU_WIDE_ARCH_REV(gpu_id64);
> + gpu_id->prod_major = GPU_WIDE_PROD_MAJOR(gpu_id64);
> + gpu_id->ver.major = GPU_WIDE_VER_MAJOR(gpu_id64);
> + gpu_id->ver.minor = GPU_WIDE_VER_MINOR(gpu_id64);
> + gpu_id->ver.status = GPU_WIDE_VER_STATUS(gpu_id64);
> + }
It seems odd to assign a bunch of wrong values to then detect the
WIDE_COMPAT marker afterwards. Wouldn't it be clearer to write:
if (GPU_ARCH_MAJOR(gpu_id32) != GPU_WIDE_COMPAT) {
ptdev->gpu_info.gpu_id = gpu_id32;
gpu_id->arch.major = GPU_ARCH_MAJOR(gpu_id32);
gpu_id->arch.minor = GPU_ARCH_MINOR(gpu_id32);
gpu_id->arch.rev = GPU_ARCH_REV(gpu_id32);
gpu_id->prod_major = GPU_PROD_MAJOR(gpu_id32);
gpu_id->ver.major = GPU_VER_MAJOR(gpu_id32);
gpu_id->ver.minor = GPU_VER_MINOR(gpu_id32);
gpu_id->ver.status = GPU_VER_STATUS(gpu_id32);
} else {
u64 gpu_id64 = gpu_read64(ptdev->iomem, GPU_WIDE_ID);
if (!gpu_id64)
return -ENXIO;
ptdev->gpu_info.gpu_wide_id = gpu_id64;
gpu_id->arch.major = GPU_WIDE_ARCH_MAJOR(gpu_id64);
gpu_id->arch.minor = GPU_WIDE_ARCH_MINOR(gpu_id64);
gpu_id->arch.rev = GPU_WIDE_ARCH_REV(gpu_id64);
gpu_id->prod_major = GPU_WIDE_PROD_MAJOR(gpu_id64);
gpu_id->ver.major = GPU_WIDE_VER_MAJOR(gpu_id64);
gpu_id->ver.minor = GPU_WIDE_VER_MINOR(gpu_id64);
gpu_id->ver.status = GPU_WIDE_VER_STATUS(gpu_id64);
}
>
> return 0;
> }
> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index 0e455d91e77d..04fc9f133152 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -373,6 +373,16 @@ struct drm_panthor_gpu_info {
>
> /** @gpu_features: Bitmask describing supported GPU-wide features */
> __u64 gpu_features;
> +
> + /** @gpu_wide_id: 64-bit GPU_ID for v15 onwards. */
> + __u64 gpu_wide_id;
I think it's worth a comment explaining how user space is meant to know
that gpu_wide_id is in use. I presume this is by looking at gpu_id and
seing it's zero?
We might also want a version bump in panthor_drm_driver so user space
knows that this is available (but that can be when all v15 support is in
place).
Thanks,
Steve
> +#define DRM_PANTHOR_WIDE_ARCH_MAJOR(x) (((x) >> 56) & 0xff)
> +#define DRM_PANTHOR_WIDE_ARCH_MINOR(x) (((x) >> 48) & 0xff)
> +#define DRM_PANTHOR_WIDE_ARCH_REV(x) (((x) >> 40) & 0xff)
> +#define DRM_PANTHOR_WIDE_PRODUCT_MAJOR(x) (((x) >> 32) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
> +#define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
> };
>
> /**
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (3 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 04/18] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:32 ` Boris Brezillon
2026-05-28 15:05 ` [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
` (13 subsequent siblings)
18 siblings, 1 reply; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Mali v15 reorders several register regions, including GPU_CONTROL,
MCU_CONTROL and PWR_CONTROL, so the driver can no longer assume a single
fixed base layout across all supported GPUs.
Add per-GPU register base offsets to the panthor hardware description
and switch the GPU, FW and PWR local iomem setup to use those stored
values. With the base addresses now owned by the hardware description,
drop the corresponding fixed base-offset definitions from the register
headers.
This prepares the driver for GPUs with different register layouts
without changing the existing behaviour on currently supported parts.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_fw.c | 2 +-
drivers/gpu/drm/panthor/panthor_fw_regs.h | 2 --
drivers/gpu/drm/panthor/panthor_gpu.c | 7 +++----
drivers/gpu/drm/panthor/panthor_gpu_regs.h | 2 --
drivers/gpu/drm/panthor/panthor_hw.c | 13 +++++++++++--
drivers/gpu/drm/panthor/panthor_hw.h | 19 +++++++++++++++++--
drivers/gpu/drm/panthor/panthor_pwr.c | 4 ++--
drivers/gpu/drm/panthor/panthor_pwr_regs.h | 3 ---
8 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 784d0a25beb2..f6381f5f236f 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1467,7 +1467,7 @@ int panthor_fw_init(struct panthor_device *ptdev)
if (!fw)
return -ENOMEM;
- fw->iomem = ptdev->iomem + MCU_CONTROL_BASE;
+ fw->iomem = ptdev->iomem + ptdev->hw->map.mcu_control_base;
ptdev->fw = fw;
init_waitqueue_head(&fw->req_waitqueue);
INIT_LIST_HEAD(&fw->sections);
diff --git a/drivers/gpu/drm/panthor/panthor_fw_regs.h b/drivers/gpu/drm/panthor/panthor_fw_regs.h
index b2e59cfc22b0..a3492f7f913f 100644
--- a/drivers/gpu/drm/panthor/panthor_fw_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_fw_regs.h
@@ -4,8 +4,6 @@
#ifndef __PANTHOR_FW_REGS_H__
#define __PANTHOR_FW_REGS_H__
-#define MCU_CONTROL_BASE 0x700
-
#define MCU_CONTROL 0x0
#define MCU_CONTROL_ENABLE 1
#define MCU_CONTROL_AUTO 2
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index bb5e64188a15..3c88a25287bf 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -154,7 +154,7 @@ int panthor_gpu_init(struct panthor_device *ptdev)
if (!gpu)
return -ENOMEM;
- gpu->iomem = ptdev->iomem + GPU_CONTROL_BASE;
+ gpu->iomem = ptdev->iomem + ptdev->hw->map.gpu_control_base;
spin_lock_init(&gpu->reqs_lock);
init_waitqueue_head(&gpu->reqs_acked);
mutex_init(&gpu->cache_flush_lock);
@@ -170,9 +170,8 @@ int panthor_gpu_init(struct panthor_device *ptdev)
if (irq < 0)
return irq;
- ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq,
- GPU_INTERRUPTS_MASK,
- ptdev->iomem + GPU_INT_BASE);
+ ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, GPU_INTERRUPTS_MASK,
+ gpu->iomem + GPU_INT_BASE);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/panthor/panthor_gpu_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_regs.h
index 4c5b953796e4..753df8700990 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu_regs.h
@@ -4,8 +4,6 @@
#ifndef __PANTHOR_GPU_REGS_H__
#define __PANTHOR_GPU_REGS_H__
-#define GPU_CONTROL_BASE 0x0
-
#define GPU_ID 0x0
#define GPU_ARCH_MAJOR(x) ((x) >> 28)
#define GPU_ARCH_MINOR(x) (((x) & GENMASK(27, 24)) >> 24)
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 833d10cbb2d6..1f7fab4caf4d 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -38,6 +38,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
.power_changed_off = panthor_gpu_power_changed_off,
.power_changed_on = panthor_gpu_power_changed_on,
},
+ .map = {
+ .gpu_control_base = 0x0,
+ .mcu_control_base = 0x700,
+ },
};
static struct panthor_hw panthor_hw_arch_v14 = {
@@ -46,6 +50,11 @@ static struct panthor_hw panthor_hw_arch_v14 = {
.l2_power_off = panthor_pwr_l2_power_off,
.l2_power_on = panthor_pwr_l2_power_on,
},
+ .map = {
+ .gpu_control_base = 0x0,
+ .pwr_control_base = 0x800,
+ .mcu_control_base = 0x700,
+ },
};
static struct panthor_hw_entry panthor_hw_match[] = {
@@ -197,7 +206,7 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
{
unsigned int i;
- void __iomem *gpu_iomem = ptdev->iomem + GPU_CONTROL_BASE;
+ void __iomem *gpu_iomem = ptdev->iomem + ptdev->hw->map.gpu_control_base;
ptdev->gpu_info.csf_id = gpu_read(gpu_iomem, GPU_CSF_ID);
ptdev->gpu_info.gpu_rev = gpu_read(gpu_iomem, GPU_REVID);
@@ -223,7 +232,7 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
ptdev->gpu_info.gpu_features = gpu_read64(gpu_iomem, GPU_FEATURES);
if (panthor_hw_has_pwr_ctrl(ptdev)) {
- void __iomem *pwr_iomem = gpu_iomem + PWR_CONTROL_BASE;
+ void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
/* Introduced in arch 14.x */
ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 2a666d8cdb98..58673a427bc9 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -26,14 +26,29 @@ struct panthor_hw_ops {
void (*power_changed_off)(struct panthor_device *ptdev);
};
+/**
+ * struct panthor_hw_regmap - Register base addresses
+ */
+struct panthor_hw_regmap {
+ /** @gpu_control_base: GPU_CONTROL base address */
+ u32 gpu_control_base;
+
+ /** @pwr_control_base: PWR_CONTROL base address */
+ u32 pwr_control_base;
+
+ /** @mcu_control_base: MCU_CONTROL base address */
+ u32 mcu_control_base;
+};
+
/**
* struct panthor_hw - GPU specific register mapping and functions
*/
struct panthor_hw {
- /** @features: Bitmap containing panthor_hw_feature */
-
/** @ops: Panthor HW specific operations */
struct panthor_hw_ops ops;
+
+ /** @map: Panthor HW-specific register base addresses */
+ struct panthor_hw_regmap map;
};
int panthor_hw_init(struct panthor_device *ptdev);
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
index 1cba093a2452..b833e4559e45 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -475,7 +475,7 @@ int panthor_pwr_init(struct panthor_device *ptdev)
if (!pwr)
return -ENOMEM;
- pwr->iomem = ptdev->iomem + PWR_CONTROL_BASE;
+ pwr->iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
spin_lock_init(&pwr->reqs_lock);
init_waitqueue_head(&pwr->reqs_acked);
ptdev->pwr = pwr;
@@ -486,7 +486,7 @@ int panthor_pwr_init(struct panthor_device *ptdev)
err = panthor_request_pwr_irq(
ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK,
- pwr->iomem + PWR_INT_BASE);
+ pwr->iomem);
if (err)
return err;
diff --git a/drivers/gpu/drm/panthor/panthor_pwr_regs.h b/drivers/gpu/drm/panthor/panthor_pwr_regs.h
index 9cf7a715066f..12939dcd7519 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_pwr_regs.h
@@ -4,9 +4,6 @@
#ifndef __PANTHOR_PWR_REGS_H__
#define __PANTHOR_PWR_REGS_H__
-#define PWR_CONTROL_BASE 0x800
-
-#define PWR_INT_BASE 0x0
#define PWR_IRQ_POWER_CHANGED_SINGLE BIT(0)
#define PWR_IRQ_POWER_CHANGED_ALL BIT(1)
#define PWR_IRQ_DELEGATION_CHANGED BIT(2)
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description
2026-05-28 15:05 ` [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description Karunika Choo
@ 2026-06-08 13:32 ` Boris Brezillon
2026-06-08 13:41 ` Boris Brezillon
0 siblings, 1 reply; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:32 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:33 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> /**
> * struct panthor_hw - GPU specific register mapping and functions
> */
> struct panthor_hw {
> - /** @features: Bitmap containing panthor_hw_feature */
> -
> /** @ops: Panthor HW specific operations */
> struct panthor_hw_ops ops;
> +
> + /** @map: Panthor HW-specific register base addresses */
> + struct panthor_hw_regmap map;
map is a bit too vague. Can we rename this regbank_offsets or something
along those lines? If we do that, we can drop the _base in the field
names, because it'd be obvious that these are offsets when the field is
used:
void __iomem *gpu_iomem = ptdev->iomem + ptdev->hw->regbank_offsets.gpu_control;
> };
>
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description
2026-06-08 13:32 ` Boris Brezillon
@ 2026-06-08 13:41 ` Boris Brezillon
0 siblings, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:41 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Mon, 8 Jun 2026 15:32:46 +0200
Boris Brezillon <boris.brezillon@collabora.com> wrote:
> On Thu, 28 May 2026 16:05:33 +0100
> Karunika Choo <karunika.choo@arm.com> wrote:
>
> > /**
> > * struct panthor_hw - GPU specific register mapping and functions
> > */
> > struct panthor_hw {
> > - /** @features: Bitmap containing panthor_hw_feature */
> > -
> > /** @ops: Panthor HW specific operations */
> > struct panthor_hw_ops ops;
> > +
> > + /** @map: Panthor HW-specific register base addresses */
> > + struct panthor_hw_regmap map;
>
> map is a bit too vague. Can we rename this regbank_offsets or something
> along those lines? If we do that, we can drop the _base in the field
> names, because it'd be obvious that these are offsets when the field is
> used:
>
> void __iomem *gpu_iomem = ptdev->iomem + ptdev->hw->regbank_offsets.gpu_control;
Actually, MMU ranges need a stride, so I'd go for:
struct panthor_regbank {
u32 offset;
u32 stride;
};
struct panthor_regs_layout {
struct panthor_regbank gpu_control;
struct panthor_regbank pwr_control;
..
};
struct panthor_hw {
struct panthor_regs_layout regs_layout;
};
>
> > };
> >
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (4 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 05/18] drm/panthor: Move register base offsets to the HW description Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:45 ` Boris Brezillon
2026-06-11 14:45 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support Karunika Choo
` (12 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Move the MMU address-space register layout into the hardware description
by storing the MMU_AS base offset and per-AS stride in the Panthor HW
map.
Use those values to compute the iomem pointer for each AS slot and make
the MMU AS register definitions relative to the per-slot register window
instead of hard-coding the slot offset in every register macro.
This prepares the MMU code for GPUs where the MMU AS register region is
not at a fixed offset while keeping the existing register accesses
scoped to a single AS window.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_hw.c | 8 +++++
drivers/gpu/drm/panthor/panthor_hw.h | 8 +++++
drivers/gpu/drm/panthor/panthor_mmu.c | 35 ++++++++++++++--------
drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 ++++++--------
4 files changed, 47 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 1f7fab4caf4d..e677f1a8f488 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -41,6 +41,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
.map = {
.gpu_control_base = 0x0,
.mcu_control_base = 0x700,
+ .mmu_as = {
+ .base = 0x2400,
+ .stride = 0x40,
+ },
},
};
@@ -54,6 +58,10 @@ static struct panthor_hw panthor_hw_arch_v14 = {
.gpu_control_base = 0x0,
.pwr_control_base = 0x800,
.mcu_control_base = 0x700,
+ .mmu_as = {
+ .base = 0x2400,
+ .stride = 0x40,
+ },
},
};
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 58673a427bc9..0ae11b78c77e 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -38,6 +38,14 @@ struct panthor_hw_regmap {
/** @mcu_control_base: MCU_CONTROL base address */
u32 mcu_control_base;
+
+ struct {
+ /** @mmu_as.base: MMU_AS base address */
+ u32 base;
+
+ /** @mmu_as.stride: Stride between subsequent MMU_AS register blocks */
+ u32 stride;
+ } mmu_as;
};
/**
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 48127313332f..9a64f977d28c 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -36,6 +36,7 @@
#include "panthor_gpu.h"
#include "panthor_gpu_regs.h"
#include "panthor_heap.h"
+#include "panthor_hw.h"
#include "panthor_mmu.h"
#include "panthor_mmu_regs.h"
#include "panthor_sched.h"
@@ -57,7 +58,7 @@ struct panthor_as_slot {
*/
struct panthor_mmu {
/** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
- void __iomem *iomem;
+ void __iomem *iomem[MAX_AS_SLOTS];
/** @irq: The MMU irq. */
struct panthor_irq irq;
@@ -528,7 +529,7 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
/* Wait for the MMU status to indicate there is no active command, in
* case one is pending.
*/
- ret = gpu_read_relaxed_poll_timeout_atomic(mmu->iomem, AS_STATUS(as_nr), val,
+ ret = gpu_read_relaxed_poll_timeout_atomic(mmu->iomem[as_nr], AS_STATUS, val,
!(val & AS_STATUS_AS_ACTIVE), 10, 100000);
if (ret) {
@@ -546,7 +547,7 @@ static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd
/* write AS_COMMAND when MMU is ready to accept another command */
status = wait_ready(ptdev, as_nr);
if (!status) {
- gpu_write(ptdev->mmu->iomem, AS_COMMAND(as_nr), cmd);
+ gpu_write(ptdev->mmu->iomem[as_nr], AS_COMMAND, cmd);
status = wait_ready(ptdev, as_nr);
}
@@ -599,9 +600,9 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
panthor_mmu_irq_enable_events(&ptdev->mmu->irq,
panthor_mmu_as_fault_mask(ptdev, as_nr));
- gpu_write64(mmu->iomem, AS_TRANSTAB(as_nr), transtab);
- gpu_write64(mmu->iomem, AS_MEMATTR(as_nr), memattr);
- gpu_write64(mmu->iomem, AS_TRANSCFG(as_nr), transcfg);
+ gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, transtab);
+ gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, memattr);
+ gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, transcfg);
return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
}
@@ -637,9 +638,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
if (recycle_slot)
return 0;
- gpu_write64(mmu->iomem, AS_TRANSTAB(as_nr), 0);
- gpu_write64(mmu->iomem, AS_MEMATTR(as_nr), 0);
- gpu_write64(mmu->iomem, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED);
+ gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, 0);
+ gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, 0);
+ gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
}
@@ -1739,7 +1740,7 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size)
mutex_lock(&ptdev->mmu->as.slots_lock);
if (vm->as.id >= 0 && size) {
/* Lock the region that needs to be updated */
- gpu_write64(ptdev->mmu->iomem, AS_LOCKADDR(vm->as.id),
+ gpu_write64(ptdev->mmu->iomem[vm->as.id], AS_LOCKADDR,
pack_region_range(ptdev, &start, &size));
/* If the lock succeeded, update the locked_region info. */
@@ -1801,8 +1802,8 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
u32 access_type;
u32 source_id;
- fault_status = gpu_read(mmu->iomem, AS_FAULTSTATUS(as));
- addr = gpu_read64(mmu->iomem, AS_FAULTADDRESS(as));
+ fault_status = gpu_read(mmu->iomem[as], AS_FAULTSTATUS);
+ addr = gpu_read64(mmu->iomem[as], AS_FAULTADDRESS);
/* decode the fault status */
exception_type = fault_status & 0xFF;
@@ -3237,8 +3238,11 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res)
int panthor_mmu_init(struct panthor_device *ptdev)
{
u32 va_bits = GPU_MMU_FEATURES_VA_BITS(ptdev->gpu_info.mmu_features);
+ unsigned long as_present_mask = ptdev->gpu_info.as_present;
+ struct panthor_hw_regmap *regmap = &ptdev->hw->map;
struct panthor_mmu *mmu;
int ret, irq;
+ u32 as_id;
mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL);
if (!mmu)
@@ -3255,7 +3259,12 @@ int panthor_mmu_init(struct panthor_device *ptdev)
if (ret)
return ret;
- mmu->iomem = ptdev->iomem + MMU_AS_BASE;
+ for_each_set_bit(as_id, &as_present_mask, MAX_AS_SLOTS) {
+ u64 offset = regmap->mmu_as.base + (regmap->mmu_as.stride * as_id);
+
+ mmu->iomem[as_id] = ptdev->iomem + offset;
+ }
+
ptdev->mmu = mmu;
irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "mmu");
diff --git a/drivers/gpu/drm/panthor/panthor_mmu_regs.h b/drivers/gpu/drm/panthor/panthor_mmu_regs.h
index 4e32ab931949..9d3747601e69 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu_regs.h
@@ -10,13 +10,8 @@
/* AS_COMMAND register commands */
-#define MMU_AS_BASE 0x2400
-
-#define MMU_AS_SHIFT 6
-#define MMU_AS(as) ((as) << MMU_AS_SHIFT)
-
-#define AS_TRANSTAB(as) (MMU_AS(as) + 0x0)
-#define AS_MEMATTR(as) (MMU_AS(as) + 0x8)
+#define AS_TRANSTAB 0x0
+#define AS_MEMATTR 0x8
#define AS_MEMATTR_AARCH64_INNER_ALLOC_IMPL (2 << 2)
#define AS_MEMATTR_AARCH64_INNER_ALLOC_EXPL(w, r) ((3 << 2) | \
((w) ? BIT(0) : 0) | \
@@ -28,8 +23,8 @@
#define AS_MEMATTR_AARCH64_INNER_OUTER_NC (1 << 6)
#define AS_MEMATTR_AARCH64_INNER_OUTER_WB (2 << 6)
#define AS_MEMATTR_AARCH64_FAULT (3 << 6)
-#define AS_LOCKADDR(as) (MMU_AS(as) + 0x10)
-#define AS_COMMAND(as) (MMU_AS(as) + 0x18)
+#define AS_LOCKADDR 0x10
+#define AS_COMMAND 0x18
#define AS_COMMAND_NOP 0
#define AS_COMMAND_UPDATE 1
#define AS_COMMAND_LOCK 2
@@ -37,16 +32,16 @@
#define AS_COMMAND_FLUSH_PT 4
#define AS_COMMAND_FLUSH_MEM 5
#define AS_LOCK_REGION_MIN_SIZE (1ULL << 15)
-#define AS_FAULTSTATUS(as) (MMU_AS(as) + 0x1C)
+#define AS_FAULTSTATUS 0x1C
#define AS_FAULTSTATUS_ACCESS_TYPE_MASK (0x3 << 8)
#define AS_FAULTSTATUS_ACCESS_TYPE_ATOMIC (0x0 << 8)
#define AS_FAULTSTATUS_ACCESS_TYPE_EX (0x1 << 8)
#define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8)
#define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8)
-#define AS_FAULTADDRESS(as) (MMU_AS(as) + 0x20)
-#define AS_STATUS(as) (MMU_AS(as) + 0x28)
+#define AS_FAULTADDRESS 0x20
+#define AS_STATUS 0x28
#define AS_STATUS_AS_ACTIVE BIT(0)
-#define AS_TRANSCFG(as) (MMU_AS(as) + 0x30)
+#define AS_TRANSCFG 0x30
#define AS_TRANSCFG_ADRMODE_UNMAPPED (1 << 0)
#define AS_TRANSCFG_ADRMODE_IDENTITY (2 << 0)
#define AS_TRANSCFG_ADRMODE_AARCH64_4K (6 << 0)
@@ -64,6 +59,6 @@
#define AS_TRANSCFG_DISABLE_AF_FAULT BIT(34)
#define AS_TRANSCFG_WXN BIT(35)
#define AS_TRANSCFG_XREADABLE BIT(36)
-#define AS_FAULTEXTRA(as) (MMU_AS(as) + 0x38)
+#define AS_FAULTEXTRA 0x38
#endif /* __PANTHOR_MMU_REGS_H__ */
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride
2026-05-28 15:05 ` [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
@ 2026-06-08 13:45 ` Boris Brezillon
2026-06-11 14:45 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:45 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:34 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 48127313332f..9a64f977d28c 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -36,6 +36,7 @@
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_heap.h"
> +#include "panthor_hw.h"
> #include "panthor_mmu.h"
> #include "panthor_mmu_regs.h"
> #include "panthor_sched.h"
> @@ -57,7 +58,7 @@ struct panthor_as_slot {
> */
> struct panthor_mmu {
> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
> - void __iomem *iomem;
> + void __iomem *iomem[MAX_AS_SLOTS];
Can we move the iomem field to panthor_as_slot?
>
> /** @irq: The MMU irq. */
> struct panthor_irq irq;
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride
2026-05-28 15:05 ` [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
2026-06-08 13:45 ` Boris Brezillon
@ 2026-06-11 14:45 ` Steven Price
2026-06-11 16:11 ` Karunika Choo
1 sibling, 1 reply; 42+ messages in thread
From: Steven Price @ 2026-06-11 14:45 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> Move the MMU address-space register layout into the hardware description
> by storing the MMU_AS base offset and per-AS stride in the Panthor HW
> map.
>
> Use those values to compute the iomem pointer for each AS slot and make
> the MMU AS register definitions relative to the per-slot register window
> instead of hard-coding the slot offset in every register macro.
>
> This prepares the MMU code for GPUs where the MMU AS register region is
> not at a fixed offset while keeping the existing register accesses
> scoped to a single AS window.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> drivers/gpu/drm/panthor/panthor_hw.c | 8 +++++
> drivers/gpu/drm/panthor/panthor_hw.h | 8 +++++
> drivers/gpu/drm/panthor/panthor_mmu.c | 35 ++++++++++++++--------
> drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 ++++++--------
> 4 files changed, 47 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index 1f7fab4caf4d..e677f1a8f488 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -41,6 +41,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
> .map = {
> .gpu_control_base = 0x0,
> .mcu_control_base = 0x700,
> + .mmu_as = {
> + .base = 0x2400,
> + .stride = 0x40,
> + },
> },
> };
>
> @@ -54,6 +58,10 @@ static struct panthor_hw panthor_hw_arch_v14 = {
> .gpu_control_base = 0x0,
> .pwr_control_base = 0x800,
> .mcu_control_base = 0x700,
> + .mmu_as = {
> + .base = 0x2400,
> + .stride = 0x40,
> + },
> },
> };
>
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
> index 58673a427bc9..0ae11b78c77e 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.h
> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
> @@ -38,6 +38,14 @@ struct panthor_hw_regmap {
>
> /** @mcu_control_base: MCU_CONTROL base address */
> u32 mcu_control_base;
> +
> + struct {
> + /** @mmu_as.base: MMU_AS base address */
> + u32 base;
> +
> + /** @mmu_as.stride: Stride between subsequent MMU_AS register blocks */
> + u32 stride;
> + } mmu_as;
> };
>
> /**
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 48127313332f..9a64f977d28c 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -36,6 +36,7 @@
> #include "panthor_gpu.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_heap.h"
> +#include "panthor_hw.h"
> #include "panthor_mmu.h"
> #include "panthor_mmu_regs.h"
> #include "panthor_sched.h"
> @@ -57,7 +58,7 @@ struct panthor_as_slot {
> */
> struct panthor_mmu {
> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
> - void __iomem *iomem;
> + void __iomem *iomem[MAX_AS_SLOTS];
I'm not convinced that having a iomem pointer per AS is worth it. It
seems excessive.
Boris's suggestion of moving it to panthor_as_slot at least avoids the
array indexing, but I did wonder about just having a helper function. AI
cooked up the below diff (untested, applies on top) which demonstrates
the idea.
I think the AI was a bit over the top with the as_present check in the
helper, although that would clearly indicate a serious bug if it
triggered.
What do you think?
Thanks,
Steve
----8<-----
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 53c5744609fa..6127df893476 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -58,7 +58,7 @@ struct panthor_as_slot {
*/
struct panthor_mmu {
/** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
- void __iomem *iomem[MAX_AS_SLOTS];
+ void __iomem *iomem;
/** @irq: The MMU irq. */
struct panthor_irq irq;
@@ -112,6 +112,20 @@ struct panthor_mmu {
} vm;
};
+static void __iomem *
+panthor_mmu_as_iomem(struct panthor_device *ptdev, u32 as_nr)
+{
+ struct panthor_mmu *mmu = ptdev->mmu;
+
+ if (drm_WARN_ON(&ptdev->base, as_nr >= MAX_AS_SLOTS))
+ return mmu->iomem;
+
+ if (drm_WARN_ON(&ptdev->base, !(ptdev->gpu_info.as_present & BIT(as_nr))))
+ return mmu->iomem;
+
+ return mmu->iomem + (ptdev->hw->map.mmu_as.stride * as_nr);
+}
+
/**
* struct panthor_vm_pool - VM pool object
*/
@@ -522,14 +536,14 @@ static void free_pt(void *cookie, void *data, size_t size)
static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
{
- struct panthor_mmu *mmu = ptdev->mmu;
+ void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
int ret;
u32 val;
/* Wait for the MMU status to indicate there is no active command, in
* case one is pending.
*/
- ret = gpu_read_relaxed_poll_timeout_atomic(mmu->iomem[as_nr], AS_STATUS, val,
+ ret = gpu_read_relaxed_poll_timeout_atomic(as_iomem, AS_STATUS, val,
!(val & AS_STATUS_AS_ACTIVE), 10, 100000);
if (ret) {
@@ -542,12 +556,13 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd)
{
+ void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
int status;
/* write AS_COMMAND when MMU is ready to accept another command */
status = wait_ready(ptdev, as_nr);
if (!status) {
- gpu_write(ptdev->mmu->iomem[as_nr], AS_COMMAND, cmd);
+ gpu_write(as_iomem, AS_COMMAND, cmd);
status = wait_ready(ptdev, as_nr);
}
@@ -595,14 +610,14 @@ PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler);
static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
u64 transtab, u64 transcfg, u64 memattr)
{
- struct panthor_mmu *mmu = ptdev->mmu;
+ void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
panthor_mmu_irq_enable_events(&ptdev->mmu->irq,
panthor_mmu_as_fault_mask(ptdev, as_nr));
- gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, transtab);
- gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, memattr);
- gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, transcfg);
+ gpu_write64(as_iomem, AS_TRANSTAB, transtab);
+ gpu_write64(as_iomem, AS_MEMATTR, memattr);
+ gpu_write64(as_iomem, AS_TRANSCFG, transcfg);
return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
}
@@ -610,8 +625,8 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
bool recycle_slot)
{
- struct panthor_mmu *mmu = ptdev->mmu;
struct panthor_vm *vm = ptdev->mmu->as.slots[as_nr].vm;
+ void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
int ret;
lockdep_assert_held(&ptdev->mmu->as.slots_lock);
@@ -638,9 +653,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
if (recycle_slot)
return 0;
- gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, 0);
- gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, 0);
- gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
+ gpu_write64(as_iomem, AS_TRANSTAB, 0);
+ gpu_write64(as_iomem, AS_MEMATTR, 0);
+ gpu_write64(as_iomem, AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
}
@@ -1740,7 +1755,7 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size)
mutex_lock(&ptdev->mmu->as.slots_lock);
if (vm->as.id >= 0 && size) {
/* Lock the region that needs to be updated */
- gpu_write64(ptdev->mmu->iomem[vm->as.id], AS_LOCKADDR,
+ gpu_write64(panthor_mmu_as_iomem(ptdev, vm->as.id), AS_LOCKADDR,
pack_region_range(ptdev, &start, &size));
/* If the lock succeeded, update the locked_region info. */
@@ -1789,21 +1804,21 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm)
static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
{
- struct panthor_mmu *mmu = ptdev->mmu;
bool has_unhandled_faults = false;
status = panthor_mmu_fault_mask(ptdev, status);
while (status) {
u32 as = ffs(status | (status >> 16)) - 1;
u32 mask = panthor_mmu_as_fault_mask(ptdev, as);
+ void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as);
u64 addr;
u32 fault_status;
u32 exception_type;
u32 access_type;
u32 source_id;
- fault_status = gpu_read(mmu->iomem[as], AS_FAULTSTATUS);
- addr = gpu_read64(mmu->iomem[as], AS_FAULTADDRESS);
+ fault_status = gpu_read(as_iomem, AS_FAULTSTATUS);
+ addr = gpu_read64(as_iomem, AS_FAULTADDRESS);
/* decode the fault status */
exception_type = fault_status & 0xFF;
@@ -1834,7 +1849,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
* Note that COMPLETED irqs are never cleared, but this is fine
* because they are always masked.
*/
- gpu_write(mmu->irq.iomem, INT_CLEAR, mask);
+ gpu_write(ptdev->mmu->irq.iomem, INT_CLEAR, mask);
if (ptdev->mmu->as.slots[as].vm)
ptdev->mmu->as.slots[as].vm->unhandled_fault = true;
@@ -3239,11 +3254,8 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res)
int panthor_mmu_init(struct panthor_device *ptdev)
{
u32 va_bits = GPU_MMU_FEATURES_VA_BITS(ptdev->gpu_info.mmu_features);
- unsigned long as_present_mask = ptdev->gpu_info.as_present;
- struct panthor_hw_regmap *regmap = &ptdev->hw->map;
struct panthor_mmu *mmu;
int ret, irq;
- u32 as_id;
mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL);
if (!mmu)
@@ -3260,12 +3272,7 @@ int panthor_mmu_init(struct panthor_device *ptdev)
if (ret)
return ret;
- for_each_set_bit(as_id, &as_present_mask, MAX_AS_SLOTS) {
- u64 offset = regmap->mmu_as.base + (regmap->mmu_as.stride * as_id);
-
- mmu->iomem[as_id] = ptdev->iomem + offset;
- }
-
+ mmu->iomem = ptdev->iomem + ptdev->hw->map.mmu_as.base;
ptdev->mmu = mmu;
irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "mmu");
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride
2026-06-11 14:45 ` Steven Price
@ 2026-06-11 16:11 ` Karunika Choo
2026-06-12 15:58 ` Steven Price
0 siblings, 1 reply; 42+ messages in thread
From: Karunika Choo @ 2026-06-11 16:11 UTC (permalink / raw)
To: Steven Price, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 11/06/2026 15:45, Steven Price wrote:
> On 28/05/2026 16:05, Karunika Choo wrote:
>> Move the MMU address-space register layout into the hardware description
>> by storing the MMU_AS base offset and per-AS stride in the Panthor HW
>> map.
>>
>> Use those values to compute the iomem pointer for each AS slot and make
>> the MMU AS register definitions relative to the per-slot register window
>> instead of hard-coding the slot offset in every register macro.
>>
>> This prepares the MMU code for GPUs where the MMU AS register region is
>> not at a fixed offset while keeping the existing register accesses
>> scoped to a single AS window.
>>
>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>> ---
>> drivers/gpu/drm/panthor/panthor_hw.c | 8 +++++
>> drivers/gpu/drm/panthor/panthor_hw.h | 8 +++++
>> drivers/gpu/drm/panthor/panthor_mmu.c | 35 ++++++++++++++--------
>> drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 ++++++--------
>> 4 files changed, 47 insertions(+), 27 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
>> index 1f7fab4caf4d..e677f1a8f488 100644
>> --- a/drivers/gpu/drm/panthor/panthor_hw.c
>> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
>> @@ -41,6 +41,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
>> .map = {
>> .gpu_control_base = 0x0,
>> .mcu_control_base = 0x700,
>> + .mmu_as = {
>> + .base = 0x2400,
>> + .stride = 0x40,
>> + },
>> },
>> };
>>
>> @@ -54,6 +58,10 @@ static struct panthor_hw panthor_hw_arch_v14 = {
>> .gpu_control_base = 0x0,
>> .pwr_control_base = 0x800,
>> .mcu_control_base = 0x700,
>> + .mmu_as = {
>> + .base = 0x2400,
>> + .stride = 0x40,
>> + },
>> },
>> };
>>
>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
>> index 58673a427bc9..0ae11b78c77e 100644
>> --- a/drivers/gpu/drm/panthor/panthor_hw.h
>> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
>> @@ -38,6 +38,14 @@ struct panthor_hw_regmap {
>>
>> /** @mcu_control_base: MCU_CONTROL base address */
>> u32 mcu_control_base;
>> +
>> + struct {
>> + /** @mmu_as.base: MMU_AS base address */
>> + u32 base;
>> +
>> + /** @mmu_as.stride: Stride between subsequent MMU_AS register blocks */
>> + u32 stride;
>> + } mmu_as;
>> };
>>
>> /**
>> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
>> index 48127313332f..9a64f977d28c 100644
>> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
>> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
>> @@ -36,6 +36,7 @@
>> #include "panthor_gpu.h"
>> #include "panthor_gpu_regs.h"
>> #include "panthor_heap.h"
>> +#include "panthor_hw.h"
>> #include "panthor_mmu.h"
>> #include "panthor_mmu_regs.h"
>> #include "panthor_sched.h"
>> @@ -57,7 +58,7 @@ struct panthor_as_slot {
>> */
>> struct panthor_mmu {
>> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
>> - void __iomem *iomem;
>> + void __iomem *iomem[MAX_AS_SLOTS];
>
> I'm not convinced that having a iomem pointer per AS is worth it. It
> seems excessive.
>
> Boris's suggestion of moving it to panthor_as_slot at least avoids the
> array indexing, but I did wonder about just having a helper function. AI
> cooked up the below diff (untested, applies on top) which demonstrates
> the idea.
>
> I think the AI was a bit over the top with the as_present check in the
> helper, although that would clearly indicate a serious bug if it
> triggered.
>
> What do you think?
>
> Thanks,
> Steve
TBF, with Boris's suggestion, I included a new as_iomem() helper
anyways, this isn't that far off as an option. Perhaps maybe
rename `iomem` to `as_iomem` to clarify that this isn't the
MMU_CONTROL iomem.
I don't really mind either way, this option has the ambiguity
of where the iomem belongs to, while Boris' solution adds a
little more baggage to the struct but ownership is clear.
Kind regards,
Karunika
>
> ----8<-----
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index 53c5744609fa..6127df893476 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -58,7 +58,7 @@ struct panthor_as_slot {
> */
> struct panthor_mmu {
> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
> - void __iomem *iomem[MAX_AS_SLOTS];
> + void __iomem *iomem;
>
> /** @irq: The MMU irq. */
> struct panthor_irq irq;
> @@ -112,6 +112,20 @@ struct panthor_mmu {
> } vm;
> };
>
> +static void __iomem *
> +panthor_mmu_as_iomem(struct panthor_device *ptdev, u32 as_nr)
> +{
> + struct panthor_mmu *mmu = ptdev->mmu;
> +
> + if (drm_WARN_ON(&ptdev->base, as_nr >= MAX_AS_SLOTS))
> + return mmu->iomem;
> +
> + if (drm_WARN_ON(&ptdev->base, !(ptdev->gpu_info.as_present & BIT(as_nr))))
> + return mmu->iomem;
> +
> + return mmu->iomem + (ptdev->hw->map.mmu_as.stride * as_nr);
> +}
> +
> /**
> * struct panthor_vm_pool - VM pool object
> */
> @@ -522,14 +536,14 @@ static void free_pt(void *cookie, void *data, size_t size)
>
> static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
> {
> - struct panthor_mmu *mmu = ptdev->mmu;
> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
> int ret;
> u32 val;
>
> /* Wait for the MMU status to indicate there is no active command, in
> * case one is pending.
> */
> - ret = gpu_read_relaxed_poll_timeout_atomic(mmu->iomem[as_nr], AS_STATUS, val,
> + ret = gpu_read_relaxed_poll_timeout_atomic(as_iomem, AS_STATUS, val,
> !(val & AS_STATUS_AS_ACTIVE), 10, 100000);
>
> if (ret) {
> @@ -542,12 +556,13 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
>
> static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd)
> {
> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
> int status;
>
> /* write AS_COMMAND when MMU is ready to accept another command */
> status = wait_ready(ptdev, as_nr);
> if (!status) {
> - gpu_write(ptdev->mmu->iomem[as_nr], AS_COMMAND, cmd);
> + gpu_write(as_iomem, AS_COMMAND, cmd);
> status = wait_ready(ptdev, as_nr);
> }
>
> @@ -595,14 +610,14 @@ PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler);
> static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
> u64 transtab, u64 transcfg, u64 memattr)
> {
> - struct panthor_mmu *mmu = ptdev->mmu;
> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
>
> panthor_mmu_irq_enable_events(&ptdev->mmu->irq,
> panthor_mmu_as_fault_mask(ptdev, as_nr));
>
> - gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, transtab);
> - gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, memattr);
> - gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, transcfg);
> + gpu_write64(as_iomem, AS_TRANSTAB, transtab);
> + gpu_write64(as_iomem, AS_MEMATTR, memattr);
> + gpu_write64(as_iomem, AS_TRANSCFG, transcfg);
>
> return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
> }
> @@ -610,8 +625,8 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
> static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
> bool recycle_slot)
> {
> - struct panthor_mmu *mmu = ptdev->mmu;
> struct panthor_vm *vm = ptdev->mmu->as.slots[as_nr].vm;
> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
> int ret;
>
> lockdep_assert_held(&ptdev->mmu->as.slots_lock);
> @@ -638,9 +653,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
> if (recycle_slot)
> return 0;
>
> - gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, 0);
> - gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, 0);
> - gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
> + gpu_write64(as_iomem, AS_TRANSTAB, 0);
> + gpu_write64(as_iomem, AS_MEMATTR, 0);
> + gpu_write64(as_iomem, AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
>
> return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
> }
> @@ -1740,7 +1755,7 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size)
> mutex_lock(&ptdev->mmu->as.slots_lock);
> if (vm->as.id >= 0 && size) {
> /* Lock the region that needs to be updated */
> - gpu_write64(ptdev->mmu->iomem[vm->as.id], AS_LOCKADDR,
> + gpu_write64(panthor_mmu_as_iomem(ptdev, vm->as.id), AS_LOCKADDR,
> pack_region_range(ptdev, &start, &size));
>
> /* If the lock succeeded, update the locked_region info. */
> @@ -1789,21 +1804,21 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm)
>
> static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
> {
> - struct panthor_mmu *mmu = ptdev->mmu;
> bool has_unhandled_faults = false;
>
> status = panthor_mmu_fault_mask(ptdev, status);
> while (status) {
> u32 as = ffs(status | (status >> 16)) - 1;
> u32 mask = panthor_mmu_as_fault_mask(ptdev, as);
> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as);
> u64 addr;
> u32 fault_status;
> u32 exception_type;
> u32 access_type;
> u32 source_id;
>
> - fault_status = gpu_read(mmu->iomem[as], AS_FAULTSTATUS);
> - addr = gpu_read64(mmu->iomem[as], AS_FAULTADDRESS);
> + fault_status = gpu_read(as_iomem, AS_FAULTSTATUS);
> + addr = gpu_read64(as_iomem, AS_FAULTADDRESS);
>
> /* decode the fault status */
> exception_type = fault_status & 0xFF;
> @@ -1834,7 +1849,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
> * Note that COMPLETED irqs are never cleared, but this is fine
> * because they are always masked.
> */
> - gpu_write(mmu->irq.iomem, INT_CLEAR, mask);
> + gpu_write(ptdev->mmu->irq.iomem, INT_CLEAR, mask);
>
> if (ptdev->mmu->as.slots[as].vm)
> ptdev->mmu->as.slots[as].vm->unhandled_fault = true;
> @@ -3239,11 +3254,8 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res)
> int panthor_mmu_init(struct panthor_device *ptdev)
> {
> u32 va_bits = GPU_MMU_FEATURES_VA_BITS(ptdev->gpu_info.mmu_features);
> - unsigned long as_present_mask = ptdev->gpu_info.as_present;
> - struct panthor_hw_regmap *regmap = &ptdev->hw->map;
> struct panthor_mmu *mmu;
> int ret, irq;
> - u32 as_id;
>
> mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL);
> if (!mmu)
> @@ -3260,12 +3272,7 @@ int panthor_mmu_init(struct panthor_device *ptdev)
> if (ret)
> return ret;
>
> - for_each_set_bit(as_id, &as_present_mask, MAX_AS_SLOTS) {
> - u64 offset = regmap->mmu_as.base + (regmap->mmu_as.stride * as_id);
> -
> - mmu->iomem[as_id] = ptdev->iomem + offset;
> - }
> -
> + mmu->iomem = ptdev->iomem + ptdev->hw->map.mmu_as.base;
> ptdev->mmu = mmu;
>
> irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "mmu");
>
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride
2026-06-11 16:11 ` Karunika Choo
@ 2026-06-12 15:58 ` Steven Price
0 siblings, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-12 15:58 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 11/06/2026 17:11, Karunika Choo wrote:
> On 11/06/2026 15:45, Steven Price wrote:
>> On 28/05/2026 16:05, Karunika Choo wrote:
>>> Move the MMU address-space register layout into the hardware description
>>> by storing the MMU_AS base offset and per-AS stride in the Panthor HW
>>> map.
>>>
>>> Use those values to compute the iomem pointer for each AS slot and make
>>> the MMU AS register definitions relative to the per-slot register window
>>> instead of hard-coding the slot offset in every register macro.
>>>
>>> This prepares the MMU code for GPUs where the MMU AS register region is
>>> not at a fixed offset while keeping the existing register accesses
>>> scoped to a single AS window.
>>>
>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>> ---
>>> drivers/gpu/drm/panthor/panthor_hw.c | 8 +++++
>>> drivers/gpu/drm/panthor/panthor_hw.h | 8 +++++
>>> drivers/gpu/drm/panthor/panthor_mmu.c | 35 ++++++++++++++--------
>>> drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 ++++++--------
>>> 4 files changed, 47 insertions(+), 27 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
>>> index 1f7fab4caf4d..e677f1a8f488 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_hw.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
>>> @@ -41,6 +41,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
>>> .map = {
>>> .gpu_control_base = 0x0,
>>> .mcu_control_base = 0x700,
>>> + .mmu_as = {
>>> + .base = 0x2400,
>>> + .stride = 0x40,
>>> + },
>>> },
>>> };
>>>
>>> @@ -54,6 +58,10 @@ static struct panthor_hw panthor_hw_arch_v14 = {
>>> .gpu_control_base = 0x0,
>>> .pwr_control_base = 0x800,
>>> .mcu_control_base = 0x700,
>>> + .mmu_as = {
>>> + .base = 0x2400,
>>> + .stride = 0x40,
>>> + },
>>> },
>>> };
>>>
>>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
>>> index 58673a427bc9..0ae11b78c77e 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_hw.h
>>> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
>>> @@ -38,6 +38,14 @@ struct panthor_hw_regmap {
>>>
>>> /** @mcu_control_base: MCU_CONTROL base address */
>>> u32 mcu_control_base;
>>> +
>>> + struct {
>>> + /** @mmu_as.base: MMU_AS base address */
>>> + u32 base;
>>> +
>>> + /** @mmu_as.stride: Stride between subsequent MMU_AS register blocks */
>>> + u32 stride;
>>> + } mmu_as;
>>> };
>>>
>>> /**
>>> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
>>> index 48127313332f..9a64f977d28c 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
>>> @@ -36,6 +36,7 @@
>>> #include "panthor_gpu.h"
>>> #include "panthor_gpu_regs.h"
>>> #include "panthor_heap.h"
>>> +#include "panthor_hw.h"
>>> #include "panthor_mmu.h"
>>> #include "panthor_mmu_regs.h"
>>> #include "panthor_sched.h"
>>> @@ -57,7 +58,7 @@ struct panthor_as_slot {
>>> */
>>> struct panthor_mmu {
>>> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
>>> - void __iomem *iomem;
>>> + void __iomem *iomem[MAX_AS_SLOTS];
>>
>> I'm not convinced that having a iomem pointer per AS is worth it. It
>> seems excessive.
>>
>> Boris's suggestion of moving it to panthor_as_slot at least avoids the
>> array indexing, but I did wonder about just having a helper function. AI
>> cooked up the below diff (untested, applies on top) which demonstrates
>> the idea.
>>
>> I think the AI was a bit over the top with the as_present check in the
>> helper, although that would clearly indicate a serious bug if it
>> triggered.
>>
>> What do you think?
>>
>> Thanks,
>> Steve
>
> TBF, with Boris's suggestion, I included a new as_iomem() helper
> anyways, this isn't that far off as an option. Perhaps maybe
> rename `iomem` to `as_iomem` to clarify that this isn't the
> MMU_CONTROL iomem.
>
> I don't really mind either way, this option has the ambiguity
> of where the iomem belongs to, while Boris' solution adds a
> little more baggage to the struct but ownership is clear.
Yeah I wasn't really happy with the AI's output - I just wanted to see
what it would look like and it was a quick way of doing that. There's
definitely some cases where the helper makes the code easier to read and
I disliked the array.
It sounds like you've already got something with Boris's suggestion and
a helper, so I think we're probably good to stick with that.
Thanks,
Steve
> Kind regards,
> Karunika
>
>>
>> ----8<-----
>> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
>> index 53c5744609fa..6127df893476 100644
>> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
>> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
>> @@ -58,7 +58,7 @@ struct panthor_as_slot {
>> */
>> struct panthor_mmu {
>> /** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
>> - void __iomem *iomem[MAX_AS_SLOTS];
>> + void __iomem *iomem;
>>
>> /** @irq: The MMU irq. */
>> struct panthor_irq irq;
>> @@ -112,6 +112,20 @@ struct panthor_mmu {
>> } vm;
>> };
>>
>> +static void __iomem *
>> +panthor_mmu_as_iomem(struct panthor_device *ptdev, u32 as_nr)
>> +{
>> + struct panthor_mmu *mmu = ptdev->mmu;
>> +
>> + if (drm_WARN_ON(&ptdev->base, as_nr >= MAX_AS_SLOTS))
>> + return mmu->iomem;
>> +
>> + if (drm_WARN_ON(&ptdev->base, !(ptdev->gpu_info.as_present & BIT(as_nr))))
>> + return mmu->iomem;
>> +
>> + return mmu->iomem + (ptdev->hw->map.mmu_as.stride * as_nr);
>> +}
>> +
>> /**
>> * struct panthor_vm_pool - VM pool object
>> */
>> @@ -522,14 +536,14 @@ static void free_pt(void *cookie, void *data, size_t size)
>>
>> static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
>> {
>> - struct panthor_mmu *mmu = ptdev->mmu;
>> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
>> int ret;
>> u32 val;
>>
>> /* Wait for the MMU status to indicate there is no active command, in
>> * case one is pending.
>> */
>> - ret = gpu_read_relaxed_poll_timeout_atomic(mmu->iomem[as_nr], AS_STATUS, val,
>> + ret = gpu_read_relaxed_poll_timeout_atomic(as_iomem, AS_STATUS, val,
>> !(val & AS_STATUS_AS_ACTIVE), 10, 100000);
>>
>> if (ret) {
>> @@ -542,12 +556,13 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
>>
>> static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd)
>> {
>> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
>> int status;
>>
>> /* write AS_COMMAND when MMU is ready to accept another command */
>> status = wait_ready(ptdev, as_nr);
>> if (!status) {
>> - gpu_write(ptdev->mmu->iomem[as_nr], AS_COMMAND, cmd);
>> + gpu_write(as_iomem, AS_COMMAND, cmd);
>> status = wait_ready(ptdev, as_nr);
>> }
>>
>> @@ -595,14 +610,14 @@ PANTHOR_IRQ_HANDLER(mmu, panthor_mmu_irq_handler);
>> static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
>> u64 transtab, u64 transcfg, u64 memattr)
>> {
>> - struct panthor_mmu *mmu = ptdev->mmu;
>> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
>>
>> panthor_mmu_irq_enable_events(&ptdev->mmu->irq,
>> panthor_mmu_as_fault_mask(ptdev, as_nr));
>>
>> - gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, transtab);
>> - gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, memattr);
>> - gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, transcfg);
>> + gpu_write64(as_iomem, AS_TRANSTAB, transtab);
>> + gpu_write64(as_iomem, AS_MEMATTR, memattr);
>> + gpu_write64(as_iomem, AS_TRANSCFG, transcfg);
>>
>> return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
>> }
>> @@ -610,8 +625,8 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
>> static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
>> bool recycle_slot)
>> {
>> - struct panthor_mmu *mmu = ptdev->mmu;
>> struct panthor_vm *vm = ptdev->mmu->as.slots[as_nr].vm;
>> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as_nr);
>> int ret;
>>
>> lockdep_assert_held(&ptdev->mmu->as.slots_lock);
>> @@ -638,9 +653,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
>> if (recycle_slot)
>> return 0;
>>
>> - gpu_write64(mmu->iomem[as_nr], AS_TRANSTAB, 0);
>> - gpu_write64(mmu->iomem[as_nr], AS_MEMATTR, 0);
>> - gpu_write64(mmu->iomem[as_nr], AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
>> + gpu_write64(as_iomem, AS_TRANSTAB, 0);
>> + gpu_write64(as_iomem, AS_MEMATTR, 0);
>> + gpu_write64(as_iomem, AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
>>
>> return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
>> }
>> @@ -1740,7 +1755,7 @@ static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size)
>> mutex_lock(&ptdev->mmu->as.slots_lock);
>> if (vm->as.id >= 0 && size) {
>> /* Lock the region that needs to be updated */
>> - gpu_write64(ptdev->mmu->iomem[vm->as.id], AS_LOCKADDR,
>> + gpu_write64(panthor_mmu_as_iomem(ptdev, vm->as.id), AS_LOCKADDR,
>> pack_region_range(ptdev, &start, &size));
>>
>> /* If the lock succeeded, update the locked_region info. */
>> @@ -1789,21 +1804,21 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm)
>>
>> static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
>> {
>> - struct panthor_mmu *mmu = ptdev->mmu;
>> bool has_unhandled_faults = false;
>>
>> status = panthor_mmu_fault_mask(ptdev, status);
>> while (status) {
>> u32 as = ffs(status | (status >> 16)) - 1;
>> u32 mask = panthor_mmu_as_fault_mask(ptdev, as);
>> + void __iomem *as_iomem = panthor_mmu_as_iomem(ptdev, as);
>> u64 addr;
>> u32 fault_status;
>> u32 exception_type;
>> u32 access_type;
>> u32 source_id;
>>
>> - fault_status = gpu_read(mmu->iomem[as], AS_FAULTSTATUS);
>> - addr = gpu_read64(mmu->iomem[as], AS_FAULTADDRESS);
>> + fault_status = gpu_read(as_iomem, AS_FAULTSTATUS);
>> + addr = gpu_read64(as_iomem, AS_FAULTADDRESS);
>>
>> /* decode the fault status */
>> exception_type = fault_status & 0xFF;
>> @@ -1834,7 +1849,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status)
>> * Note that COMPLETED irqs are never cleared, but this is fine
>> * because they are always masked.
>> */
>> - gpu_write(mmu->irq.iomem, INT_CLEAR, mask);
>> + gpu_write(ptdev->mmu->irq.iomem, INT_CLEAR, mask);
>>
>> if (ptdev->mmu->as.slots[as].vm)
>> ptdev->mmu->as.slots[as].vm->unhandled_fault = true;
>> @@ -3239,11 +3254,8 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res)
>> int panthor_mmu_init(struct panthor_device *ptdev)
>> {
>> u32 va_bits = GPU_MMU_FEATURES_VA_BITS(ptdev->gpu_info.mmu_features);
>> - unsigned long as_present_mask = ptdev->gpu_info.as_present;
>> - struct panthor_hw_regmap *regmap = &ptdev->hw->map;
>> struct panthor_mmu *mmu;
>> int ret, irq;
>> - u32 as_id;
>>
>> mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL);
>> if (!mmu)
>> @@ -3260,12 +3272,7 @@ int panthor_mmu_init(struct panthor_device *ptdev)
>> if (ret)
>> return ret;
>>
>> - for_each_set_bit(as_id, &as_present_mask, MAX_AS_SLOTS) {
>> - u64 offset = regmap->mmu_as.base + (regmap->mmu_as.stride * as_id);
>> -
>> - mmu->iomem[as_id] = ptdev->iomem + offset;
>> - }
>> -
>> + mmu->iomem = ptdev->iomem + ptdev->hw->map.mmu_as.base;
>> ptdev->mmu = mmu;
>>
>> irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "mmu");
>>
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (5 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 06/18] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 13:59 ` Boris Brezillon
2026-06-11 15:18 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
` (11 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Add the panthor hardware description for Mali v15 GPUs and hook it into
device matching.
This includes the v15 register map and product identification needed to
populate the GPU information exposed by the driver and its uAPI.
Add compatibility string for v15 GPUs.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_drv.c | 1 +
drivers/gpu/drm/panthor/panthor_fw.c | 1 +
.../drm/panthor/panthor_gpu_discover_regs.h | 25 ++++++
drivers/gpu/drm/panthor/panthor_heap.c | 3 +-
drivers/gpu/drm/panthor/panthor_hw.c | 81 ++++++++++++++++++-
drivers/gpu/drm/panthor/panthor_hw.h | 13 +++
include/uapi/drm/panthor_drm.h | 10 ++-
7 files changed, 128 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index e8dc4096c1d2..21644a60c8e4 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1875,6 +1875,7 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, },
{ .compatible = "rockchip,rk3588-mali" },
{ .compatible = "arm,mali-valhall-csf" },
+ { .compatible = "arm,mali-gen5-am" },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index f6381f5f236f..e2a511b741f4 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1529,3 +1529,4 @@ MODULE_FIRMWARE("arm/mali/arch11.8/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch12.8/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch13.8/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch14.8/mali_csffw.bin");
+MODULE_FIRMWARE("arm/mali/arch15.8/mali_csffw.bin");
diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
index 54bb104902ee..9c64676dc35a 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
@@ -14,4 +14,29 @@
#define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
#define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
+#define DISCOVER_REVIDR 0x8
+#define DISCOVER_GPU_FEATURES 0x20
+#define GPU_FEATURES_VARIABLE_RATE_SHADING BIT(0)
+#define GPU_FEATURES_SIMD_STATE BIT(1)
+#define GPU_FEATURES_CROSS_STREAM_SYNC BIT(3)
+#define GPU_FEATURES_NX BIT(4)
+#define GPU_FEATURES_RAY_TRAVERSAL BIT(5)
+#define GPU_FEATURES_LD_LEA_TENSOR_INSTRUCTIONS BIT(6)
+#define DISCOVER_MCU_FEATURES 0x40
+#define DISCOVER_PRFCNT_FEATURES 0x50
+#define PRFCNT_FEATURES_COUNTER_BLOCK_SIZE(x) (((x) & GENMASK(7, 0)) << 8)
+#define DISCOVER_DOORBELL_FEATURES 0x60
+#define DISCOVER_MEM_FEATURES 0x100
+#define DISCOVER_MMU_FEATURES 0x108
+#define MMU_FEATURES_AS_COUNT(x) (((x) & GENMASK(23, 16)) >> 16)
+#define DISCOVER_AMBA_FEATURES 0x120
+
+#define DISCOVER_L2_FEATURES 0x128
+#define DISCOVER_TILER_FEATURES 0x200
+#define DISCOVER_CORE_FEATURES 0x300
+#define DISCOVER_THREAD_FEATURES 0x320
+#define DISCOVER_THREAD_MAX_THREADS 0x330
+#define DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY 0x334
+#define DISCOVER_TEXTURE_FEATURES 0x360
+
#endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
index 99311abdf1e9..afb7d68abc87 100644
--- a/drivers/gpu/drm/panthor/panthor_heap.c
+++ b/drivers/gpu/drm/panthor/panthor_heap.c
@@ -11,6 +11,7 @@
#include "panthor_gem.h"
#include "panthor_gpu_regs.h"
#include "panthor_heap.h"
+#include "panthor_hw.h"
#include "panthor_mmu.h"
/*
@@ -105,7 +106,7 @@ struct panthor_heap_pool {
static int panthor_heap_ctx_stride(struct panthor_device *ptdev)
{
- u32 l2_features = ptdev->gpu_info.l2_features;
+ u64 l2_features = panthor_hw_get_l2_features(ptdev);
u32 gpu_cache_line_size = GPU_L2_FEATURES_LINE_SIZE(l2_features);
return ALIGN(HEAP_CONTEXT_SIZE, gpu_cache_line_size);
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index e677f1a8f488..52271fb9db52 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -65,6 +65,23 @@ static struct panthor_hw panthor_hw_arch_v14 = {
},
};
+static struct panthor_hw panthor_hw_arch_v15 = {
+ .ops = {
+ .soft_reset = panthor_pwr_reset_soft,
+ .l2_power_off = panthor_pwr_l2_power_off,
+ .l2_power_on = panthor_pwr_l2_power_on,
+ },
+ .map = {
+ .gpu_control_base = 0x3000,
+ .pwr_control_base = 0x3800,
+ .mcu_control_base = 0x3100,
+ .mmu_as = {
+ .base = 0x2800,
+ .stride = 0x80,
+ },
+ },
+};
+
static struct panthor_hw_entry panthor_hw_match[] = {
{
.arch_min = 10,
@@ -76,6 +93,11 @@ static struct panthor_hw_entry panthor_hw_match[] = {
.arch_max = 14,
.hwdev = &panthor_hw_arch_v14,
},
+ {
+ .arch_min = 15,
+ .arch_max = 15,
+ .hwdev = &panthor_hw_arch_v15,
+ }
};
static int panthor_hw_set_power_tracing(struct device *dev, void *data)
@@ -187,6 +209,12 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
return "Mali-G1-Premium";
case GPU_PROD_ID_MAKE(14, 3):
return "Mali-G1-Pro";
+ case GPU_PROD_ID_MAKE(15, 0):
+ return "Mali-G2-Ultra";
+ case GPU_PROD_ID_MAKE(15, 1):
+ return "Mali-G2-Premium";
+ case GPU_PROD_ID_MAKE(15, 3):
+ return "Mali-G2-Pro";
}
return "(Unknown Mali GPU)";
@@ -210,7 +238,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
return 0;
}
-static int panthor_gpu_info_init(struct panthor_device *ptdev)
+static void panthor_gpu_info_v10_init(struct panthor_device *ptdev)
{
unsigned int i;
@@ -251,12 +279,56 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
ptdev->gpu_info.tiler_present = gpu_read64(gpu_iomem, GPU_TILER_PRESENT);
ptdev->gpu_info.l2_present = gpu_read64(gpu_iomem, GPU_L2_PRESENT);
}
+}
+
+static void panthor_gpu_info_v15_init(struct panthor_device *ptdev)
+{
+ void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
+ u64 texture_features;
+
+ ptdev->gpu_info.gpu_rev_wide = gpu_read64(ptdev->iomem, DISCOVER_REVIDR);
+ ptdev->gpu_info.l2_features_wide = gpu_read64(ptdev->iomem, DISCOVER_L2_FEATURES);
+
+ texture_features = gpu_read64(ptdev->iomem, DISCOVER_TEXTURE_FEATURES);
+ ptdev->gpu_info.texture_features[0] = lower_32_bits(texture_features);
+ ptdev->gpu_info.texture_features[1] = upper_32_bits(texture_features);
+
+ ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, DISCOVER_THREAD_FEATURES);
+ ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, DISCOVER_THREAD_MAX_THREADS);
+ ptdev->gpu_info.thread_num_active_granularity =
+ gpu_read(ptdev->iomem, DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY);
+
+ ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, DISCOVER_GPU_FEATURES);
+
+ /* The following _HI registers do not contain any information (yet) */
+ ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, DISCOVER_MEM_FEATURES);
+ ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, DISCOVER_MMU_FEATURES);
+ ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, DISCOVER_AMBA_FEATURES);
+ ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, DISCOVER_TILER_FEATURES);
+ ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, DISCOVER_CORE_FEATURES);
+
+ /* AS_PRESENT register removed on v15+ create virtual mask from MMU_FEATURES.AS_COUNT */
+ ptdev->gpu_info.as_present =
+ (1U << MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features)) - 1;
+
+ ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
+ ptdev->gpu_info.tiler_present = gpu_read64(pwr_iomem, PWR_TILER_PRESENT);
+ ptdev->gpu_info.shader_present = gpu_read64(pwr_iomem, PWR_SHADER_PRESENT);
+}
+
+static int panthor_gpu_info_init(struct panthor_device *ptdev)
+{
+ if (panthor_hw_has_gpu_discover(ptdev))
+ panthor_gpu_info_v15_init(ptdev);
+ else
+ panthor_gpu_info_v10_init(ptdev);
return overload_shader_present(ptdev);
}
static int panthor_hw_info_init(struct panthor_device *ptdev)
{
+ u64 l2_features = ptdev->gpu_info.l2_features;
u32 major, minor, status;
int ret;
@@ -268,14 +340,17 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
minor = ptdev->gpu_id.ver.minor;
status = ptdev->gpu_id.ver.status;
+ if (panthor_hw_has_gpu_discover(ptdev))
+ l2_features = ptdev->gpu_info.l2_features_wide;
+
drm_info(&ptdev->base,
"%s id 0x%x major 0x%x minor 0x%x status 0x%x",
get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
major, minor, status);
drm_info(&ptdev->base,
- "Features: L2:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
- ptdev->gpu_info.l2_features,
+ "Features: L2:%#llx Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
+ l2_features,
ptdev->gpu_info.tiler_features,
ptdev->gpu_info.mem_features,
ptdev->gpu_info.mmu_features,
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 0ae11b78c77e..1b2678ea00db 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -83,4 +83,17 @@ static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
return ptdev->gpu_id.arch.major >= 14;
}
+static inline bool panthor_hw_has_gpu_discover(struct panthor_device *ptdev)
+{
+ return ptdev->gpu_id.arch.major >= 15;
+}
+
+static inline u64 panthor_hw_get_l2_features(struct panthor_device *ptdev)
+{
+ if (panthor_hw_has_gpu_discover(ptdev))
+ return ptdev->gpu_info.l2_features_wide;
+
+ return ptdev->gpu_info.l2_features;
+}
+
#endif /* __PANTHOR_HW_H__ */
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index 04fc9f133152..2ecc50eade27 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -368,8 +368,8 @@ struct drm_panthor_gpu_info {
/** @core_features: Used to discriminate core variants when they exist. */
__u32 core_features;
- /** @pad: MBZ. */
- __u32 pad;
+ /** @thread_num_active_granularity: Granularity of number of active threads */
+ __u32 thread_num_active_granularity;
/** @gpu_features: Bitmask describing supported GPU-wide features */
__u64 gpu_features;
@@ -383,6 +383,12 @@ struct drm_panthor_gpu_info {
#define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
#define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
#define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
+
+ /** @gpu_rev_wide: 64-bit GPU revision for v15 onwards */
+ __u64 gpu_rev_wide;
+
+ /** @l2_features_wide: 64-bit L2_FEATURES for v15 onwards */
+ __u64 l2_features_wide;
};
/**
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support
2026-05-28 15:05 ` [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support Karunika Choo
@ 2026-06-08 13:59 ` Boris Brezillon
2026-06-11 15:18 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 13:59 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:35 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> @@ -251,12 +279,56 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
> ptdev->gpu_info.tiler_present = gpu_read64(gpu_iomem, GPU_TILER_PRESENT);
> ptdev->gpu_info.l2_present = gpu_read64(gpu_iomem, GPU_L2_PRESENT);
> }
> +}
> +
> +static void panthor_gpu_info_v15_init(struct panthor_device *ptdev)
> +{
> + void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
> + u64 texture_features;
> +
> + ptdev->gpu_info.gpu_rev_wide = gpu_read64(ptdev->iomem, DISCOVER_REVIDR);
> + ptdev->gpu_info.l2_features_wide = gpu_read64(ptdev->iomem, DISCOVER_L2_FEATURES);
> +
> + texture_features = gpu_read64(ptdev->iomem, DISCOVER_TEXTURE_FEATURES);
> + ptdev->gpu_info.texture_features[0] = lower_32_bits(texture_features);
> + ptdev->gpu_info.texture_features[1] = upper_32_bits(texture_features);
> +
> + ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, DISCOVER_THREAD_FEATURES);
> + ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, DISCOVER_THREAD_MAX_THREADS);
> + ptdev->gpu_info.thread_num_active_granularity =
> + gpu_read(ptdev->iomem, DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY);
> +
> + ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, DISCOVER_GPU_FEATURES);
> +
> + /* The following _HI registers do not contain any information (yet) */
AFAICT, this is also the case for DISCOVER_GPU_FEATURES.
> + ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, DISCOVER_MEM_FEATURES);
> + ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, DISCOVER_MMU_FEATURES);
> + ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, DISCOVER_AMBA_FEATURES);
> + ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, DISCOVER_TILER_FEATURES);
> + ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, DISCOVER_CORE_FEATURES);
> +
> + /* AS_PRESENT register removed on v15+ create virtual mask from MMU_FEATURES.AS_COUNT */
> + ptdev->gpu_info.as_present =
> + (1U << MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features)) - 1;
> +
> + ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
> + ptdev->gpu_info.tiler_present = gpu_read64(pwr_iomem, PWR_TILER_PRESENT);
> + ptdev->gpu_info.shader_present = gpu_read64(pwr_iomem, PWR_SHADER_PRESENT);
> +}
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support
2026-05-28 15:05 ` [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support Karunika Choo
2026-06-08 13:59 ` Boris Brezillon
@ 2026-06-11 15:18 ` Steven Price
2026-06-11 16:04 ` Karunika Choo
1 sibling, 1 reply; 42+ messages in thread
From: Steven Price @ 2026-06-11 15:18 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> Add the panthor hardware description for Mali v15 GPUs and hook it into
> device matching.
>
> This includes the v15 register map and product identification needed to
> populate the GPU information exposed by the driver and its uAPI.
>
> Add compatibility string for v15 GPUs.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> drivers/gpu/drm/panthor/panthor_drv.c | 1 +
> drivers/gpu/drm/panthor/panthor_fw.c | 1 +
> .../drm/panthor/panthor_gpu_discover_regs.h | 25 ++++++
> drivers/gpu/drm/panthor/panthor_heap.c | 3 +-
> drivers/gpu/drm/panthor/panthor_hw.c | 81 ++++++++++++++++++-
> drivers/gpu/drm/panthor/panthor_hw.h | 13 +++
> include/uapi/drm/panthor_drm.h | 10 ++-
> 7 files changed, 128 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
> index e8dc4096c1d2..21644a60c8e4 100644
> --- a/drivers/gpu/drm/panthor/panthor_drv.c
> +++ b/drivers/gpu/drm/panthor/panthor_drv.c
> @@ -1875,6 +1875,7 @@ static const struct of_device_id dt_match[] = {
> { .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, },
> { .compatible = "rockchip,rk3588-mali" },
> { .compatible = "arm,mali-valhall-csf" },
> + { .compatible = "arm,mali-gen5-am" },
Just a heads up: a new DT compatible should have a binding document
describing it. So this can't be merged until the DT bindings change is
agreed.
> {}
> };
> MODULE_DEVICE_TABLE(of, dt_match);
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index f6381f5f236f..e2a511b741f4 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -1529,3 +1529,4 @@ MODULE_FIRMWARE("arm/mali/arch11.8/mali_csffw.bin");
> MODULE_FIRMWARE("arm/mali/arch12.8/mali_csffw.bin");
> MODULE_FIRMWARE("arm/mali/arch13.8/mali_csffw.bin");
> MODULE_FIRMWARE("arm/mali/arch14.8/mali_csffw.bin");
> +MODULE_FIRMWARE("arm/mali/arch15.8/mali_csffw.bin");
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> index 54bb104902ee..9c64676dc35a 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> +++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> @@ -14,4 +14,29 @@
> #define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
> #define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
>
> +#define DISCOVER_REVIDR 0x8
> +#define DISCOVER_GPU_FEATURES 0x20
> +#define GPU_FEATURES_VARIABLE_RATE_SHADING BIT(0)
> +#define GPU_FEATURES_SIMD_STATE BIT(1)
> +#define GPU_FEATURES_CROSS_STREAM_SYNC BIT(3)
> +#define GPU_FEATURES_NX BIT(4)
> +#define GPU_FEATURES_RAY_TRAVERSAL BIT(5)
> +#define GPU_FEATURES_LD_LEA_TENSOR_INSTRUCTIONS BIT(6)
> +#define DISCOVER_MCU_FEATURES 0x40
> +#define DISCOVER_PRFCNT_FEATURES 0x50
> +#define PRFCNT_FEATURES_COUNTER_BLOCK_SIZE(x) (((x) & GENMASK(7, 0)) << 8)
> +#define DISCOVER_DOORBELL_FEATURES 0x60
> +#define DISCOVER_MEM_FEATURES 0x100
> +#define DISCOVER_MMU_FEATURES 0x108
> +#define MMU_FEATURES_AS_COUNT(x) (((x) & GENMASK(23, 16)) >> 16)
> +#define DISCOVER_AMBA_FEATURES 0x120
> +
> +#define DISCOVER_L2_FEATURES 0x128
> +#define DISCOVER_TILER_FEATURES 0x200
> +#define DISCOVER_CORE_FEATURES 0x300
> +#define DISCOVER_THREAD_FEATURES 0x320
> +#define DISCOVER_THREAD_MAX_THREADS 0x330
> +#define DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY 0x334
> +#define DISCOVER_TEXTURE_FEATURES 0x360
> +
> #endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
> diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
> index 99311abdf1e9..afb7d68abc87 100644
> --- a/drivers/gpu/drm/panthor/panthor_heap.c
> +++ b/drivers/gpu/drm/panthor/panthor_heap.c
> @@ -11,6 +11,7 @@
> #include "panthor_gem.h"
> #include "panthor_gpu_regs.h"
> #include "panthor_heap.h"
> +#include "panthor_hw.h"
> #include "panthor_mmu.h"
>
> /*
> @@ -105,7 +106,7 @@ struct panthor_heap_pool {
>
> static int panthor_heap_ctx_stride(struct panthor_device *ptdev)
> {
> - u32 l2_features = ptdev->gpu_info.l2_features;
> + u64 l2_features = panthor_hw_get_l2_features(ptdev);
> u32 gpu_cache_line_size = GPU_L2_FEATURES_LINE_SIZE(l2_features);
>
> return ALIGN(HEAP_CONTEXT_SIZE, gpu_cache_line_size);
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
> index e677f1a8f488..52271fb9db52 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.c
> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
> @@ -65,6 +65,23 @@ static struct panthor_hw panthor_hw_arch_v14 = {
> },
> };
>
> +static struct panthor_hw panthor_hw_arch_v15 = {
> + .ops = {
> + .soft_reset = panthor_pwr_reset_soft,
> + .l2_power_off = panthor_pwr_l2_power_off,
> + .l2_power_on = panthor_pwr_l2_power_on,
> + },
> + .map = {
> + .gpu_control_base = 0x3000,
> + .pwr_control_base = 0x3800,
> + .mcu_control_base = 0x3100,
> + .mmu_as = {
> + .base = 0x2800,
> + .stride = 0x80,
> + },
> + },
> +};
> +
> static struct panthor_hw_entry panthor_hw_match[] = {
> {
> .arch_min = 10,
> @@ -76,6 +93,11 @@ static struct panthor_hw_entry panthor_hw_match[] = {
> .arch_max = 14,
> .hwdev = &panthor_hw_arch_v14,
> },
> + {
> + .arch_min = 15,
> + .arch_max = 15,
> + .hwdev = &panthor_hw_arch_v15,
> + }
> };
>
> static int panthor_hw_set_power_tracing(struct device *dev, void *data)
> @@ -187,6 +209,12 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
> return "Mali-G1-Premium";
> case GPU_PROD_ID_MAKE(14, 3):
> return "Mali-G1-Pro";
> + case GPU_PROD_ID_MAKE(15, 0):
> + return "Mali-G2-Ultra";
> + case GPU_PROD_ID_MAKE(15, 1):
> + return "Mali-G2-Premium";
> + case GPU_PROD_ID_MAKE(15, 3):
> + return "Mali-G2-Pro";
> }
>
> return "(Unknown Mali GPU)";
> @@ -210,7 +238,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
> return 0;
> }
>
> -static int panthor_gpu_info_init(struct panthor_device *ptdev)
> +static void panthor_gpu_info_v10_init(struct panthor_device *ptdev)
> {
> unsigned int i;
>
> @@ -251,12 +279,56 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
> ptdev->gpu_info.tiler_present = gpu_read64(gpu_iomem, GPU_TILER_PRESENT);
> ptdev->gpu_info.l2_present = gpu_read64(gpu_iomem, GPU_L2_PRESENT);
> }
> +}
> +
> +static void panthor_gpu_info_v15_init(struct panthor_device *ptdev)
> +{
> + void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
> + u64 texture_features;
> +
> + ptdev->gpu_info.gpu_rev_wide = gpu_read64(ptdev->iomem, DISCOVER_REVIDR);
> + ptdev->gpu_info.l2_features_wide = gpu_read64(ptdev->iomem, DISCOVER_L2_FEATURES);
> +
> + texture_features = gpu_read64(ptdev->iomem, DISCOVER_TEXTURE_FEATURES);
> + ptdev->gpu_info.texture_features[0] = lower_32_bits(texture_features);
> + ptdev->gpu_info.texture_features[1] = upper_32_bits(texture_features);
> +
> + ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, DISCOVER_THREAD_FEATURES);
> + ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, DISCOVER_THREAD_MAX_THREADS);
> + ptdev->gpu_info.thread_num_active_granularity =
> + gpu_read(ptdev->iomem, DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY);
> +
> + ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, DISCOVER_GPU_FEATURES);
> +
> + /* The following _HI registers do not contain any information (yet) */
This is a little confusing as a comment - there are no "_HI" registers
mentioned. I think this would be better reworded as "32 bit reads are
used as the HI part of the 64 bit register does not contian any
information (yet)" (or similar).
Or alternatively you could just upgrade these to 64 bit and be done with
it ;)
> + ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, DISCOVER_MEM_FEATURES);
> + ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, DISCOVER_MMU_FEATURES);
> + ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, DISCOVER_AMBA_FEATURES);
> + ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, DISCOVER_TILER_FEATURES);
> + ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, DISCOVER_CORE_FEATURES);
> +
> + /* AS_PRESENT register removed on v15+ create virtual mask from MMU_FEATURES.AS_COUNT */
> + ptdev->gpu_info.as_present =
> + (1U << MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features)) - 1;
This might be better expressed using GENMASK:
GENMASK(MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features) - 1, 0)
> +
> + ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
> + ptdev->gpu_info.tiler_present = gpu_read64(pwr_iomem, PWR_TILER_PRESENT);
> + ptdev->gpu_info.shader_present = gpu_read64(pwr_iomem, PWR_SHADER_PRESENT);
> +}
> +
> +static int panthor_gpu_info_init(struct panthor_device *ptdev)
> +{
> + if (panthor_hw_has_gpu_discover(ptdev))
> + panthor_gpu_info_v15_init(ptdev);
> + else
> + panthor_gpu_info_v10_init(ptdev);
>
> return overload_shader_present(ptdev);
> }
>
> static int panthor_hw_info_init(struct panthor_device *ptdev)
> {
> + u64 l2_features = ptdev->gpu_info.l2_features;
> u32 major, minor, status;
> int ret;
>
> @@ -268,14 +340,17 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
> minor = ptdev->gpu_id.ver.minor;
> status = ptdev->gpu_id.ver.status;
>
> + if (panthor_hw_has_gpu_discover(ptdev))
> + l2_features = ptdev->gpu_info.l2_features_wide;
> +
> drm_info(&ptdev->base,
> "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
> get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
> major, minor, status);
>
> drm_info(&ptdev->base,
> - "Features: L2:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
> - ptdev->gpu_info.l2_features,
> + "Features: L2:%#llx Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
> + l2_features,
> ptdev->gpu_info.tiler_features,
> ptdev->gpu_info.mem_features,
> ptdev->gpu_info.mmu_features,
> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
> index 0ae11b78c77e..1b2678ea00db 100644
> --- a/drivers/gpu/drm/panthor/panthor_hw.h
> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
> @@ -83,4 +83,17 @@ static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
> return ptdev->gpu_id.arch.major >= 14;
> }
>
> +static inline bool panthor_hw_has_gpu_discover(struct panthor_device *ptdev)
> +{
> + return ptdev->gpu_id.arch.major >= 15;
> +}
> +
> +static inline u64 panthor_hw_get_l2_features(struct panthor_device *ptdev)
> +{
> + if (panthor_hw_has_gpu_discover(ptdev))
> + return ptdev->gpu_info.l2_features_wide;
> +
> + return ptdev->gpu_info.l2_features;
> +}
> +
> #endif /* __PANTHOR_HW_H__ */
> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index 04fc9f133152..2ecc50eade27 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -368,8 +368,8 @@ struct drm_panthor_gpu_info {
> /** @core_features: Used to discriminate core variants when they exist. */
> __u32 core_features;
>
> - /** @pad: MBZ. */
> - __u32 pad;
> + /** @thread_num_active_granularity: Granularity of number of active threads */
> + __u32 thread_num_active_granularity;
>
> /** @gpu_features: Bitmask describing supported GPU-wide features */
> __u64 gpu_features;
> @@ -383,6 +383,12 @@ struct drm_panthor_gpu_info {
> #define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
> #define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
> #define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
> +
> + /** @gpu_rev_wide: 64-bit GPU revision for v15 onwards */
> + __u64 gpu_rev_wide;
> +
> + /** @l2_features_wide: 64-bit L2_FEATURES for v15 onwards */
> + __u64 l2_features_wide;
> };
>
> /**
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support
2026-06-11 15:18 ` Steven Price
@ 2026-06-11 16:04 ` Karunika Choo
2026-06-12 16:01 ` Steven Price
0 siblings, 1 reply; 42+ messages in thread
From: Karunika Choo @ 2026-06-11 16:04 UTC (permalink / raw)
To: Steven Price, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 11/06/2026 16:18, Steven Price wrote:
> On 28/05/2026 16:05, Karunika Choo wrote:
>> Add the panthor hardware description for Mali v15 GPUs and hook it into
>> device matching.
>>
>> This includes the v15 register map and product identification needed to
>> populate the GPU information exposed by the driver and its uAPI.
>>
>> Add compatibility string for v15 GPUs.
>>
>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>> ---
>> drivers/gpu/drm/panthor/panthor_drv.c | 1 +
>> drivers/gpu/drm/panthor/panthor_fw.c | 1 +
>> .../drm/panthor/panthor_gpu_discover_regs.h | 25 ++++++
>> drivers/gpu/drm/panthor/panthor_heap.c | 3 +-
>> drivers/gpu/drm/panthor/panthor_hw.c | 81 ++++++++++++++++++-
>> drivers/gpu/drm/panthor/panthor_hw.h | 13 +++
>> include/uapi/drm/panthor_drm.h | 10 ++-
>> 7 files changed, 128 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
>> index e8dc4096c1d2..21644a60c8e4 100644
>> --- a/drivers/gpu/drm/panthor/panthor_drv.c
>> +++ b/drivers/gpu/drm/panthor/panthor_drv.c
>> @@ -1875,6 +1875,7 @@ static const struct of_device_id dt_match[] = {
>> { .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, },
>> { .compatible = "rockchip,rk3588-mali" },
>> { .compatible = "arm,mali-valhall-csf" },
>> + { .compatible = "arm,mali-gen5-am" },
>
> Just a heads up: a new DT compatible should have a binding document
> describing it. So this can't be merged until the DT bindings change is
> agreed.
>
In this case are we leaning more towards a new arm,mali-gen5-am.yaml
or is it preferrable to update arm/mali-valhall-csf.yaml instead?
>> {}
>> };
>> MODULE_DEVICE_TABLE(of, dt_match);
>> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
>> index f6381f5f236f..e2a511b741f4 100644
>> --- a/drivers/gpu/drm/panthor/panthor_fw.c
>> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
>> @@ -1529,3 +1529,4 @@ MODULE_FIRMWARE("arm/mali/arch11.8/mali_csffw.bin");
>> MODULE_FIRMWARE("arm/mali/arch12.8/mali_csffw.bin");
>> MODULE_FIRMWARE("arm/mali/arch13.8/mali_csffw.bin");
>> MODULE_FIRMWARE("arm/mali/arch14.8/mali_csffw.bin");
>> +MODULE_FIRMWARE("arm/mali/arch15.8/mali_csffw.bin");
>> diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>> index 54bb104902ee..9c64676dc35a 100644
>> --- a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>> +++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>> @@ -14,4 +14,29 @@
>> #define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
>> #define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
>>
>> +#define DISCOVER_REVIDR 0x8
>> +#define DISCOVER_GPU_FEATURES 0x20
>> +#define GPU_FEATURES_VARIABLE_RATE_SHADING BIT(0)
>> +#define GPU_FEATURES_SIMD_STATE BIT(1)
>> +#define GPU_FEATURES_CROSS_STREAM_SYNC BIT(3)
>> +#define GPU_FEATURES_NX BIT(4)
>> +#define GPU_FEATURES_RAY_TRAVERSAL BIT(5)
>> +#define GPU_FEATURES_LD_LEA_TENSOR_INSTRUCTIONS BIT(6)
>> +#define DISCOVER_MCU_FEATURES 0x40
>> +#define DISCOVER_PRFCNT_FEATURES 0x50
>> +#define PRFCNT_FEATURES_COUNTER_BLOCK_SIZE(x) (((x) & GENMASK(7, 0)) << 8)
>> +#define DISCOVER_DOORBELL_FEATURES 0x60
>> +#define DISCOVER_MEM_FEATURES 0x100
>> +#define DISCOVER_MMU_FEATURES 0x108
>> +#define MMU_FEATURES_AS_COUNT(x) (((x) & GENMASK(23, 16)) >> 16)
>> +#define DISCOVER_AMBA_FEATURES 0x120
>> +
>> +#define DISCOVER_L2_FEATURES 0x128
>> +#define DISCOVER_TILER_FEATURES 0x200
>> +#define DISCOVER_CORE_FEATURES 0x300
>> +#define DISCOVER_THREAD_FEATURES 0x320
>> +#define DISCOVER_THREAD_MAX_THREADS 0x330
>> +#define DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY 0x334
>> +#define DISCOVER_TEXTURE_FEATURES 0x360
>> +
>> #endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
>> diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
>> index 99311abdf1e9..afb7d68abc87 100644
>> --- a/drivers/gpu/drm/panthor/panthor_heap.c
>> +++ b/drivers/gpu/drm/panthor/panthor_heap.c
>> @@ -11,6 +11,7 @@
>> #include "panthor_gem.h"
>> #include "panthor_gpu_regs.h"
>> #include "panthor_heap.h"
>> +#include "panthor_hw.h"
>> #include "panthor_mmu.h"
>>
>> /*
>> @@ -105,7 +106,7 @@ struct panthor_heap_pool {
>>
>> static int panthor_heap_ctx_stride(struct panthor_device *ptdev)
>> {
>> - u32 l2_features = ptdev->gpu_info.l2_features;
>> + u64 l2_features = panthor_hw_get_l2_features(ptdev);
>> u32 gpu_cache_line_size = GPU_L2_FEATURES_LINE_SIZE(l2_features);
>>
>> return ALIGN(HEAP_CONTEXT_SIZE, gpu_cache_line_size);
>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
>> index e677f1a8f488..52271fb9db52 100644
>> --- a/drivers/gpu/drm/panthor/panthor_hw.c
>> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
>> @@ -65,6 +65,23 @@ static struct panthor_hw panthor_hw_arch_v14 = {
>> },
>> };
>>
>> +static struct panthor_hw panthor_hw_arch_v15 = {
>> + .ops = {
>> + .soft_reset = panthor_pwr_reset_soft,
>> + .l2_power_off = panthor_pwr_l2_power_off,
>> + .l2_power_on = panthor_pwr_l2_power_on,
>> + },
>> + .map = {
>> + .gpu_control_base = 0x3000,
>> + .pwr_control_base = 0x3800,
>> + .mcu_control_base = 0x3100,
>> + .mmu_as = {
>> + .base = 0x2800,
>> + .stride = 0x80,
>> + },
>> + },
>> +};
>> +
>> static struct panthor_hw_entry panthor_hw_match[] = {
>> {
>> .arch_min = 10,
>> @@ -76,6 +93,11 @@ static struct panthor_hw_entry panthor_hw_match[] = {
>> .arch_max = 14,
>> .hwdev = &panthor_hw_arch_v14,
>> },
>> + {
>> + .arch_min = 15,
>> + .arch_max = 15,
>> + .hwdev = &panthor_hw_arch_v15,
>> + }
>> };
>>
>> static int panthor_hw_set_power_tracing(struct device *dev, void *data)
>> @@ -187,6 +209,12 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
>> return "Mali-G1-Premium";
>> case GPU_PROD_ID_MAKE(14, 3):
>> return "Mali-G1-Pro";
>> + case GPU_PROD_ID_MAKE(15, 0):
>> + return "Mali-G2-Ultra";
>> + case GPU_PROD_ID_MAKE(15, 1):
>> + return "Mali-G2-Premium";
>> + case GPU_PROD_ID_MAKE(15, 3):
>> + return "Mali-G2-Pro";
>> }
>>
>> return "(Unknown Mali GPU)";
>> @@ -210,7 +238,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
>> return 0;
>> }
>>
>> -static int panthor_gpu_info_init(struct panthor_device *ptdev)
>> +static void panthor_gpu_info_v10_init(struct panthor_device *ptdev)
>> {
>> unsigned int i;
>>
>> @@ -251,12 +279,56 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
>> ptdev->gpu_info.tiler_present = gpu_read64(gpu_iomem, GPU_TILER_PRESENT);
>> ptdev->gpu_info.l2_present = gpu_read64(gpu_iomem, GPU_L2_PRESENT);
>> }
>> +}
>> +
>> +static void panthor_gpu_info_v15_init(struct panthor_device *ptdev)
>> +{
>> + void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
>> + u64 texture_features;
>> +
>> + ptdev->gpu_info.gpu_rev_wide = gpu_read64(ptdev->iomem, DISCOVER_REVIDR);
>> + ptdev->gpu_info.l2_features_wide = gpu_read64(ptdev->iomem, DISCOVER_L2_FEATURES);
>> +
>> + texture_features = gpu_read64(ptdev->iomem, DISCOVER_TEXTURE_FEATURES);
>> + ptdev->gpu_info.texture_features[0] = lower_32_bits(texture_features);
>> + ptdev->gpu_info.texture_features[1] = upper_32_bits(texture_features);
>> +
>> + ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, DISCOVER_THREAD_FEATURES);
>> + ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, DISCOVER_THREAD_MAX_THREADS);
>> + ptdev->gpu_info.thread_num_active_granularity =
>> + gpu_read(ptdev->iomem, DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY);
>> +
>> + ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, DISCOVER_GPU_FEATURES);
>> +
>> + /* The following _HI registers do not contain any information (yet) */
>
> This is a little confusing as a comment - there are no "_HI" registers
> mentioned. I think this would be better reworded as "32 bit reads are
> used as the HI part of the 64 bit register does not contian any
> information (yet)" (or similar).
ACK.
> Or alternatively you could just upgrade these to 64 bit and be done with
> it ;)
>
>> + ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, DISCOVER_MEM_FEATURES);
>> + ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, DISCOVER_MMU_FEATURES);
>> + ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, DISCOVER_AMBA_FEATURES);
>> + ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, DISCOVER_TILER_FEATURES);
>> + ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, DISCOVER_CORE_FEATURES);
>> +
>> + /* AS_PRESENT register removed on v15+ create virtual mask from MMU_FEATURES.AS_COUNT */
>> + ptdev->gpu_info.as_present =
>> + (1U << MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features)) - 1;
>
> This might be better expressed using GENMASK:
>
> GENMASK(MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features) - 1, 0)
>
ACK.
Kind regards,
Karunika
>> +
>> + ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
>> + ptdev->gpu_info.tiler_present = gpu_read64(pwr_iomem, PWR_TILER_PRESENT);
>> + ptdev->gpu_info.shader_present = gpu_read64(pwr_iomem, PWR_SHADER_PRESENT);
>> +}
>> +
>> +static int panthor_gpu_info_init(struct panthor_device *ptdev)
>> +{
>> + if (panthor_hw_has_gpu_discover(ptdev))
>> + panthor_gpu_info_v15_init(ptdev);
>> + else
>> + panthor_gpu_info_v10_init(ptdev);
>>
>> return overload_shader_present(ptdev);
>> }
>>
>> static int panthor_hw_info_init(struct panthor_device *ptdev)
>> {
>> + u64 l2_features = ptdev->gpu_info.l2_features;
>> u32 major, minor, status;
>> int ret;
>>
>> @@ -268,14 +340,17 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
>> minor = ptdev->gpu_id.ver.minor;
>> status = ptdev->gpu_id.ver.status;
>>
>> + if (panthor_hw_has_gpu_discover(ptdev))
>> + l2_features = ptdev->gpu_info.l2_features_wide;
>> +
>> drm_info(&ptdev->base,
>> "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
>> get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
>> major, minor, status);
>>
>> drm_info(&ptdev->base,
>> - "Features: L2:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
>> - ptdev->gpu_info.l2_features,
>> + "Features: L2:%#llx Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
>> + l2_features,
>> ptdev->gpu_info.tiler_features,
>> ptdev->gpu_info.mem_features,
>> ptdev->gpu_info.mmu_features,
>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
>> index 0ae11b78c77e..1b2678ea00db 100644
>> --- a/drivers/gpu/drm/panthor/panthor_hw.h
>> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
>> @@ -83,4 +83,17 @@ static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
>> return ptdev->gpu_id.arch.major >= 14;
>> }
>>
>> +static inline bool panthor_hw_has_gpu_discover(struct panthor_device *ptdev)
>> +{
>> + return ptdev->gpu_id.arch.major >= 15;
>> +}
>> +
>> +static inline u64 panthor_hw_get_l2_features(struct panthor_device *ptdev)
>> +{
>> + if (panthor_hw_has_gpu_discover(ptdev))
>> + return ptdev->gpu_info.l2_features_wide;
>> +
>> + return ptdev->gpu_info.l2_features;
>> +}
>> +
>> #endif /* __PANTHOR_HW_H__ */
>> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
>> index 04fc9f133152..2ecc50eade27 100644
>> --- a/include/uapi/drm/panthor_drm.h
>> +++ b/include/uapi/drm/panthor_drm.h
>> @@ -368,8 +368,8 @@ struct drm_panthor_gpu_info {
>> /** @core_features: Used to discriminate core variants when they exist. */
>> __u32 core_features;
>>
>> - /** @pad: MBZ. */
>> - __u32 pad;
>> + /** @thread_num_active_granularity: Granularity of number of active threads */
>> + __u32 thread_num_active_granularity;
>>
>> /** @gpu_features: Bitmask describing supported GPU-wide features */
>> __u64 gpu_features;
>> @@ -383,6 +383,12 @@ struct drm_panthor_gpu_info {
>> #define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
>> #define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
>> #define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
>> +
>> + /** @gpu_rev_wide: 64-bit GPU revision for v15 onwards */
>> + __u64 gpu_rev_wide;
>> +
>> + /** @l2_features_wide: 64-bit L2_FEATURES for v15 onwards */
>> + __u64 l2_features_wide;
>> };
>>
>> /**
>
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support
2026-06-11 16:04 ` Karunika Choo
@ 2026-06-12 16:01 ` Steven Price
0 siblings, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-12 16:01 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 11/06/2026 17:04, Karunika Choo wrote:
> On 11/06/2026 16:18, Steven Price wrote:
>> On 28/05/2026 16:05, Karunika Choo wrote:
>>> Add the panthor hardware description for Mali v15 GPUs and hook it into
>>> device matching.
>>>
>>> This includes the v15 register map and product identification needed to
>>> populate the GPU information exposed by the driver and its uAPI.
>>>
>>> Add compatibility string for v15 GPUs.
>>>
>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>> ---
>>> drivers/gpu/drm/panthor/panthor_drv.c | 1 +
>>> drivers/gpu/drm/panthor/panthor_fw.c | 1 +
>>> .../drm/panthor/panthor_gpu_discover_regs.h | 25 ++++++
>>> drivers/gpu/drm/panthor/panthor_heap.c | 3 +-
>>> drivers/gpu/drm/panthor/panthor_hw.c | 81 ++++++++++++++++++-
>>> drivers/gpu/drm/panthor/panthor_hw.h | 13 +++
>>> include/uapi/drm/panthor_drm.h | 10 ++-
>>> 7 files changed, 128 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
>>> index e8dc4096c1d2..21644a60c8e4 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_drv.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_drv.c
>>> @@ -1875,6 +1875,7 @@ static const struct of_device_id dt_match[] = {
>>> { .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, },
>>> { .compatible = "rockchip,rk3588-mali" },
>>> { .compatible = "arm,mali-valhall-csf" },
>>> + { .compatible = "arm,mali-gen5-am" },
>>
>> Just a heads up: a new DT compatible should have a binding document
>> describing it. So this can't be merged until the DT bindings change is
>> agreed.
>>
>
> In this case are we leaning more towards a new arm,mali-gen5-am.yaml
> or is it preferrable to update arm/mali-valhall-csf.yaml instead?
You've probably got a better idea of how similar it is to
mali-valhall-csf.yaml. I don't have a preference as to how it's
documented - although others might. But we get into issues if the
bindings aren't agreed before the code using them is merged.
Thanks,
Steve
>>> {}
>>> };
>>> MODULE_DEVICE_TABLE(of, dt_match);
>>> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
>>> index f6381f5f236f..e2a511b741f4 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_fw.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
>>> @@ -1529,3 +1529,4 @@ MODULE_FIRMWARE("arm/mali/arch11.8/mali_csffw.bin");
>>> MODULE_FIRMWARE("arm/mali/arch12.8/mali_csffw.bin");
>>> MODULE_FIRMWARE("arm/mali/arch13.8/mali_csffw.bin");
>>> MODULE_FIRMWARE("arm/mali/arch14.8/mali_csffw.bin");
>>> +MODULE_FIRMWARE("arm/mali/arch15.8/mali_csffw.bin");
>>> diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>>> index 54bb104902ee..9c64676dc35a 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>>> +++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
>>> @@ -14,4 +14,29 @@
>>> #define GPU_WIDE_VER_MINOR(x) (((x) & GENMASK(15, 8)) >> 8)
>>> #define GPU_WIDE_VER_STATUS(x) ((x) & GENMASK(7, 0))
>>>
>>> +#define DISCOVER_REVIDR 0x8
>>> +#define DISCOVER_GPU_FEATURES 0x20
>>> +#define GPU_FEATURES_VARIABLE_RATE_SHADING BIT(0)
>>> +#define GPU_FEATURES_SIMD_STATE BIT(1)
>>> +#define GPU_FEATURES_CROSS_STREAM_SYNC BIT(3)
>>> +#define GPU_FEATURES_NX BIT(4)
>>> +#define GPU_FEATURES_RAY_TRAVERSAL BIT(5)
>>> +#define GPU_FEATURES_LD_LEA_TENSOR_INSTRUCTIONS BIT(6)
>>> +#define DISCOVER_MCU_FEATURES 0x40
>>> +#define DISCOVER_PRFCNT_FEATURES 0x50
>>> +#define PRFCNT_FEATURES_COUNTER_BLOCK_SIZE(x) (((x) & GENMASK(7, 0)) << 8)
>>> +#define DISCOVER_DOORBELL_FEATURES 0x60
>>> +#define DISCOVER_MEM_FEATURES 0x100
>>> +#define DISCOVER_MMU_FEATURES 0x108
>>> +#define MMU_FEATURES_AS_COUNT(x) (((x) & GENMASK(23, 16)) >> 16)
>>> +#define DISCOVER_AMBA_FEATURES 0x120
>>> +
>>> +#define DISCOVER_L2_FEATURES 0x128
>>> +#define DISCOVER_TILER_FEATURES 0x200
>>> +#define DISCOVER_CORE_FEATURES 0x300
>>> +#define DISCOVER_THREAD_FEATURES 0x320
>>> +#define DISCOVER_THREAD_MAX_THREADS 0x330
>>> +#define DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY 0x334
>>> +#define DISCOVER_TEXTURE_FEATURES 0x360
>>> +
>>> #endif /* __PANTHOR_GPU_DISCOVER_REGS_H__ */
>>> diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
>>> index 99311abdf1e9..afb7d68abc87 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_heap.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_heap.c
>>> @@ -11,6 +11,7 @@
>>> #include "panthor_gem.h"
>>> #include "panthor_gpu_regs.h"
>>> #include "panthor_heap.h"
>>> +#include "panthor_hw.h"
>>> #include "panthor_mmu.h"
>>>
>>> /*
>>> @@ -105,7 +106,7 @@ struct panthor_heap_pool {
>>>
>>> static int panthor_heap_ctx_stride(struct panthor_device *ptdev)
>>> {
>>> - u32 l2_features = ptdev->gpu_info.l2_features;
>>> + u64 l2_features = panthor_hw_get_l2_features(ptdev);
>>> u32 gpu_cache_line_size = GPU_L2_FEATURES_LINE_SIZE(l2_features);
>>>
>>> return ALIGN(HEAP_CONTEXT_SIZE, gpu_cache_line_size);
>>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
>>> index e677f1a8f488..52271fb9db52 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_hw.c
>>> +++ b/drivers/gpu/drm/panthor/panthor_hw.c
>>> @@ -65,6 +65,23 @@ static struct panthor_hw panthor_hw_arch_v14 = {
>>> },
>>> };
>>>
>>> +static struct panthor_hw panthor_hw_arch_v15 = {
>>> + .ops = {
>>> + .soft_reset = panthor_pwr_reset_soft,
>>> + .l2_power_off = panthor_pwr_l2_power_off,
>>> + .l2_power_on = panthor_pwr_l2_power_on,
>>> + },
>>> + .map = {
>>> + .gpu_control_base = 0x3000,
>>> + .pwr_control_base = 0x3800,
>>> + .mcu_control_base = 0x3100,
>>> + .mmu_as = {
>>> + .base = 0x2800,
>>> + .stride = 0x80,
>>> + },
>>> + },
>>> +};
>>> +
>>> static struct panthor_hw_entry panthor_hw_match[] = {
>>> {
>>> .arch_min = 10,
>>> @@ -76,6 +93,11 @@ static struct panthor_hw_entry panthor_hw_match[] = {
>>> .arch_max = 14,
>>> .hwdev = &panthor_hw_arch_v14,
>>> },
>>> + {
>>> + .arch_min = 15,
>>> + .arch_max = 15,
>>> + .hwdev = &panthor_hw_arch_v15,
>>> + }
>>> };
>>>
>>> static int panthor_hw_set_power_tracing(struct device *dev, void *data)
>>> @@ -187,6 +209,12 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
>>> return "Mali-G1-Premium";
>>> case GPU_PROD_ID_MAKE(14, 3):
>>> return "Mali-G1-Pro";
>>> + case GPU_PROD_ID_MAKE(15, 0):
>>> + return "Mali-G2-Ultra";
>>> + case GPU_PROD_ID_MAKE(15, 1):
>>> + return "Mali-G2-Premium";
>>> + case GPU_PROD_ID_MAKE(15, 3):
>>> + return "Mali-G2-Pro";
>>> }
>>>
>>> return "(Unknown Mali GPU)";
>>> @@ -210,7 +238,7 @@ static int overload_shader_present(struct panthor_device *ptdev)
>>> return 0;
>>> }
>>>
>>> -static int panthor_gpu_info_init(struct panthor_device *ptdev)
>>> +static void panthor_gpu_info_v10_init(struct panthor_device *ptdev)
>>> {
>>> unsigned int i;
>>>
>>> @@ -251,12 +279,56 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
>>> ptdev->gpu_info.tiler_present = gpu_read64(gpu_iomem, GPU_TILER_PRESENT);
>>> ptdev->gpu_info.l2_present = gpu_read64(gpu_iomem, GPU_L2_PRESENT);
>>> }
>>> +}
>>> +
>>> +static void panthor_gpu_info_v15_init(struct panthor_device *ptdev)
>>> +{
>>> + void __iomem *pwr_iomem = ptdev->iomem + ptdev->hw->map.pwr_control_base;
>>> + u64 texture_features;
>>> +
>>> + ptdev->gpu_info.gpu_rev_wide = gpu_read64(ptdev->iomem, DISCOVER_REVIDR);
>>> + ptdev->gpu_info.l2_features_wide = gpu_read64(ptdev->iomem, DISCOVER_L2_FEATURES);
>>> +
>>> + texture_features = gpu_read64(ptdev->iomem, DISCOVER_TEXTURE_FEATURES);
>>> + ptdev->gpu_info.texture_features[0] = lower_32_bits(texture_features);
>>> + ptdev->gpu_info.texture_features[1] = upper_32_bits(texture_features);
>>> +
>>> + ptdev->gpu_info.thread_features = gpu_read(ptdev->iomem, DISCOVER_THREAD_FEATURES);
>>> + ptdev->gpu_info.max_threads = gpu_read(ptdev->iomem, DISCOVER_THREAD_MAX_THREADS);
>>> + ptdev->gpu_info.thread_num_active_granularity =
>>> + gpu_read(ptdev->iomem, DISCOVER_THREAD_NUM_ACTIVE_GRANULARITY);
>>> +
>>> + ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, DISCOVER_GPU_FEATURES);
>>> +
>>> + /* The following _HI registers do not contain any information (yet) */
>>
>> This is a little confusing as a comment - there are no "_HI" registers
>> mentioned. I think this would be better reworded as "32 bit reads are
>> used as the HI part of the 64 bit register does not contian any
>> information (yet)" (or similar).
>
> ACK.
>
>> Or alternatively you could just upgrade these to 64 bit and be done with
>> it ;)
>>
>>> + ptdev->gpu_info.mem_features = gpu_read(ptdev->iomem, DISCOVER_MEM_FEATURES);
>>> + ptdev->gpu_info.mmu_features = gpu_read(ptdev->iomem, DISCOVER_MMU_FEATURES);
>>> + ptdev->gpu_info.coherency_features = gpu_read(ptdev->iomem, DISCOVER_AMBA_FEATURES);
>>> + ptdev->gpu_info.tiler_features = gpu_read(ptdev->iomem, DISCOVER_TILER_FEATURES);
>>> + ptdev->gpu_info.core_features = gpu_read(ptdev->iomem, DISCOVER_CORE_FEATURES);
>>> +
>>> + /* AS_PRESENT register removed on v15+ create virtual mask from MMU_FEATURES.AS_COUNT */
>>> + ptdev->gpu_info.as_present =
>>> + (1U << MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features)) - 1;
>>
>> This might be better expressed using GENMASK:
>>
>> GENMASK(MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features) - 1, 0)
>>
>
> ACK.
>
> Kind regards,
> Karunika
>
>>> +
>>> + ptdev->gpu_info.l2_present = gpu_read64(pwr_iomem, PWR_L2_PRESENT);
>>> + ptdev->gpu_info.tiler_present = gpu_read64(pwr_iomem, PWR_TILER_PRESENT);
>>> + ptdev->gpu_info.shader_present = gpu_read64(pwr_iomem, PWR_SHADER_PRESENT);
>>> +}
>>> +
>>> +static int panthor_gpu_info_init(struct panthor_device *ptdev)
>>> +{
>>> + if (panthor_hw_has_gpu_discover(ptdev))
>>> + panthor_gpu_info_v15_init(ptdev);
>>> + else
>>> + panthor_gpu_info_v10_init(ptdev);
>>>
>>> return overload_shader_present(ptdev);
>>> }
>>>
>>> static int panthor_hw_info_init(struct panthor_device *ptdev)
>>> {
>>> + u64 l2_features = ptdev->gpu_info.l2_features;
>>> u32 major, minor, status;
>>> int ret;
>>>
>>> @@ -268,14 +340,17 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
>>> minor = ptdev->gpu_id.ver.minor;
>>> status = ptdev->gpu_id.ver.status;
>>>
>>> + if (panthor_hw_has_gpu_discover(ptdev))
>>> + l2_features = ptdev->gpu_info.l2_features_wide;
>>> +
>>> drm_info(&ptdev->base,
>>> "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
>>> get_gpu_model_name(ptdev), ptdev->gpu_id.prod_major,
>>> major, minor, status);
>>>
>>> drm_info(&ptdev->base,
>>> - "Features: L2:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
>>> - ptdev->gpu_info.l2_features,
>>> + "Features: L2:%#llx Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
>>> + l2_features,
>>> ptdev->gpu_info.tiler_features,
>>> ptdev->gpu_info.mem_features,
>>> ptdev->gpu_info.mmu_features,
>>> diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
>>> index 0ae11b78c77e..1b2678ea00db 100644
>>> --- a/drivers/gpu/drm/panthor/panthor_hw.h
>>> +++ b/drivers/gpu/drm/panthor/panthor_hw.h
>>> @@ -83,4 +83,17 @@ static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
>>> return ptdev->gpu_id.arch.major >= 14;
>>> }
>>>
>>> +static inline bool panthor_hw_has_gpu_discover(struct panthor_device *ptdev)
>>> +{
>>> + return ptdev->gpu_id.arch.major >= 15;
>>> +}
>>> +
>>> +static inline u64 panthor_hw_get_l2_features(struct panthor_device *ptdev)
>>> +{
>>> + if (panthor_hw_has_gpu_discover(ptdev))
>>> + return ptdev->gpu_info.l2_features_wide;
>>> +
>>> + return ptdev->gpu_info.l2_features;
>>> +}
>>> +
>>> #endif /* __PANTHOR_HW_H__ */
>>> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
>>> index 04fc9f133152..2ecc50eade27 100644
>>> --- a/include/uapi/drm/panthor_drm.h
>>> +++ b/include/uapi/drm/panthor_drm.h
>>> @@ -368,8 +368,8 @@ struct drm_panthor_gpu_info {
>>> /** @core_features: Used to discriminate core variants when they exist. */
>>> __u32 core_features;
>>>
>>> - /** @pad: MBZ. */
>>> - __u32 pad;
>>> + /** @thread_num_active_granularity: Granularity of number of active threads */
>>> + __u32 thread_num_active_granularity;
>>>
>>> /** @gpu_features: Bitmask describing supported GPU-wide features */
>>> __u64 gpu_features;
>>> @@ -383,6 +383,12 @@ struct drm_panthor_gpu_info {
>>> #define DRM_PANTHOR_WIDE_VERSION_MAJOR(x) (((x) >> 16) & 0xff)
>>> #define DRM_PANTHOR_WIDE_VERSION_MINOR(x) (((x) >> 8) & 0xff)
>>> #define DRM_PANTHOR_WIDE_VERSION_STATUS(x) ((x) & 0xff)
>>> +
>>> + /** @gpu_rev_wide: 64-bit GPU revision for v15 onwards */
>>> + __u64 gpu_rev_wide;
>>> +
>>> + /** @l2_features_wide: 64-bit L2_FEATURES for v15 onwards */
>>> + __u64 l2_features_wide;
>>> };
>>>
>>> /**
>>
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (6 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 07/18] drm/panthor: Add Mali v15 hardware support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 14:08 ` Boris Brezillon
2026-06-11 15:34 ` Steven Price
2026-05-28 15:05 ` [RFC PATCH 09/18] drm/panthor: Add basic AM_SYSTEM support Karunika Choo
` (10 subsequent siblings)
18 siblings, 2 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
On Mali v15 AM systems, frequency scaling is handled outside panthor by
the AM_GOVERNOR block, so the GPU DT node may not provide an OPP table.
Make panthor_devfreq_init() return early when operating-points-v2 is
absent, and guard the devfreq helper paths against a missing devfreq
instance.
This keeps devfreq enabled for existing platforms while allowing v15 AM
systems to probe without a local devfreq setup.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_devfreq.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
index 2249b41ca4af..c3252ce54437 100644
--- a/drivers/gpu/drm/panthor/panthor_devfreq.c
+++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
@@ -148,6 +148,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
unsigned long freq = ULONG_MAX;
int ret;
+ if (!device_property_read_bool(ptdev->base.dev, "operating-points-v2"))
+ return 0;
+
pdevfreq = drmm_kzalloc(&ptdev->base, sizeof(*ptdev->devfreq), GFP_KERNEL);
if (!pdevfreq)
return -ENOMEM;
@@ -267,7 +270,7 @@ void panthor_devfreq_resume(struct panthor_device *ptdev)
{
struct panthor_devfreq *pdevfreq = ptdev->devfreq;
- if (!pdevfreq->devfreq)
+ if (!pdevfreq || !pdevfreq->devfreq)
return;
panthor_devfreq_reset(pdevfreq);
@@ -279,7 +282,7 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev)
{
struct panthor_devfreq *pdevfreq = ptdev->devfreq;
- if (!pdevfreq->devfreq)
+ if (!pdevfreq || !pdevfreq->devfreq)
return;
drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq));
@@ -290,7 +293,7 @@ void panthor_devfreq_record_busy(struct panthor_device *ptdev)
struct panthor_devfreq *pdevfreq = ptdev->devfreq;
unsigned long irqflags;
- if (!pdevfreq->devfreq)
+ if (!pdevfreq || !pdevfreq->devfreq)
return;
spin_lock_irqsave(&pdevfreq->lock, irqflags);
@@ -306,7 +309,7 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev)
struct panthor_devfreq *pdevfreq = ptdev->devfreq;
unsigned long irqflags;
- if (!pdevfreq->devfreq)
+ if (!pdevfreq || !pdevfreq->devfreq)
return;
spin_lock_irqsave(&pdevfreq->lock, irqflags);
@@ -323,7 +326,7 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
unsigned long freq = 0;
int ret;
- if (!pdevfreq->devfreq)
+ if (!pdevfreq || !pdevfreq->devfreq)
return 0;
ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present
2026-05-28 15:05 ` [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
@ 2026-06-08 14:08 ` Boris Brezillon
2026-06-08 14:39 ` Karunika Choo
2026-06-11 15:34 ` Steven Price
1 sibling, 1 reply; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 14:08 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:36 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> On Mali v15 AM systems, frequency scaling is handled outside panthor by
> the AM_GOVERNOR block, so the GPU DT node may not provide an OPP table.
OOC, is this still handled as clock frequency changes? I'm asking
because the recent perfcnt work requires registering clk notifiers to
get an approximation of the number of shader/top-level/coregroup clock
cycles on a given period of time so we an calculate relative GPU
utilization, and so far we've only considered clk notifiers as a way to
get informed of these frequency changes.
BTW, this is already problematic for the mediatek GPUEB design, where
devfreq stuff is delegated to an MCU, and we don't currently have a way
to get notified when this MCU changes the frequencies.
>
> Make panthor_devfreq_init() return early when operating-points-v2 is
> absent, and guard the devfreq helper paths against a missing devfreq
> instance.
>
> This keeps devfreq enabled for existing platforms while allowing v15 AM
> systems to probe without a local devfreq setup.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> drivers/gpu/drm/panthor/panthor_devfreq.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
> index 2249b41ca4af..c3252ce54437 100644
> --- a/drivers/gpu/drm/panthor/panthor_devfreq.c
> +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
> @@ -148,6 +148,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
> unsigned long freq = ULONG_MAX;
> int ret;
>
> + if (!device_property_read_bool(ptdev->base.dev, "operating-points-v2"))
> + return 0;
> +
> pdevfreq = drmm_kzalloc(&ptdev->base, sizeof(*ptdev->devfreq), GFP_KERNEL);
> if (!pdevfreq)
> return -ENOMEM;
> @@ -267,7 +270,7 @@ void panthor_devfreq_resume(struct panthor_device *ptdev)
> {
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> panthor_devfreq_reset(pdevfreq);
> @@ -279,7 +282,7 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev)
> {
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq));
> @@ -290,7 +293,7 @@ void panthor_devfreq_record_busy(struct panthor_device *ptdev)
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
> unsigned long irqflags;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> spin_lock_irqsave(&pdevfreq->lock, irqflags);
> @@ -306,7 +309,7 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev)
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
> unsigned long irqflags;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> spin_lock_irqsave(&pdevfreq->lock, irqflags);
> @@ -323,7 +326,7 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
> unsigned long freq = 0;
> int ret;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return 0;
>
> ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present
2026-06-08 14:08 ` Boris Brezillon
@ 2026-06-08 14:39 ` Karunika Choo
2026-06-08 14:51 ` Boris Brezillon
0 siblings, 1 reply; 42+ messages in thread
From: Karunika Choo @ 2026-06-08 14:39 UTC (permalink / raw)
To: Boris Brezillon
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 08/06/2026 15:08, Boris Brezillon wrote:
> On Thu, 28 May 2026 16:05:36 +0100
> Karunika Choo <karunika.choo@arm.com> wrote:
>
>> On Mali v15 AM systems, frequency scaling is handled outside panthor by
>> the AM_GOVERNOR block, so the GPU DT node may not provide an OPP table.
>
> OOC, is this still handled as clock frequency changes? I'm asking
> because the recent perfcnt work requires registering clk notifiers to
> get an approximation of the number of shader/top-level/coregroup clock
> cycles on a given period of time so we an calculate relative GPU
> utilization, and so far we've only considered clk notifiers as a way to
> get informed of these frequency changes.
>
> BTW, this is already problematic for the mediatek GPUEB design, where
> devfreq stuff is delegated to an MCU, and we don't currently have a way
> to get notified when this MCU changes the frequencies.
>
I believe we should still support initializing clocks and registering
notifiers for them. We are just disabling the devfreq subsystem in
this patch.
Would mediatek be able to expose a child clock or logical clock from
ther GPUEB driver to Panthor so that we can get these notifications?
Kind regards,
Karunika Choo
>>
>> Make panthor_devfreq_init() return early when operating-points-v2 is
>> absent, and guard the devfreq helper paths against a missing devfreq
>> instance.
>>
>> This keeps devfreq enabled for existing platforms while allowing v15 AM
>> systems to probe without a local devfreq setup.
>>
>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
>
>> ---
>> drivers/gpu/drm/panthor/panthor_devfreq.c | 13 ++++++++-----
>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
>> index 2249b41ca4af..c3252ce54437 100644
>> --- a/drivers/gpu/drm/panthor/panthor_devfreq.c
>> +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
>> @@ -148,6 +148,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
>> unsigned long freq = ULONG_MAX;
>> int ret;
>>
>> + if (!device_property_read_bool(ptdev->base.dev, "operating-points-v2"))
>> + return 0;
>> +
>> pdevfreq = drmm_kzalloc(&ptdev->base, sizeof(*ptdev->devfreq), GFP_KERNEL);
>> if (!pdevfreq)
>> return -ENOMEM;
>> @@ -267,7 +270,7 @@ void panthor_devfreq_resume(struct panthor_device *ptdev)
>> {
>> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>>
>> - if (!pdevfreq->devfreq)
>> + if (!pdevfreq || !pdevfreq->devfreq)
>> return;
>>
>> panthor_devfreq_reset(pdevfreq);
>> @@ -279,7 +282,7 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev)
>> {
>> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>>
>> - if (!pdevfreq->devfreq)
>> + if (!pdevfreq || !pdevfreq->devfreq)
>> return;
>>
>> drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq));
>> @@ -290,7 +293,7 @@ void panthor_devfreq_record_busy(struct panthor_device *ptdev)
>> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>> unsigned long irqflags;
>>
>> - if (!pdevfreq->devfreq)
>> + if (!pdevfreq || !pdevfreq->devfreq)
>> return;
>>
>> spin_lock_irqsave(&pdevfreq->lock, irqflags);
>> @@ -306,7 +309,7 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev)
>> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>> unsigned long irqflags;
>>
>> - if (!pdevfreq->devfreq)
>> + if (!pdevfreq || !pdevfreq->devfreq)
>> return;
>>
>> spin_lock_irqsave(&pdevfreq->lock, irqflags);
>> @@ -323,7 +326,7 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
>> unsigned long freq = 0;
>> int ret;
>>
>> - if (!pdevfreq->devfreq)
>> + if (!pdevfreq || !pdevfreq->devfreq)
>> return 0;
>>
>> ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
>
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present
2026-06-08 14:39 ` Karunika Choo
@ 2026-06-08 14:51 ` Boris Brezillon
0 siblings, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 14:51 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Mon, 8 Jun 2026 15:39:56 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> On 08/06/2026 15:08, Boris Brezillon wrote:
> > On Thu, 28 May 2026 16:05:36 +0100
> > Karunika Choo <karunika.choo@arm.com> wrote:
> >
> >> On Mali v15 AM systems, frequency scaling is handled outside panthor by
> >> the AM_GOVERNOR block, so the GPU DT node may not provide an OPP table.
> >
> > OOC, is this still handled as clock frequency changes? I'm asking
> > because the recent perfcnt work requires registering clk notifiers to
> > get an approximation of the number of shader/top-level/coregroup clock
> > cycles on a given period of time so we an calculate relative GPU
> > utilization, and so far we've only considered clk notifiers as a way to
> > get informed of these frequency changes.
> >
> > BTW, this is already problematic for the mediatek GPUEB design, where
> > devfreq stuff is delegated to an MCU, and we don't currently have a way
> > to get notified when this MCU changes the frequencies.
> >
>
> I believe we should still support initializing clocks and registering
> notifiers for them. We are just disabling the devfreq subsystem in
> this patch.
>
> Would mediatek be able to expose a child clock or logical clock from
> ther GPUEB driver to Panthor so that we can get these notifications?
Yep, the clk is exposed and I believe the current clk rate is
properly reflected when clk_get_rate() is called, but the clk framework
only issues notification for rate change requests going through
clk_set_rate(), there's no way for a clk provider to report a rate
change AFAIK.
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present
2026-05-28 15:05 ` [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
2026-06-08 14:08 ` Boris Brezillon
@ 2026-06-11 15:34 ` Steven Price
1 sibling, 0 replies; 42+ messages in thread
From: Steven Price @ 2026-06-11 15:34 UTC (permalink / raw)
To: Karunika Choo, dri-devel
Cc: nd, Boris Brezillon, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On 28/05/2026 16:05, Karunika Choo wrote:
> On Mali v15 AM systems, frequency scaling is handled outside panthor by
> the AM_GOVERNOR block, so the GPU DT node may not provide an OPP table.
>
> Make panthor_devfreq_init() return early when operating-points-v2 is
> absent, and guard the devfreq helper paths against a missing devfreq
> instance.
>
> This keeps devfreq enabled for existing platforms while allowing v15 AM
> systems to probe without a local devfreq setup.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> drivers/gpu/drm/panthor/panthor_devfreq.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
> index 2249b41ca4af..c3252ce54437 100644
> --- a/drivers/gpu/drm/panthor/panthor_devfreq.c
> +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
> @@ -148,6 +148,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
> unsigned long freq = ULONG_MAX;
> int ret;
>
> + if (!device_property_read_bool(ptdev->base.dev, "operating-points-v2"))
Don't you want device_property_present() here?
I'm also a little wary of encoding operating-points-v2 as the check -
e.g. it potentially breaks if there's ever a "-v3" or some other way of
describing the opps. Can we use the -ENODEV returned from the
devm_pm_opp functions instead?
> + return 0;
> +
> pdevfreq = drmm_kzalloc(&ptdev->base, sizeof(*ptdev->devfreq), GFP_KERNEL);
> if (!pdevfreq)
> return -ENOMEM;
> @@ -267,7 +270,7 @@ void panthor_devfreq_resume(struct panthor_device *ptdev)
> {
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> panthor_devfreq_reset(pdevfreq);
> @@ -279,7 +282,7 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev)
> {
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq));
> @@ -290,7 +293,7 @@ void panthor_devfreq_record_busy(struct panthor_device *ptdev)
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
> unsigned long irqflags;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> spin_lock_irqsave(&pdevfreq->lock, irqflags);
> @@ -306,7 +309,7 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev)
> struct panthor_devfreq *pdevfreq = ptdev->devfreq;
> unsigned long irqflags;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return;
>
> spin_lock_irqsave(&pdevfreq->lock, irqflags);
> @@ -323,7 +326,7 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
> unsigned long freq = 0;
> int ret;
>
> - if (!pdevfreq->devfreq)
> + if (!pdevfreq || !pdevfreq->devfreq)
> return 0;
This has the unfortunate effect of reporting a bogus frequency in
panthor_gpu_show_fdinfo(). We should avoid reporting a frequency at all
if we don't know it. Or of course even better report the actual
frequency the governor block has set.
Thanks,
Steve
>
> ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
^ permalink raw reply [flat|nested] 42+ messages in thread
* [RFC PATCH 09/18] drm/panthor: Add basic AM_SYSTEM support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (7 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 08/18] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 10/18] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
` (9 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Mali v15 AM systems expose an AM_SYSTEM block for GPU-wide control.
Add a small AM_SYSTEM platform driver, wire it into Kconfig and the
panthor build, and use it to manage the GPU clock and regulator
resources. The driver also performs the initial GPU reset sequence
during probe so the panthor GPU driver can subsequently bind to the
device.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/Kconfig | 7 +
drivers/gpu/drm/panthor/Makefile | 1 +
drivers/gpu/drm/panthor/system/Makefile | 12 +
.../gpu/drm/panthor/system/panthor_system.c | 298 ++++++++++++++++++
4 files changed, 318 insertions(+)
create mode 100644 drivers/gpu/drm/panthor/system/Makefile
create mode 100644 drivers/gpu/drm/panthor/system/panthor_system.c
diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
index 911e7f4810c3..da6b9bc1c67b 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -20,3 +20,10 @@ config DRM_PANTHOR
Note that the Mali-G68 and Mali-G78, while Valhall architecture, will
be supported with the panfrost driver as they are not CSF GPUs.
+
+config DRM_PANTHOR_SYSTEM
+ tristate "GPU-wide configuration for Arm Mali 5th Gen AM GPUs"
+ depends on DRM_PANTHOR
+ depends on OF
+ help
+ Driver for GPU-wide configuration of Arm Mali 5th Gen AM GPUs.
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index dd15d52a88ba..488a5e03efbc 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -14,5 +14,6 @@ panthor-y := \
panthor_sched.o
obj-$(CONFIG_DRM_PANTHOR) += panthor.o
+obj-$(CONFIG_DRM_PANTHOR_SYSTEM) += system/
CFLAGS_panthor_gpu.o := -I$(src)
diff --git a/drivers/gpu/drm/panthor/system/Makefile b/drivers/gpu/drm/panthor/system/Makefile
new file mode 100644
index 000000000000..cafa509148cc
--- /dev/null
+++ b/drivers/gpu/drm/panthor/system/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+# Copyright 2026 ARM Limited. All rights reserved.
+
+obj-$(CONFIG_DRM_PANTHOR_SYSTEM) += panthor_system.o
+
+INCLUDES = \
+ -I$(src)/..
+
+ccflags-y += $(INCLUDES)
+subdir-ccflags-y += $(INCLUDES)
+
+
diff --git a/drivers/gpu/drm/panthor/system/panthor_system.c b/drivers/gpu/drm/panthor/system/panthor_system.c
new file mode 100644
index 000000000000..985a1ecc5d2f
--- /dev/null
+++ b/drivers/gpu/drm/panthor/system/panthor_system.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "panthor_device_io.h"
+
+#define AM_SYS_IRQ_RAWSTAT 0x40
+#define AM_SYS_IRQ_CLEAR 0x4C
+#define AM_SYS_IRQ_MASK 0x58
+#define AM_SYS_IRQ_STATUS 0x70
+#define AM_SYS_IRQ_RESET_COMPLETED BIT(1)
+#define AM_SYS_IRQ_INVALID_COMMAND BIT(2)
+#define AM_SYS_IRQ_INVALID_ACCESS_MASK GENMASK(31, 16)
+#define AM_SYS_IRQ_INVALID_ACCESS(x) FIELD_GET(AM_IRQ_INVALID_ACCESS_MASK, x)
+
+#define AM_SYS_STATUS 0xC0
+#define AM_SYS_COMMAND 0xC4
+#define AM_SYS_CMD_INDEX(x) FIELD_PREP(GENMASK(11, 8), x)
+#define AM_SYS_CMD_SOFT_RESET 0x10
+#define AM_SYS_CMD_HARD_RESET 0x11
+#define AM_SYS_CMD_AW_RESET 0x12
+
+#define AM_SYS_RESET_SLEEP_US 10
+#define AM_RESET_TIMEOUT_US (20 * USEC_PER_MSEC)
+
+/**
+ * struct panthor_system - System device
+ */
+struct panthor_system {
+ /** @dev: Device pointer */
+ struct device *dev;
+
+ /** @iomem: CPU mapping of the AM_SYSTEM IOMEM region */
+ void __iomem *iomem;
+
+ /** @regulator: Pointer to device regulator */
+ struct regulator *regulator;
+
+ /** @clk: Pointer to device clk */
+ struct clk *clk;
+};
+
+static int panthor_system_regulator_init(struct panthor_system *sdev)
+{
+ struct device_node *np = sdev->dev->of_node;
+ struct regulator *regulator = NULL;
+ const char *regulator_name = NULL;
+ int ret, cnt;
+
+ cnt = of_property_count_strings(np, "clock-names");
+ /* No clock-names defined. Exit */
+ if (!cnt || (cnt == -EINVAL))
+ return 0;
+ else if (cnt < 0)
+ return dev_err_probe(sdev->dev, cnt,
+ "Failed to count clock-names");
+
+ ret = of_property_read_string_index(np, "clock-names", 0,
+ ®ulator_name);
+ if (ret)
+ return dev_err_probe(sdev->dev, ret,
+ "Failed to get string from clock-names");
+
+ regulator = devm_regulator_get_optional(sdev->dev, regulator_name);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+
+ /* Regulator not found. Exit */
+ if (ret == -ENODEV)
+ return 0;
+
+ return dev_err_probe(sdev->dev, ret, "Failed to get %s-supply",
+ regulator_name);
+ }
+
+ sdev->regulator = regulator;
+
+ return 0;
+}
+
+static int panthor_system_regulator_resume(struct panthor_system *sdev)
+{
+ int ret;
+
+ if (!sdev->regulator)
+ return 0;
+
+ ret = regulator_enable(sdev->regulator);
+ if (ret) {
+ dev_err(sdev->dev, "Failed to enable regulator");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_system_regulator_suspend(struct panthor_system *sdev)
+{
+ int ret;
+
+ if (!sdev->regulator)
+ return 0;
+
+ ret = regulator_disable(sdev->regulator);
+ if (ret) {
+ dev_err(sdev->dev, "Failed to disable regulator");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_system_clk_init(struct panthor_system *sdev)
+{
+ struct clk *clk = NULL;
+
+ clk = devm_clk_get(sdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(sdev->dev, PTR_ERR(clk),
+ "Failed to get clk");
+
+ sdev->clk = clk;
+
+ return 0;
+}
+
+static int panthor_system_clk_resume(struct panthor_system *sdev)
+{
+ int ret;
+
+ ret = clk_prepare_enable(sdev->clk);
+ if (ret) {
+ dev_err(sdev->dev, "Failed to enable clk: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void panthor_system_clk_suspend(struct panthor_system *sdev)
+{
+ clk_disable_unprepare(sdev->clk);
+}
+
+static int __panthor_system_reset(struct panthor_system *sdev, u32 cmd)
+{
+ u64 val;
+
+ gpu_write(sdev->iomem, AM_SYS_COMMAND, cmd);
+
+ return read_poll_timeout(gpu_read64, val,
+ (val & AM_SYS_IRQ_RESET_COMPLETED),
+ AM_SYS_RESET_SLEEP_US, AM_RESET_TIMEOUT_US, false,
+ sdev->iomem, AM_SYS_IRQ_RAWSTAT);
+}
+
+static int panthor_system_reset(struct panthor_system *sdev)
+{
+ int ret;
+
+ ret = __panthor_system_reset(sdev, AM_SYS_CMD_SOFT_RESET);
+ if (ret) {
+ dev_err(sdev->dev, "SOFT_RESET failed, attempting HARD_RESET");
+
+ ret = __panthor_system_reset(sdev, AM_SYS_CMD_HARD_RESET);
+ if (ret) {
+ dev_err(sdev->dev, "HARD_RESET failed");
+ return -EIO;
+ }
+ }
+
+ gpu_write64(sdev->iomem, AM_SYS_IRQ_CLEAR, AM_SYS_IRQ_RESET_COMPLETED);
+
+ return 0;
+}
+
+static int panthor_system_suspend(struct device *dev)
+{
+ struct panthor_system *sdev = dev_get_drvdata(dev);
+
+ panthor_system_clk_suspend(sdev);
+ return panthor_system_regulator_suspend(sdev);
+}
+
+static int panthor_system_resume(struct device *dev)
+{
+ struct panthor_system *sdev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = panthor_system_regulator_resume(sdev);
+ if (ret)
+ return ret;
+
+ ret = panthor_system_clk_resume(sdev);
+ if (ret)
+ goto err_regulator_suspend;
+
+ return 0;
+
+err_regulator_suspend:
+ panthor_system_regulator_suspend(sdev);
+
+ return ret;
+}
+
+static int panthor_system_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct panthor_system *sdev;
+ void __iomem *iomem;
+ int ret;
+
+ sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return -ENOMEM;
+
+ iomem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(iomem)) {
+ dev_err(dev, "Failed to ioremap");
+ return PTR_ERR(iomem);
+ }
+
+ sdev->dev = dev;
+ sdev->iomem = iomem;
+
+ ret = panthor_system_regulator_init(sdev);
+ if (ret)
+ return ret;
+
+ ret = panthor_system_clk_init(sdev);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, sdev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM");
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to resume device");
+
+ ret = panthor_system_reset(sdev);
+ if (ret) {
+ dev_err(dev, "Failed to reset GPU");
+ goto err_exit;
+ }
+
+ /* TODO: IRQs */
+
+err_exit:
+ pm_runtime_put_sync_suspend(dev);
+
+ return ret;
+}
+
+static void panthor_system_remove(struct platform_device *pdev)
+{
+}
+
+static const struct dev_pm_ops panthor_system_pm_ops = {
+ .suspend = pm_runtime_force_suspend,
+ .resume = pm_runtime_force_resume,
+ .runtime_suspend = panthor_system_suspend,
+ .runtime_resume = panthor_system_resume,
+};
+
+static const struct of_device_id panthor_system_dt_match[] = {
+ { .compatible = "arm,mali-gen5-am-system" },
+ {}
+};
+
+static struct platform_driver panthor_system_driver = {
+ .probe = panthor_system_probe,
+ .remove = panthor_system_remove,
+ .driver = {
+ .name = "panthor_system",
+ .pm = pm_ptr(&panthor_system_pm_ops),
+ .of_match_table = panthor_system_dt_match,
+ },
+};
+
+module_platform_driver(panthor_system_driver);
+
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_DESCRIPTION("GPU system driver for AM GPUs");
+MODULE_LICENSE("Dual MIT/GPL");
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 10/18] drm/panthor: Add AM_PARTITION_CONTROL support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (8 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 09/18] drm/panthor: Add basic AM_SYSTEM support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 11/18] drm/panthor: Add AM message helpers Karunika Choo
` (8 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Mali v15 AM GPUs expose AM_PARTITION_CONTROL blocks to manage partition
access windows.
Add a panthor arbitration platform driver and wire it into Kconfig and
the panthor build. The driver maps pcN partition-control resources,
handles RESET_DONE and INVALID_COMMAND interrupts, and provides helpers
to open, close and yield partition access windows.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/Kconfig | 7 +
drivers/gpu/drm/panthor/Makefile | 1 +
drivers/gpu/drm/panthor/arbitration/Makefile | 15 +
.../panthor/arbitration/panthor_arbitration.h | 24 ++
.../arbitration/panthor_arbitration_drv.c | 127 ++++++
.../arbitration/panthor_partition_control.c | 381 ++++++++++++++++++
.../arbitration/panthor_partition_control.h | 27 ++
7 files changed, 582 insertions(+)
create mode 100644 drivers/gpu/drm/panthor/arbitration/Makefile
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h
diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
index da6b9bc1c67b..55b9b80f45d1 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -27,3 +27,10 @@ config DRM_PANTHOR_SYSTEM
depends on OF
help
Driver for GPU-wide configuration of Arm Mali 5th Gen AM GPUs.
+
+config DRM_PANTHOR_ARBITRATION
+ tristate "HW-assisted virtualization for Mali 5th Gen AM GPUs"
+ depends on DRM_PANTHOR
+ depends on OF
+ help
+ HW-assisted virtualization driver for Mali 5th Gen AM GPUs.
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 488a5e03efbc..5d4d0ae64952 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -15,5 +15,6 @@ panthor-y := \
obj-$(CONFIG_DRM_PANTHOR) += panthor.o
obj-$(CONFIG_DRM_PANTHOR_SYSTEM) += system/
+obj-$(CONFIG_DRM_PANTHOR_ARBITRATION) += arbitration/
CFLAGS_panthor_gpu.o := -I$(src)
diff --git a/drivers/gpu/drm/panthor/arbitration/Makefile b/drivers/gpu/drm/panthor/arbitration/Makefile
new file mode 100644
index 000000000000..5d5b2b8d84bc
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+# Copyright 2026 ARM Limited. All rights reserved.
+
+obj-$(CONFIG_DRM_PANTHOR_ARBITRATION) += panthor_arbitration.o
+panthor_arbitration-y := \
+ panthor_arbitration_drv.o \
+ panthor_partition_control.o
+
+INCLUDES = \
+ -I$(src)/..
+
+ccflags-y += $(INCLUDES)
+subdir-ccflags-y += $(INCLUDES)
+
+
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
new file mode 100644
index 000000000000..5e35ae8463c4
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_ARBITRATION_H__
+#define __PANTHOR_ARBITRATION_H__
+
+struct device;
+struct panthor_partition_control;
+
+#define AM_ARB_MAX_PC_COUNT 1
+#define AM_ARB_MAX_AW_COUNT 16
+
+/**
+ * struct panthor_arbitration - Arbitration device
+ */
+struct panthor_arbitration {
+ /** @dev: Device pointer */
+ struct device *dev;
+
+ /** @pc: Pointer array to partition control data */
+ struct panthor_partition_control *pc[AM_ARB_MAX_PC_COUNT];
+};
+
+#endif
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
new file mode 100644
index 000000000000..07a566168e6a
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+
+#include "panthor_arbitration.h"
+#include "panthor_partition_control.h"
+
+#define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
+
+static int panthor_arbitration_runtime_suspend(struct device *dev)
+{
+ struct panthor_arbitration *adev = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = panthor_partition_control_suspend(adev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int panthor_arbitration_runtime_resume(struct device *dev)
+{
+ struct panthor_arbitration *adev = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = panthor_partition_control_resume(adev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int panthor_arbitration_probe(struct platform_device *pdev)
+{
+ struct panthor_arbitration *adev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ if (!pdev)
+ return -EINVAL;
+
+ adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->dev = dev;
+
+ dev_set_drvdata(dev, adev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ ret = panthor_partition_control_init(adev);
+ if (ret)
+ goto err_out;
+
+ pm_runtime_set_autosuspend_delay(dev, PANTHOR_PM_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_out:
+ pm_runtime_put_noidle(dev);
+ return ret;
+}
+
+static void panthor_arbitration_remove(struct platform_device *pdev)
+{
+ struct panthor_arbitration *adev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!adev)
+ return;
+
+ ret = pm_runtime_resume_and_get(adev->dev);
+ if (ret < 0)
+ goto out_suspended;
+
+ panthor_partition_control_term(adev);
+
+ pm_runtime_put_noidle(adev->dev);
+
+out_suspended:
+ pm_runtime_set_suspended(adev->dev);
+}
+
+static const struct dev_pm_ops panthor_arbitration_pm_ops = {
+ .resume = pm_runtime_force_resume,
+ .suspend = pm_runtime_force_suspend,
+ .runtime_resume = panthor_arbitration_runtime_resume,
+ .runtime_suspend = panthor_arbitration_runtime_suspend,
+};
+
+static const struct of_device_id panthor_arbitration_of_match[] = {
+ { .compatible = "arm,mali-gen5-am-arbitration" },
+ { /* Sentinel */ },
+};
+
+static struct platform_driver panthor_arbitration_driver = {
+ .probe = panthor_arbitration_probe,
+ .remove = panthor_arbitration_remove,
+ .driver = {
+ .name = "panthor-arbitration",
+ .of_match_table = panthor_arbitration_of_match,
+ .pm = pm_ptr(&panthor_arbitration_pm_ops),
+ },
+};
+
+module_platform_driver(panthor_arbitration_driver);
+
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_DESCRIPTION("Panthor HW-assisted virtualization Driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
new file mode 100644
index 000000000000..f4bad839610d
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "panthor_arbitration.h"
+#include "panthor_device_io.h"
+#include "panthor_partition_control.h"
+
+#define AM_PART_IRQ_RAWSTAT 0x40
+#define AM_PART_IRQ_CLEAR 0x44
+#define AM_PART_IRQ_MASK 0x48
+#define AM_PART_IRQ_STATUS 0x4C
+#define PART_RESET_DONE BIT(3)
+#define PART_INVALID_COMMAND BIT(6)
+
+#define AM_PART_STATE 0x60
+#define AM_PART_STATE_GET(x) FIELD_GET(GENMASK(11, 8), x)
+#define PART_STATE_RESET 0
+#define PART_STATE_WINDOW_OPENING 1
+#define PART_STATE_WINDOW_OPEN 7
+#define PART_STATE_WINDOW_CLOSED 8
+#define AM_PART_STATE_WINDOW_GET(x) FIELD_GET(GENMASK(15, 12), x)
+
+#define AM_PART_COMMAND 0x100
+#define AM_PART_SET_COMMAND(x) FIELD_PREP(GENMASK(7, 0), x)
+#define PART_CMD_YIELD_IDLE 0x10
+#define PART_CMD_YIELD_NOW 0x11
+#define PART_CMD_CLOSE_WINDOW 0x20
+#define PART_CMD_OPEN_WINDOW 0x21
+#define AM_PART_SET_WINDOW(x) FIELD_PREP(GENMASK(11, 8), x)
+
+#define PART_CONTROL_IRQ_MASK \
+ (PART_RESET_DONE | PART_INVALID_COMMAND)
+
+#define PART_REG_POLL_SLEEP_US 10
+#define PART_STATE_TRANSITION_TIMEOUT_US 5000000
+
+/**
+ * struct panthor_partition_control - Partition control data
+ */
+struct panthor_partition_control {
+ /** @dev: Device pointer */
+ struct device *dev;
+
+ /** @name: Resource name */
+ char *name;
+
+ /** @iomem: CPU mapping of the partition control IOMEM region */
+ void __iomem *iomem;
+
+ /** @irq: IRQ number */
+ int irq;
+
+ /** @lock: Proctects partition control state data */
+ spinlock_t lock;
+
+ /** @current_aw: currently open access window */
+ int current_aw;
+
+ /** @closing: synchronous closing of the partition */
+ bool closing;
+
+ /** @waitqueue: Event wait queue. Not IRQ safe */
+ wait_queue_head_t waitqueue;
+};
+
+static void partition_irq_suspend(struct panthor_partition_control *pc)
+{
+ gpu_write(pc->iomem, AM_PART_IRQ_MASK, 0);
+}
+
+static void partition_irq_resume(struct panthor_partition_control *pc)
+{
+ gpu_write(pc->iomem, AM_PART_IRQ_MASK, PART_CONTROL_IRQ_MASK);
+}
+
+static u32 partition_state_get(struct panthor_partition_control *pc)
+{
+ const u32 state = gpu_read(pc->iomem, AM_PART_STATE);
+
+ return AM_PART_STATE_GET(state);
+}
+
+static u8 partition_aw_get(struct panthor_partition_control *pc)
+{
+ const u32 state = gpu_read(pc->iomem, AM_PART_STATE);
+
+ return AM_PART_STATE_WINDOW_GET(state);
+}
+
+static int partition_state_wait(struct panthor_partition_control *pc, u32 state)
+{
+ u32 partition_state;
+
+ return read_poll_timeout_atomic(partition_state_get, partition_state,
+ partition_state == state,
+ PART_REG_POLL_SLEEP_US,
+ PART_STATE_TRANSITION_TIMEOUT_US,
+ false, pc);
+}
+
+static int partition_state_wait_transition(struct panthor_partition_control *pc)
+{
+ int ret;
+
+again:
+ switch (partition_state_get(pc)) {
+ case PART_STATE_WINDOW_CLOSED:
+ ret = partition_state_wait(pc, PART_STATE_RESET);
+ if (ret)
+ return ret;
+ goto again;
+ case PART_STATE_WINDOW_OPENING:
+ ret = partition_state_wait(pc, PART_STATE_WINDOW_OPEN);
+ if (ret)
+ return ret;
+ goto again;
+ case PART_STATE_RESET:
+ case PART_STATE_WINDOW_OPEN:
+ return 0;
+ default:
+ dev_err(pc->dev, "%s: Unknown state %u", pc->name,
+ partition_state_get(pc));
+ return -EINVAL;
+ }
+}
+
+static int yield_now(struct panthor_partition_control *pc)
+{
+ int ret;
+
+ ret = partition_state_wait_transition(pc);
+ if (ret)
+ return ret;
+
+ /* Partition already reset, nothing to yield */
+ if (partition_state_get(pc) == PART_STATE_RESET)
+ return 0;
+
+ gpu_write(pc->iomem, AM_PART_COMMAND,
+ AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW));
+
+ return 0;
+}
+
+static int window_close(struct panthor_partition_control *pc)
+{
+ int ret;
+
+ ret = partition_state_wait_transition(pc);
+ if (ret)
+ return ret;
+
+ /* Partition already closed. */
+ if (partition_state_get(pc) == PART_STATE_RESET)
+ return 0;
+
+ scoped_guard(spinlock_irqsave, &pc->lock)
+ pc->closing = true;
+
+ gpu_write(pc->iomem, AM_PART_COMMAND,
+ AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW));
+
+ /*
+ * Wait for RESET_DONE interrupt to clear pc->closing. Prevents stale
+ * RESET_DONE from being misinterpreted as RESET_DONE due to YIELD_DONE.
+ */
+ if (!wait_event_timeout(
+ pc->waitqueue, !pc->closing,
+ usecs_to_jiffies(PART_STATE_TRANSITION_TIMEOUT_US))) {
+ if (pc->closing) {
+ dev_err(pc->dev,
+ "%s: Timed out waiting for partition to close: %d",
+ pc->name, ret);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int window_open(struct panthor_partition_control *pc, u8 aw_id)
+{
+ int ret;
+
+ if (aw_id >= AM_ARB_MAX_AW_COUNT)
+ return -EINVAL;
+
+ ret = partition_state_wait_transition(pc);
+ if (ret)
+ return ret;
+
+ if (partition_state_get(pc) == PART_STATE_WINDOW_OPEN) {
+ dev_warn(pc->dev,
+ "%s: Window opem for %u Failed. Already opened. aw=%u, state=%u",
+ pc->name, aw_id, partition_aw_get(pc),
+ partition_state_get(pc));
+ return 0;
+ }
+
+ gpu_write(pc->iomem, AM_PART_COMMAND,
+ AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) | AM_PART_SET_WINDOW(aw_id));
+
+ ret = partition_state_wait(pc, PART_STATE_WINDOW_OPEN);
+ if (ret) {
+ dev_err(pc->dev, "%s: Timed out waiting for partition to open: %d",
+ pc->name, ret);
+ return ret;
+ }
+
+ scoped_guard(spinlock_irqsave, &pc->lock)
+ pc->current_aw = aw_id;
+
+ return 0;
+}
+
+static void partition_handle_reset_done(struct panthor_partition_control *pc)
+{
+ scoped_guard(spinlock_irqsave, &pc->lock) {
+ pc->current_aw = -1;
+
+ /* RESET_DONE from CLOSE_WINDOW */
+ if (pc->closing)
+ pc->closing = false;
+ }
+
+ wake_up_all(&pc->waitqueue);
+}
+
+static irqreturn_t partition_irq_raw_handler(int irq, void *data)
+{
+ struct panthor_partition_control *pc = data;
+ u32 status;
+
+ status = gpu_read(pc->iomem, AM_PART_IRQ_STATUS);
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & PART_RESET_DONE)
+ partition_handle_reset_done(pc);
+
+ if (status & PART_INVALID_COMMAND)
+ dev_warn(pc->dev, "%s: Invalid command", pc->name);
+
+ gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, status);
+
+ return IRQ_HANDLED;
+}
+
+int panthor_partition_control_suspend(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_partition_control *pc = adev->pc[i];
+
+ if (!pc)
+ continue;
+
+ partition_irq_suspend(pc);
+ }
+
+ return 0;
+}
+
+int panthor_partition_control_resume(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_partition_control *pc = adev->pc[i];
+
+ if (!pc)
+ continue;
+
+ partition_irq_resume(pc);
+ }
+
+ return 0;
+}
+
+void panthor_partition_control_term(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_partition_control *pc = adev->pc[i];
+
+ if (!pc)
+ continue;
+
+ if (!WARN_ON(window_close(pc)))
+ WARN_ON(partition_state_wait(pc, PART_STATE_RESET));
+
+ partition_irq_suspend(pc);
+ }
+}
+
+static int partition_control_init(struct panthor_arbitration *adev, int i)
+{
+ struct device *dev = adev->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct panthor_partition_control *pc;
+ struct resource *res;
+ void __iomem *iomem;
+ char *name;
+ int irq;
+ int ret;
+
+ if (i >= AM_ARB_MAX_PC_COUNT)
+ return -EINVAL;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "pc%d", i);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res) {
+ devm_kfree(dev, name);
+ return -ENODEV;
+ }
+
+ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc)
+ return -ENOMEM;
+
+ iomem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(iomem)) {
+ return dev_err_probe(dev, PTR_ERR(iomem),
+ "%s: Failed to ioremap", name);
+ }
+
+ pc->name = name;
+ pc->dev = dev;
+ pc->iomem = iomem;
+
+ init_waitqueue_head(&pc->waitqueue);
+ spin_lock_init(&pc->lock);
+ pc->current_aw = -1;
+
+ adev->pc[i] = pc;
+
+ irq = platform_get_irq_byname(pdev, name);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(
+ dev, irq, partition_irq_raw_handler, IRQF_SHARED,
+ devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), pc);
+ if (ret)
+ return ret;
+
+ partition_irq_resume(pc);
+
+ return 0;
+}
+
+int panthor_partition_control_init(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ int ret = partition_control_init(adev, i);
+ if (ret == -ENODEV)
+ continue;
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int panthor_partition_control_open_window(struct panthor_partition_control *pc, u8 aw_id)
+{
+ return window_open(pc, aw_id);
+}
+
+int panthor_partition_control_close_window(struct panthor_partition_control *pc)
+{
+ return window_close(pc);
+}
+
+int panthor_partition_control_yield_now(struct panthor_partition_control *pc)
+{
+ return yield_now(pc);
+}
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h
new file mode 100644
index 000000000000..bd935de04137
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_PARTITION_CONTROL_H__
+#define __PANTHOR_PARTITION_CONTROL_H__
+
+#include <linux/types.h>
+
+struct device;
+struct panthor_arbitration;
+struct panthor_partition_control;
+
+int panthor_partition_control_init(struct panthor_arbitration *adev);
+
+void panthor_partition_control_term(struct panthor_arbitration *adev);
+
+int panthor_partition_control_suspend(struct panthor_arbitration *adev);
+
+int panthor_partition_control_resume(struct panthor_arbitration *adev);
+
+int panthor_partition_control_open_window(struct panthor_partition_control *pc, u8 aw_id);
+
+int panthor_partition_control_close_window(struct panthor_partition_control *pc);
+
+int panthor_partition_control_yield_now(struct panthor_partition_control *pc);
+
+#endif
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 11/18] drm/panthor: Add AM message helpers
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (9 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 10/18] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 12/18] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
` (7 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Add a shared helper layer for the AM_MESSAGE registers used by v15 GPUs
to coordinate between access windows to enable virtualisation of the
GPU.
The helper defines message IDs, register accessors, version negotiation,
and FIFO-backed sending. Outgoing messages are serialized with a
spinlock, written in the required register order, and queued for later
retry when the hardware outgoing slot is busy.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_am_msg.h | 157 +++++++++++++++++++++++
1 file changed, 157 insertions(+)
create mode 100644 drivers/gpu/drm/panthor/panthor_am_msg.h
diff --git a/drivers/gpu/drm/panthor/panthor_am_msg.h b/drivers/gpu/drm/panthor/panthor_am_msg.h
new file mode 100644
index 000000000000..bc4dedcd03ba
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_am_msg.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_AM_H__
+#define __PANTHOR_AM_H__
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/kfifo.h>
+#include <linux/types.h>
+
+#include "panthor_device_io.h"
+
+/* Protocol versioning */
+#define AM_MSG_MIN_SUPPORTED_VERSION 0x1
+#define AM_MSG_CURRENT_VERSION 0x1
+
+#define AM_INCOMING_MESSAGE0 0x0
+#define AM_INCOMING_MESSAGE1 0x4
+#define AM_OUTGOING_MESSAGE_STATUS 0x8
+#define AM_OUTGOING_MESSAGE0 0xc
+#define AM_OUTGOING_MESSAGE1 0x10
+
+/* Arbiter to VM message IDs */
+#define ARB_VM_GPU_STOP 0x01
+#define ARB_VM_INIT 0x04
+
+/* VM to Arbiter message IDs */
+#define VM_ARB_INIT 0x05
+#define VM_ARB_GPU_REQUEST 0x08
+#define VM_ARB_GPU_STOPPED 0x09
+
+#define AM_MSG_ID_MASK GENMASK(7, 0)
+#define AM_MSG_ACK_MASK BIT(8)
+#define AM_MSG_VERSION_MASK GENMASK(15, 9)
+
+#define AM_MSG_ID_GET(x) FIELD_GET(AM_MSG_ID_MASK, x)
+#define AM_MSG_ACK_GET(x) FIELD_GET(AM_MSG_ACK_MASK, x)
+#define AM_MSG_VERSION_GET(x) FIELD_GET(AM_MSG_VERSION_MASK, x)
+
+#define AM_MSG_INIT_MAKE(_id, _ack, _version) \
+ ((_id) | FIELD_PREP(AM_MSG_ACK_MASK, _ack) | FIELD_PREP(AM_MSG_VERSION_MASK, _version))
+
+#define ARB_VM_INIT_MAKE(_ack, _version) AM_MSG_INIT_MAKE(ARB_VM_INIT, _ack, _version)
+#define VM_ARB_INIT_MAKE(_ack, _version) AM_MSG_INIT_MAKE(VM_ARB_INIT, _ack, _version)
+
+#define AM_MSG_DEFAULT_FIFO_SIZE 4
+
+struct panthor_am_msg {
+ /** @iomem: CPU mapping of the AM_MESSAGE registers */
+ void __iomem *iomem;
+
+ /** @lock: Lock for send_fifo and outgoing message register */
+ spinlock_t lock;
+
+ /** @send_fifo: FIFO to queue of messages to send */
+ DECLARE_KFIFO(send_fifo, u64, AM_MSG_DEFAULT_FIFO_SIZE);
+
+ /** @version: Protocol version */
+ u8 version;
+};
+
+static inline void panthor_am_msg_init(struct panthor_am_msg *msg,
+ void __iomem *iomem)
+{
+ spin_lock_init(&msg->lock);
+ INIT_KFIFO(msg->send_fifo);
+ msg->iomem = iomem;
+}
+
+static inline bool panthor_am_msg_pending(struct panthor_am_msg *msg)
+{
+ return !!gpu_read(msg->iomem, AM_OUTGOING_MESSAGE_STATUS);
+}
+
+static inline u64 panthor_am_msg_read(struct panthor_am_msg *msg)
+{
+ return gpu_read64(msg->iomem, AM_INCOMING_MESSAGE0);
+}
+
+static inline void panthor_am_msg_write(struct panthor_am_msg *msg, u64 message)
+{
+ lockdep_assert_held(&msg->lock);
+
+ /*
+ * Registers must be written in this exact order to prevent interrupts
+ * being raised before the complete message is written.
+ */
+ gpu_write(msg->iomem, AM_OUTGOING_MESSAGE0, lower_32_bits(message));
+ gpu_write(msg->iomem, AM_OUTGOING_MESSAGE1, upper_32_bits(message));
+}
+
+static inline int panthor_am_msg_retry(struct panthor_am_msg *msg)
+{
+ u64 message;
+
+ guard(spinlock_irqsave)(&msg->lock);
+
+ if (kfifo_is_empty(&msg->send_fifo))
+ return 0;
+
+ if (panthor_am_msg_pending(msg))
+ return -EBUSY;
+
+ /* FIFO should never be empty at this point */
+ if (!kfifo_get(&msg->send_fifo, &message))
+ return -EINVAL;
+
+ panthor_am_msg_write(msg, message);
+
+ /* There are still messages in the FIFO, notify caller to retry again */
+ if (!kfifo_is_empty(&msg->send_fifo))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static inline int panthor_am_msg_send(struct panthor_am_msg *msg, u64 message)
+{
+ guard(spinlock_irqsave)(&msg->lock);
+
+ /*
+ * If there already is a pending message in the FIFO or the outgoing
+ * message is still not read by the receipient, add to the FIFO.
+ */
+ if (!kfifo_is_empty(&msg->send_fifo) || panthor_am_msg_pending(msg)) {
+ if (!kfifo_put(&msg->send_fifo, message))
+ return -ENOMEM;
+
+ /*
+ * return -EBUSY to indicate to the caller to schedule work to
+ * retry sending messages in the FIFO.
+ */
+ return -EBUSY;
+ }
+
+ /* We are free to write to AM_OUTGOING_MESSAGE */
+ panthor_am_msg_write(msg, message);
+
+ return 0;
+}
+
+static inline int panthor_am_msg_version_validate(struct panthor_am_msg *msg,
+ u8 version)
+{
+ if (version < AM_MSG_MIN_SUPPORTED_VERSION) {
+ msg->version = AM_MSG_MIN_SUPPORTED_VERSION;
+ return -EOPNOTSUPP;
+ }
+
+ msg->version = min(version, AM_MSG_CURRENT_VERSION);
+
+ return 0;
+}
+
+#endif
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 12/18] drm/panthor: Add AM_RESOURCE_GROUP support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (10 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 11/18] drm/panthor: Add AM message helpers Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 13/18] drm/panthor: Add arbitration scheduler Karunika Choo
` (6 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Add resource-group probing for the arbitration device, including MMIO
resources, IRQ handling, per-AW AM message helpers, handshake
negotiation, and retry work for deferred outgoing messages.
Hook resource groups into arbitration init, teardown, and runtime PM.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/arbitration/Makefile | 3 +-
.../panthor/arbitration/panthor_arbitration.h | 5 +
.../arbitration/panthor_arbitration_drv.c | 17 ++
.../arbitration/panthor_resource_group.c | 288 ++++++++++++++++++
.../arbitration/panthor_resource_group.h | 20 ++
5 files changed, 332 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_resource_group.h
diff --git a/drivers/gpu/drm/panthor/arbitration/Makefile b/drivers/gpu/drm/panthor/arbitration/Makefile
index 5d5b2b8d84bc..0ea5b70d524a 100644
--- a/drivers/gpu/drm/panthor/arbitration/Makefile
+++ b/drivers/gpu/drm/panthor/arbitration/Makefile
@@ -4,7 +4,8 @@
obj-$(CONFIG_DRM_PANTHOR_ARBITRATION) += panthor_arbitration.o
panthor_arbitration-y := \
panthor_arbitration_drv.o \
- panthor_partition_control.o
+ panthor_partition_control.o \
+ panthor_resource_group.o
INCLUDES = \
-I$(src)/..
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 5e35ae8463c4..38c8f8674a02 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -6,8 +6,10 @@
struct device;
struct panthor_partition_control;
+struct panthor_resource_group;
#define AM_ARB_MAX_PC_COUNT 1
+#define AM_ARB_MAX_RG_COUNT 1
#define AM_ARB_MAX_AW_COUNT 16
/**
@@ -19,6 +21,9 @@ struct panthor_arbitration {
/** @pc: Pointer array to partition control data */
struct panthor_partition_control *pc[AM_ARB_MAX_PC_COUNT];
+
+ /** @rg: Pointer array to resource group data */
+ struct panthor_resource_group *rg[AM_ARB_MAX_RG_COUNT];
};
#endif
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index 07a566168e6a..ac395b89b483 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -10,6 +10,7 @@
#include "panthor_arbitration.h"
#include "panthor_partition_control.h"
+#include "panthor_resource_group.h"
#define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
@@ -22,6 +23,10 @@ static int panthor_arbitration_runtime_suspend(struct device *dev)
if (ret)
return ret;
+ ret = panthor_resource_group_suspend(adev);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -30,6 +35,10 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
struct panthor_arbitration *adev = dev_get_drvdata(dev);
int ret = 0;
+ ret = panthor_resource_group_resume(adev);
+ if (ret)
+ return ret;
+
ret = panthor_partition_control_resume(adev);
if (ret)
return ret;
@@ -66,6 +75,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
if (ret)
goto err_out;
+ ret = panthor_resource_group_init(adev);
+ if (ret)
+ goto err_term_partition;
+
pm_runtime_set_autosuspend_delay(dev, PANTHOR_PM_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
@@ -73,6 +86,9 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
return 0;
+err_term_partition:
+ panthor_partition_control_term(adev);
+
err_out:
pm_runtime_put_noidle(dev);
return ret;
@@ -90,6 +106,7 @@ static void panthor_arbitration_remove(struct platform_device *pdev)
if (ret < 0)
goto out_suspended;
+ panthor_resource_group_term(adev);
panthor_partition_control_term(adev);
pm_runtime_put_noidle(adev->dev);
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
new file mode 100644
index 000000000000..cd1ad0532766
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "panthor_am_msg.h"
+#include "panthor_arbitration.h"
+#include "panthor_device_io.h"
+#include "panthor_resource_group.h"
+
+#define AM_RG_IRQ_RAWSTAT 0x80
+#define AM_RG_IRQ_CLEAR 0x84
+#define AM_RG_IRQ_MASK 0x88
+#define AM_RG_IRQ_STATUS 0x8C
+#define AM_RG_MESSAGE_MASK GENMASK(15, 0)
+
+#define AM_RG_AW_MESSAGE(n) (0x100 + (n) * 0x20)
+
+struct rg_aw_data {
+ /** @msg: Messaging data */
+ struct panthor_am_msg msg;
+};
+
+struct panthor_resource_group {
+ /** @dev: Device pointer */
+ struct device *dev;
+
+ /** @name: Resource name */
+ char *name;
+
+ /** @iomem: CPU mapping of the partition control IOMEM region */
+ void __iomem *iomem;
+
+ /** @irq: IRQ number */
+ int irq;
+
+ /** @aw_data: Data for each access window */
+ struct rg_aw_data aw_data[AM_ARB_MAX_AW_COUNT];
+
+ /** @wq: Resource group workqueue */
+ struct workqueue_struct *wq;
+
+ /** @msg_retry_work: Work to retry sending pending messages */
+ struct work_struct msg_retry_work;
+};
+
+static void rg_retry_messages(struct work_struct *work)
+{
+ struct panthor_resource_group *rg = container_of(
+ work, struct panthor_resource_group, msg_retry_work);
+ bool retry = false;
+
+ for (u8 aw_id = 0; aw_id < AM_ARB_MAX_AW_COUNT; aw_id++) {
+ struct panthor_am_msg *msg = &rg->aw_data[aw_id].msg;
+ int ret;
+
+ ret = panthor_am_msg_retry(msg);
+ if (ret == -EBUSY || ret == -EAGAIN)
+ retry = true;
+
+ if (ret == -EINVAL)
+ dev_warn(rg->dev, "AW%u send FIFO unexpectedy empty", aw_id);
+ }
+
+ if (retry)
+ queue_work(rg->wq, &rg->msg_retry_work);
+}
+
+static void rg_send_msg(struct panthor_resource_group *rg, u8 aw_id,
+ u64 message)
+{
+ int ret;
+
+ ret = panthor_am_msg_send(&rg->aw_data[aw_id].msg, message);
+ if (ret == -ENOMEM)
+ dev_err(rg->dev, "AW%u send FIFO is full", aw_id);
+
+ if (ret == -EBUSY) {
+ dev_dbg(rg->dev, "AW%u has pending messages, scheduling retry work", aw_id);
+ queue_work(rg->wq, &rg->msg_retry_work);
+ }
+}
+
+static void rg_respond_to_handshake(struct panthor_resource_group *rg,
+ u8 aw_id, u64 message)
+{
+ struct rg_aw_data *aw_data = &rg->aw_data[aw_id];
+ bool acked = AM_MSG_ACK_GET(message);
+ u8 version = AM_MSG_VERSION_GET(message);
+ int ret;
+
+ ret = panthor_am_msg_version_validate(&aw_data->msg, version);
+ if (ret == -EOPNOTSUPP)
+ dev_warn(rg->dev,
+ "AW%u protocol version less than minimum supported (%u < %u)",
+ aw_id, version, AM_MSG_MIN_SUPPORTED_VERSION);
+
+ if (!acked) {
+ /* AW initiated the handshake, reply with supported version */
+ u64 reply = ARB_VM_INIT_MAKE(1, aw_data->msg.version);
+
+ rg_send_msg(rg, aw_id, reply);
+ }
+}
+
+static void rg_handshake_init(struct panthor_resource_group *rg)
+{
+ for (u8 aw_id = 0; aw_id < AM_ARB_MAX_AW_COUNT; aw_id++) {
+ u64 message = ARB_VM_INIT_MAKE(0, AM_MSG_CURRENT_VERSION);
+
+ rg_send_msg(rg, aw_id, message);
+ }
+}
+
+static void rg_handle_message(struct panthor_resource_group *rg, u8 aw_id,
+ u64 message)
+{
+ u8 msg_id = AM_MSG_ID_GET(message);
+ if (!msg_id)
+ return;
+
+ switch (msg_id) {
+ case VM_ARB_INIT:
+ rg_respond_to_handshake(rg, aw_id, message);
+ break;
+ case VM_ARB_GPU_REQUEST:
+ /* TODO: on_request */
+ break;
+ case VM_ARB_GPU_STOPPED:
+ /* TODO: on_idle */
+ break;
+ default:
+ dev_warn(rg->dev, "Invalid message (0x%llx)", message);
+ break;
+ }
+}
+
+static irqreturn_t rg_irq_raw_handler(int irq, void *data)
+{
+ struct panthor_resource_group *rg = data;
+ unsigned long message_mask;
+ u32 status;
+ u8 aw_id;
+
+ status = gpu_read(rg->iomem, AM_RG_IRQ_STATUS);
+ if (!status)
+ return IRQ_NONE;
+
+ message_mask = status;
+ for_each_set_bit(aw_id, &message_mask, AM_ARB_MAX_AW_COUNT) {
+ u64 message = panthor_am_msg_read(&rg->aw_data[aw_id].msg);
+
+ rg_handle_message(rg, aw_id, message);
+ }
+
+ gpu_write(rg->iomem, AM_RG_IRQ_CLEAR, status);
+
+ return IRQ_HANDLED;
+}
+
+static void rg_irq_suspend(struct panthor_resource_group *rg)
+{
+ gpu_write(rg->iomem, AM_RG_IRQ_MASK, 0);
+}
+
+static void rg_irq_resume(struct panthor_resource_group *rg)
+{
+ gpu_write(rg->iomem, AM_RG_IRQ_MASK, AM_RG_MESSAGE_MASK);
+}
+
+int panthor_resource_group_suspend(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_RG_COUNT; i++) {
+ struct panthor_resource_group *rg = adev->rg[i];
+
+ if (!rg)
+ continue;
+
+ rg_irq_suspend(rg);
+ }
+
+ return 0;
+}
+
+int panthor_resource_group_resume(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_RG_COUNT; i++) {
+ struct panthor_resource_group *rg = adev->rg[i];
+
+ if (!rg)
+ continue;
+
+ rg_irq_resume(rg);
+ }
+
+ return 0;
+}
+
+void panthor_resource_group_term(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_RG_COUNT; i++) {
+ struct panthor_resource_group *rg = adev->rg[i];
+
+ if (!rg)
+ continue;
+
+ disable_work_sync(&rg->msg_retry_work);
+ rg_irq_suspend(rg);
+ }
+}
+
+static int resource_group_init(struct panthor_arbitration *adev, int i)
+{
+ struct device *dev = adev->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct panthor_resource_group *rg;
+ struct resource *res;
+ void __iomem *iomem;
+ char *name;
+ int ret = 0;
+ int irq;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "rg%d", i);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res) {
+ devm_kfree(dev, name);
+ return -ENODEV;
+ }
+
+ iomem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(iomem)) {
+ dev_err(dev, "%s: Failed to ioremap", name);
+ return PTR_ERR(iomem);
+ }
+
+ rg = devm_kzalloc(dev, sizeof(*rg), GFP_KERNEL);
+ if (!rg)
+ return -ENOMEM;
+
+ rg->dev = dev;
+ rg->iomem = iomem;
+
+ rg->wq = devm_alloc_ordered_workqueue(dev, "panthor-%s-wq", 0, name);
+ if (!rg->wq)
+ return -ENOMEM;
+
+ INIT_WORK(&rg->msg_retry_work, rg_retry_messages);
+
+ for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++)
+ panthor_am_msg_init(&rg->aw_data[i].msg,
+ rg->iomem + AM_RG_AW_MESSAGE(i));
+
+ adev->rg[i] = rg;
+
+ irq = platform_get_irq_byname(pdev, name);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(
+ dev, irq, rg_irq_raw_handler, IRQF_SHARED,
+ devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), rg);
+ if (ret)
+ return ret;
+
+ rg_irq_resume(rg);
+
+ rg_handshake_init(rg);
+
+ return 0;
+}
+
+int panthor_resource_group_init(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_RG_COUNT; i++) {
+ int ret = resource_group_init(adev, i);
+ if (ret == -ENODEV)
+ continue;
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.h b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.h
new file mode 100644
index 000000000000..b8633abdd8eb
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_RESOURCE_GROUP_H__
+#define __PANTHOR_RESOURCE_GROUP_H__
+
+#include <linux/types.h>
+
+struct device;
+struct panthor_arbitration;
+
+int panthor_resource_group_init(struct panthor_arbitration *adev);
+
+void panthor_resource_group_term(struct panthor_arbitration *adev);
+
+int panthor_resource_group_suspend(struct panthor_arbitration *adev);
+
+int panthor_resource_group_resume(struct panthor_arbitration *adev);
+
+#endif
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 13/18] drm/panthor: Add arbitration scheduler
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (11 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 12/18] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 14/18] drm/panthor: Route arbitration events Karunika Choo
` (5 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Add a per-partition arbitration scheduler that queues access-window GPU
requests, grants the active window, asks it to stop when its lease
expires, and force-closes it if it does not yield in time.
Use ordered workqueues and hrtimers for grant/stop sequencing, with
module parameters controlling grant and yield timeouts.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/arbitration/Makefile | 1 +
.../panthor/arbitration/panthor_arbitration.h | 4 +
.../arbitration/panthor_arbitration_drv.c | 17 +
.../arbitration/panthor_arbitration_sched.c | 692 ++++++++++++++++++
.../arbitration/panthor_arbitration_sched.h | 25 +
5 files changed, 739 insertions(+)
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
diff --git a/drivers/gpu/drm/panthor/arbitration/Makefile b/drivers/gpu/drm/panthor/arbitration/Makefile
index 0ea5b70d524a..83be25341ba0 100644
--- a/drivers/gpu/drm/panthor/arbitration/Makefile
+++ b/drivers/gpu/drm/panthor/arbitration/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_DRM_PANTHOR_ARBITRATION) += panthor_arbitration.o
panthor_arbitration-y := \
panthor_arbitration_drv.o \
+ panthor_arbitration_sched.o \
panthor_partition_control.o \
panthor_resource_group.o
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 38c8f8674a02..0bc683e6a71d 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -5,6 +5,7 @@
#define __PANTHOR_ARBITRATION_H__
struct device;
+struct panthor_arbitration_sched;
struct panthor_partition_control;
struct panthor_resource_group;
@@ -24,6 +25,9 @@ struct panthor_arbitration {
/** @rg: Pointer array to resource group data */
struct panthor_resource_group *rg[AM_ARB_MAX_RG_COUNT];
+
+ /** @shed: Pointer to scheduler data. 1 sched per partition */
+ struct panthor_arbitration_sched *sched[AM_ARB_MAX_PC_COUNT];
};
#endif
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index ac395b89b483..5cdefd9ed5c9 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include "panthor_arbitration.h"
+#include "panthor_arbitration_sched.h"
#include "panthor_partition_control.h"
#include "panthor_resource_group.h"
@@ -19,6 +20,10 @@ static int panthor_arbitration_runtime_suspend(struct device *dev)
struct panthor_arbitration *adev = dev_get_drvdata(dev);
int ret = 0;
+ ret = panthor_arbitration_sched_suspend(adev);
+ if (ret)
+ return ret;
+
ret = panthor_partition_control_suspend(adev);
if (ret)
return ret;
@@ -43,6 +48,10 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
if (ret)
return ret;
+ ret = panthor_arbitration_sched_resume(adev);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -79,6 +88,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
if (ret)
goto err_term_partition;
+ ret = panthor_arbitration_sched_init(adev);
+ if (ret)
+ goto err_term_resgroup;
+
pm_runtime_set_autosuspend_delay(dev, PANTHOR_PM_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
@@ -86,6 +99,9 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
return 0;
+err_term_resgroup:
+ panthor_resource_group_term(adev);
+
err_term_partition:
panthor_partition_control_term(adev);
@@ -106,6 +122,7 @@ static void panthor_arbitration_remove(struct platform_device *pdev)
if (ret < 0)
goto out_suspended;
+ panthor_arbitration_sched_term(adev);
panthor_resource_group_term(adev);
panthor_partition_control_term(adev);
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
new file mode 100644
index 000000000000..a6c5efee7b8d
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/device.h>
+#include <linux/kfifo.h>
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include "panthor_arbitration.h"
+#include "panthor_arbitration_sched.h"
+#include "panthor_resource_group.h"
+
+static u64 grant_timeout_us = 1 * USEC_PER_SEC;
+static u64 yield_timeout_us = 1 * USEC_PER_SEC;
+
+module_param(grant_timeout_us, ullong, 0664);
+MODULE_PARM_DESC(grant_timeout_us, "Time the GPU will be granted to the AW in microseconds.");
+
+module_param(yield_timeout_us, ullong, 0664);
+MODULE_PARM_DESC(yield_timeout_us, "Time, in microseconds, within which the AW must yeild the GPU.");
+
+enum arbitration_sched_phase {
+ ARB_SCHED_PHASE_IDLE = 0,
+ ARB_SCHED_PHASE_GRANTING,
+ ARB_SCHED_PHASE_GRANTED,
+ ARB_SCHED_PHASE_STOPPING,
+ ARB_SCHED_PHASE_LOST,
+};
+
+struct panthor_arbitration_timer {
+ /** @timer: High-resolution timer */
+ struct hrtimer timer;
+
+ /** @epoch: The lease generation for which the timer was triggered */
+ u32 epoch;
+};
+
+struct panthor_arbitration_work {
+ /** @work: Work struct */
+ struct work_struct work;
+
+ /** @aw_id: Window for which the work was queued */
+ u8 aw_id;
+
+ /** @epoch: The lease generation for which the work was queued */
+ u32 epoch;
+};
+
+struct panthor_arbitration_sched {
+ /** @dev: Device pointer */
+ struct device *dev;
+
+ /** @name: Scheduler indentifying name */
+ char *name;
+
+ /** @waitqueue: Event wait queue. Not IRQ safe */
+ wait_queue_head_t waitqueue;
+
+ /** @lock: Protects scheduler state */
+ spinlock_t lock;
+
+ /** @queue: Pending request FIFO */
+ DECLARE_KFIFO(queue, u8, 16);
+
+ /** @queued_mask: Mask of currently queued AWs */
+ DECLARE_BITMAP(queued_mask, AM_ARB_MAX_AW_COUNT);
+
+ /** @active_aw_id: Currently active AW. Can be -1 */
+ s8 active_aw_id;
+
+ /** @phase: Current scheduling phase */
+ enum arbitration_sched_phase phase;
+
+ /** @epoch: Current active lease generation */
+ u32 epoch;
+
+ /** @wq: Scheduler workqueue to dispatch events */
+ struct workqueue_struct *wq;
+
+ /** @grant_work: Work to grant the GPU to the AW */
+ struct panthor_arbitration_work grant_work;
+
+ /** @stop_work: Work to hard-close the AW */
+ struct panthor_arbitration_work stop_work;
+
+ /** @close_work: Work to hard-close the AW */
+ struct panthor_arbitration_work close_work;
+
+ /** @grant_timer: Timer to indicate grant window has lapsed */
+ struct panthor_arbitration_timer grant_timer;
+
+ /** @stop_timer: Timer to indicate yield window has lapsed */
+ struct panthor_arbitration_timer stop_timer;
+
+ /** @disabled: Scheduler disabled. No further scheduling can happen */
+ bool disabled;
+
+ /**
+ * @grant_previously_expired: Grant period previously expired but was
+ * not asked to stop
+ */
+ bool grant_previously_expired;
+};
+
+static inline struct panthor_arbitration *
+to_adev(struct panthor_arbitration_sched *sched)
+{
+ return dev_get_drvdata(sched->dev);
+}
+
+static inline struct panthor_arbitration_work *to_work(struct work_struct *w)
+{
+ return container_of(w, struct panthor_arbitration_work, work);
+}
+
+static bool arb_sched_phase_reached(struct panthor_arbitration_sched *sched,
+ enum arbitration_sched_phase phase)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ return sched->phase == phase;
+}
+
+/**
+ * sched_wait_phase - Wait for scheduler to enter a phase
+ *
+ * This method is not IRQ safe as it sleeps.
+ */
+static int arb_sched_wait_phase(struct panthor_arbitration_sched *sched,
+ enum arbitration_sched_phase phase,
+ u64 timeout_us)
+{
+ if (!wait_event_timeout(
+ sched->waitqueue, arb_sched_phase_reached(sched, phase),
+ usecs_to_jiffies(timeout_us))) {
+
+ if (!arb_sched_phase_reached(sched, phase))
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void arb_sched_disable(struct panthor_arbitration_sched *sched)
+{
+ scoped_guard(spinlock_irqsave, &sched->lock)
+ sched->disabled = true;
+
+ hrtimer_cancel(&sched->grant_timer.timer);
+}
+
+static void arb_sched_enable(struct panthor_arbitration_sched *sched)
+{
+ scoped_guard(spinlock_irqsave, &sched->lock)
+ sched->disabled = false;
+}
+
+static void arb_sched_queue_work(struct panthor_arbitration_sched *sched,
+ struct panthor_arbitration_work *work)
+{
+ lockdep_assert_held(&sched->lock);
+
+ work->aw_id = sched->active_aw_id;
+ work->epoch = sched->epoch;
+ queue_work(sched->wq, &work->work);
+}
+
+static void arb_sched_request_stop_locked(struct panthor_arbitration_sched *sched)
+{
+ lockdep_assert_held(&sched->lock);
+
+ if (sched->phase != ARB_SCHED_PHASE_GRANTED)
+ return;
+
+ if (sched->active_aw_id < 0)
+ return;
+
+ /*
+ * In the event of a single requesting AW, let it continue to
+ * lease the GPU until another request comes in.
+ */
+ if (!sched->disabled && kfifo_is_empty(&sched->queue)) {
+ sched->grant_previously_expired = true;
+ return;
+ }
+
+ sched->phase = ARB_SCHED_PHASE_STOPPING;
+ arb_sched_queue_work(sched, &sched->stop_work);
+
+ wake_up_all(&sched->waitqueue);
+}
+
+static void arb_sched_request_stop(struct panthor_arbitration_sched *sched)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ arb_sched_request_stop_locked(sched);
+}
+
+static void arb_sched_force_close_locked(struct panthor_arbitration_sched *sched)
+{
+ lockdep_assert_held(&sched->lock);
+
+ if (sched->phase == ARB_SCHED_PHASE_IDLE ||
+ sched->phase == ARB_SCHED_PHASE_LOST)
+ return;
+
+ if (sched->active_aw_id < 0)
+ return;
+
+ sched->phase = ARB_SCHED_PHASE_LOST;
+
+ arb_sched_queue_work(sched, &sched->close_work);
+
+ wake_up_all(&sched->waitqueue);
+}
+
+static void arb_sched_force_close(struct panthor_arbitration_sched *sched)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ arb_sched_force_close_locked(sched);
+}
+
+static int arb_sched_next_locked(struct panthor_arbitration_sched *sched)
+{
+ u8 aw_id;
+
+ lockdep_assert_held(&sched->lock);
+
+ if (sched->disabled)
+ return 0;
+
+ /* already have a running aw */
+ if (sched->active_aw_id >= 0)
+ return 0;
+
+ /* nothing to schedule */
+ if (kfifo_is_empty(&sched->queue))
+ return 0;
+
+ if (!kfifo_get(&sched->queue, &aw_id)) {
+ dev_warn(sched->dev, "%s: queue unexpectedly empty",
+ sched->name);
+ return -EINVAL;
+ }
+
+ clear_bit(aw_id, sched->queued_mask);
+
+ sched->active_aw_id = aw_id;
+ sched->epoch++;
+ sched->phase = ARB_SCHED_PHASE_GRANTING;
+
+ arb_sched_queue_work(sched, &sched->grant_work);
+
+ wake_up_all(&sched->waitqueue);
+
+ return 0;
+}
+
+static int arb_sched_next(struct panthor_arbitration_sched *sched)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ return arb_sched_next_locked(sched);
+}
+
+static void arb_sched_reset(struct panthor_arbitration_sched *sched,
+ u32 epoch, u8 aw_id)
+{
+ scoped_guard(spinlock_irqsave, &sched->lock) {
+ if (sched->epoch != epoch)
+ return;
+
+ if (sched->active_aw_id != aw_id)
+ return;
+
+ dev_warn(sched->dev, "%s: scheduler reset. active_aw=%u",
+ sched->name, sched->active_aw_id);
+
+ sched->epoch++;
+ sched->active_aw_id = -1;
+ sched->phase = ARB_SCHED_PHASE_IDLE;
+ sched->grant_previously_expired = false;
+
+ wake_up_all(&sched->waitqueue);
+ }
+
+ hrtimer_cancel(&sched->grant_timer.timer);
+ hrtimer_cancel(&sched->stop_timer.timer);
+
+ arb_sched_next(sched);
+}
+
+static void arb_sched_grant_work(struct work_struct *work)
+{
+ struct panthor_arbitration_work *w = to_work(work);
+ struct panthor_arbitration_sched *sched =
+ container_of(w, struct panthor_arbitration_sched, grant_work);
+ int ret;
+ u32 epoch;
+ u8 aw_id;
+
+ scoped_guard(spinlock_irqsave, &sched->lock) {
+ if (sched->epoch != w->epoch)
+ return;
+
+ if (sched->active_aw_id != w->aw_id)
+ return;
+
+ epoch = sched->epoch;
+ aw_id = sched->active_aw_id;
+ }
+
+ ret = panthor_arbitration_on_grant(to_adev(sched), aw_id);
+ if (!ret) {
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (sched->phase != ARB_SCHED_PHASE_GRANTING) {
+ dev_warn(sched->dev, "%s: Grant ACKED when not granting",
+ sched->name);
+ return;
+ }
+
+ if (sched->active_aw_id != aw_id)
+ return;
+
+ if (sched->disabled) {
+ sched->phase = ARB_SCHED_PHASE_STOPPING;
+ arb_sched_queue_work(sched, &sched->stop_work);
+ } else {
+ sched->phase = ARB_SCHED_PHASE_GRANTED;
+
+ sched->grant_timer.epoch = sched->epoch;
+ hrtimer_start(&sched->grant_timer.timer,
+ us_to_ktime(grant_timeout_us),
+ HRTIMER_MODE_REL);
+ }
+
+ wake_up_all(&sched->waitqueue);
+ } else {
+ dev_err(sched->dev, "%s: Failed to grant access to AW%u",
+ sched->name, aw_id);
+
+ arb_sched_reset(sched, epoch, aw_id);
+ }
+}
+
+static void arb_sched_stop_work(struct work_struct *work)
+{
+ struct panthor_arbitration_work *w = to_work(work);
+ struct panthor_arbitration_sched *sched =
+ container_of(w, struct panthor_arbitration_sched, stop_work);
+ int ret;
+ u32 epoch;
+ u8 aw_id;
+
+ scoped_guard(spinlock_irqsave, &sched->lock) {
+ if (sched->epoch != w->epoch)
+ return;
+
+ if (sched->active_aw_id != w->aw_id)
+ return;
+
+ epoch = sched->epoch;
+ aw_id = sched->active_aw_id;
+ }
+
+ ret = panthor_arbitration_on_stop(to_adev(sched), aw_id);
+ if (!ret) {
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (sched->phase != ARB_SCHED_PHASE_STOPPING) {
+ dev_warn(sched->dev, "%s: Stop ACKED when not yielding",
+ sched->name);
+ return;
+ }
+
+ if (sched->active_aw_id != aw_id)
+ return;
+
+ sched->stop_timer.epoch = sched->epoch;
+ hrtimer_start(&sched->stop_timer.timer,
+ us_to_ktime(yield_timeout_us), HRTIMER_MODE_REL);
+ } else {
+ dev_err(sched->dev, "%s: Failed to send yield to AW%u",
+ sched->name, aw_id);
+
+ arb_sched_reset(sched, epoch, aw_id);
+ }
+}
+
+static void arb_sched_close_work(struct work_struct *work)
+{
+ struct panthor_arbitration_work *w = to_work(work);
+ struct panthor_arbitration_sched *sched =
+ container_of(w, struct panthor_arbitration_sched, close_work);
+ int ret;
+ u32 epoch;
+ u8 aw_id;
+
+ scoped_guard(spinlock_irqsave, &sched->lock) {
+ if (sched->epoch != w->epoch)
+ return;
+
+ if (sched->active_aw_id != w->aw_id)
+ return;
+
+ epoch = sched->epoch;
+ aw_id = sched->active_aw_id;
+ }
+
+ ret = panthor_arbitration_on_close(to_adev(sched), w->aw_id);
+ if (ret) {
+ dev_err(sched->dev, "%s: Failed to close AW%u",
+ sched->name, aw_id);
+ arb_sched_reset(sched, epoch, aw_id);
+ } else {
+ panthor_arbitration_sched_on_stopped(sched, w->aw_id);
+ }
+}
+
+static enum hrtimer_restart
+panthor_arbitration_sched_on_stop_timeout(struct hrtimer *timer)
+{
+ struct panthor_arbitration_timer *atmr = container_of(
+ timer, struct panthor_arbitration_timer, timer);
+ struct panthor_arbitration_sched *sched = container_of(
+ atmr, struct panthor_arbitration_sched, stop_timer);
+
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (sched->epoch != atmr->epoch)
+ return HRTIMER_NORESTART;
+
+ dev_warn(sched->dev,
+ "%s: AW%d failed to stop in time. Force closing...",
+ sched->name, sched->active_aw_id);
+ arb_sched_force_close_locked(sched);
+
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart
+panthor_arbitration_sched_on_grant_timeout(struct hrtimer *timer)
+{
+ struct panthor_arbitration_timer *atmr = container_of(
+ timer, struct panthor_arbitration_timer, timer);
+ struct panthor_arbitration_sched *sched = container_of(
+ atmr, struct panthor_arbitration_sched, grant_timer);
+
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (sched->epoch != atmr->epoch)
+ return HRTIMER_NORESTART;
+
+ arb_sched_request_stop_locked(sched);
+
+ return HRTIMER_NORESTART;
+}
+
+int panthor_arbitration_sched_on_request(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (aw_id >= AM_ARB_MAX_AW_COUNT)
+ return -EINVAL;
+
+ if (test_bit(aw_id, sched->queued_mask) ||
+ (sched->active_aw_id == aw_id &&
+ sched->phase != ARB_SCHED_PHASE_STOPPING)) {
+ dev_warn(sched->dev, "%s: AW%u already in request queue.",
+ sched->name, aw_id);
+ return -EEXIST;
+ }
+
+ /* Always put onto queue */
+ kfifo_put(&sched->queue, aw_id);
+ set_bit(aw_id, sched->queued_mask);
+
+ /* Already have running or pending, exit. */
+ if (sched->active_aw_id >= 0) {
+ /*
+ * Grant timer for the current AW has previously expired,
+ * request it to yield the GPU.
+ */
+ if (sched->grant_previously_expired)
+ arb_sched_request_stop_locked(sched);
+
+ return 0;
+ }
+
+ return arb_sched_next_locked(sched);
+}
+
+int panthor_arbitration_sched_on_idle(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (aw_id >= AM_ARB_MAX_AW_COUNT)
+ return -EINVAL;
+
+ if (sched->active_aw_id != aw_id)
+ return 0;
+
+ if (sched->phase != ARB_SCHED_PHASE_GRANTED)
+ return 0;
+
+ sched->phase = ARB_SCHED_PHASE_STOPPING;
+ arb_sched_queue_work(sched, &sched->close_work);
+
+ return 0;
+}
+
+int panthor_arbitration_sched_on_stopped(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+ guard(spinlock_irqsave)(&sched->lock);
+
+ if (aw_id >= AM_ARB_MAX_AW_COUNT)
+ return -EINVAL;
+
+ if (sched->phase == ARB_SCHED_PHASE_IDLE)
+ return 0;
+
+ if (sched->active_aw_id != aw_id)
+ return 0;
+
+ /* retire old lease */
+ sched->epoch++;
+
+ sched->grant_previously_expired = false;
+ sched->active_aw_id = -1;
+ sched->phase = ARB_SCHED_PHASE_IDLE;
+
+ wake_up_all(&sched->waitqueue);
+
+ return arb_sched_next_locked(sched);
+}
+
+static void arbitration_sched_term(struct panthor_arbitration_sched *sched)
+{
+ WARN_ON(panthor_arbitration_sched_stop(sched));
+
+ disable_work_sync(&sched->grant_work.work);
+ disable_work_sync(&sched->stop_work.work);
+ disable_work_sync(&sched->close_work.work);
+}
+
+static int arbitration_sched_init(struct panthor_arbitration *adev, int i)
+{
+ struct device *dev = adev->dev;
+ struct panthor_arbitration_sched *sched;
+
+ sched = devm_kzalloc(dev, sizeof(*sched), GFP_KERNEL);
+ if (!sched)
+ return -ENOMEM;
+
+ sched->name = devm_kasprintf(dev, GFP_KERNEL, "sched%d", i);
+ sched->dev = dev;
+
+ sched->wq = devm_alloc_ordered_workqueue(dev, "%s-wq", 0, sched->name);
+ if (!sched->wq)
+ return -ENOMEM;
+
+ INIT_WORK(&sched->grant_work.work, arb_sched_grant_work);
+ INIT_WORK(&sched->stop_work.work, arb_sched_stop_work);
+ INIT_WORK(&sched->close_work.work, arb_sched_close_work);
+
+ init_waitqueue_head(&sched->waitqueue);
+
+ spin_lock_init(&sched->lock);
+ INIT_KFIFO(sched->queue);
+ sched->active_aw_id = -1;
+ sched->phase = ARB_SCHED_PHASE_IDLE;
+
+ hrtimer_setup(&sched->grant_timer.timer,
+ panthor_arbitration_sched_on_grant_timeout,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_setup(&sched->stop_timer.timer,
+ panthor_arbitration_sched_on_stop_timeout,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ adev->sched[i] = sched;
+
+ return 0;
+}
+
+void panthor_arbitration_sched_term(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_arbitration_sched *sched = adev->sched[i];
+
+ if (!sched)
+ continue;
+
+ arbitration_sched_term(sched);
+ }
+}
+
+int panthor_arbitration_sched_init(struct panthor_arbitration *adev)
+{
+
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ int ret = arbitration_sched_init(adev, i);
+
+ if (ret == -ENODEV)
+ continue;
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int panthor_arbitration_sched_suspend(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_arbitration_sched *sched = adev->sched[i];
+ int ret;
+
+ if (!sched)
+ continue;
+
+ ret = panthor_arbitration_sched_stop(sched);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int panthor_arbitration_sched_resume(struct panthor_arbitration *adev)
+{
+ for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+ struct panthor_arbitration_sched *sched = adev->sched[i];
+ int ret;
+
+ if (!sched)
+ continue;
+
+ ret = panthor_arbitration_sched_start(sched);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * panthor_arbitration_sched_stop - Stop active aw and pause scheduler
+ *
+ * This method is not IRQ safe as it waits for on_stopped() to be called.
+ */
+int panthor_arbitration_sched_stop(struct panthor_arbitration_sched *sched)
+{
+ int ret;
+
+ arb_sched_disable(sched);
+
+ arb_sched_request_stop(sched);
+
+ /*
+ * Provisionally waiting for up to 2x yield_timeout, but path may
+ * include time taken for panthor_arbitration_on_grant() to complete
+ * if sched_request_stop() is called while in the GRANTING state.
+ *
+ * Realistically, if granting takes more than yield_timeout, HW is in
+ * bad state either way. Best continue and stop the scheduler.
+ */
+ ret = arb_sched_wait_phase(sched, ARB_SCHED_PHASE_IDLE,
+ grant_timeout_us + yield_timeout_us);
+ if (ret)
+ arb_sched_force_close(sched);
+
+ /* Should no longer have any further activity. Cancel timer. */
+ hrtimer_cancel(&sched->stop_timer.timer);
+ flush_workqueue(sched->wq);
+
+ scoped_guard(spinlock_irqsave, &sched->lock)
+ if (sched->phase == ARB_SCHED_PHASE_IDLE)
+ return 0;
+
+ return ret;
+}
+
+int panthor_arbitration_sched_start(struct panthor_arbitration_sched *sched)
+{
+ arb_sched_enable(sched);
+
+ return arb_sched_next(sched);
+}
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
new file mode 100644
index 000000000000..4625b36f4ce6
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_ARBITRATION_SCHED_H__
+#define __PANTHOR_ARBITRATION_SCHED_H__
+
+#include <linux/types.h>
+
+struct panthor_arbitration;
+struct panthor_arbitration_sched;
+
+int panthor_arbitration_sched_init(struct panthor_arbitration *adev);
+void panthor_arbitration_sched_term(struct panthor_arbitration *adev);
+
+int panthor_arbitration_sched_suspend(struct panthor_arbitration *adev);
+int panthor_arbitration_sched_resume(struct panthor_arbitration *adev);
+
+int panthor_arbitration_sched_stop(struct panthor_arbitration_sched *sched);
+int panthor_arbitration_sched_start(struct panthor_arbitration_sched *sched);
+
+int panthor_arbitration_sched_on_request(struct panthor_arbitration_sched *sched, u8 aw_id);
+int panthor_arbitration_sched_on_idle(struct panthor_arbitration_sched *sched, u8 aw_id);
+int panthor_arbitration_sched_on_stopped(struct panthor_arbitration_sched *sched, u8 aw_id);
+
+#endif
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 14/18] drm/panthor: Route arbitration events
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (12 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 13/18] drm/panthor: Add arbitration scheduler Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 15/18] drm/panthor: Add access-window support Karunika Choo
` (4 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Wire the resource-group, scheduler, and partition-control blocks
together. Resource-group messages now feed request and idle events into
the scheduler, while partition RESET_DONE from a YIELD_DONE reports the
active access window as stopped.
Add the arbiter-to-window callbacks used by the scheduler to grant,
yield, and close partition windows.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
.../panthor/arbitration/panthor_arbitration.h | 12 ++++++
.../arbitration/panthor_arbitration_drv.c | 38 +++++++++++++++++++
.../arbitration/panthor_partition_control.c | 9 +++++
.../arbitration/panthor_resource_group.c | 4 +-
4 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 0bc683e6a71d..fca1a2325b16 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -4,6 +4,8 @@
#ifndef __PANTHOR_ARBITRATION_H__
#define __PANTHOR_ARBITRATION_H__
+#include <linux/types.h>
+
struct device;
struct panthor_arbitration_sched;
struct panthor_partition_control;
@@ -30,4 +32,14 @@ struct panthor_arbitration {
struct panthor_arbitration_sched *sched[AM_ARB_MAX_PC_COUNT];
};
+/* AW to Arbiter events */
+int panthor_arbitration_on_request(struct panthor_arbitration *adev, u8 aw_id);
+int panthor_arbitration_on_idle(struct panthor_arbitration *adev, u8 aw_id);
+int panthor_arbitration_on_stopped(struct panthor_arbitration *adev, u8 aw_id);
+
+/* Arbiter to AW events */
+int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 aw_id);
+int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 aw_id);
+int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 aw_id);
+
#endif
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index 5cdefd9ed5c9..edbda1845068 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -55,6 +55,44 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
return 0;
}
+/* AW to Arbiter events */
+int panthor_arbitration_on_request(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_arbitration_sched_on_request(adev->sched[0], aw_id);
+}
+
+int panthor_arbitration_on_idle(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_arbitration_sched_on_idle(adev->sched[0], aw_id);
+}
+
+int panthor_arbitration_on_stopped(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_arbitration_sched_on_stopped(adev->sched[0], aw_id);
+}
+
+/* Arbiter to AW events */
+int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_partition_control_open_window(adev->pc[0], aw_id);
+}
+
+int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_partition_control_yield_now(adev->pc[0]);
+}
+
+int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 aw_id)
+{
+ /* TODO: AW to PC assignment */
+ return panthor_partition_control_close_window(adev->pc[0]);
+}
+
static int panthor_arbitration_probe(struct platform_device *pdev)
{
struct panthor_arbitration *adev;
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
index f4bad839610d..095e19c8d4c5 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -220,15 +220,24 @@ static int window_open(struct panthor_partition_control *pc, u8 aw_id)
static void partition_handle_reset_done(struct panthor_partition_control *pc)
{
+ bool notify_stopped = false;
+ int aw_id;
+
scoped_guard(spinlock_irqsave, &pc->lock) {
+ aw_id = pc->current_aw;
pc->current_aw = -1;
/* RESET_DONE from CLOSE_WINDOW */
if (pc->closing)
pc->closing = false;
+ else if (aw_id >= 0)
+ notify_stopped = true;
}
wake_up_all(&pc->waitqueue);
+
+ if (notify_stopped)
+ panthor_arbitration_on_stopped(dev_get_drvdata(pc->dev), aw_id);
}
static irqreturn_t partition_irq_raw_handler(int irq, void *data)
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
index cd1ad0532766..6264efc2e29a 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
@@ -127,10 +127,10 @@ static void rg_handle_message(struct panthor_resource_group *rg, u8 aw_id,
rg_respond_to_handshake(rg, aw_id, message);
break;
case VM_ARB_GPU_REQUEST:
- /* TODO: on_request */
+ panthor_arbitration_on_request(dev_get_drvdata(rg->dev), aw_id);
break;
case VM_ARB_GPU_STOPPED:
- /* TODO: on_idle */
+ panthor_arbitration_on_idle(dev_get_drvdata(rg->dev), aw_id);
break;
default:
dev_warn(rg->dev, "Invalid message (0x%llx)", message);
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 15/18] drm/panthor: Add access-window support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (13 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 14/18] drm/panthor: Route arbitration events Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 16/18] drm/panthor: Synchronize HW component PM transitions Karunika Choo
` (3 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Add the VM-side access-window component for v15 GPUs. The new code
handles AM message handshakes, requests GPU access from the arbiter,
waits for the window-open interrupt, and yields access on suspend.
Split panthor_hw_init into bind device and initialize gpu_info after
panthor_aw has ensured GPU access.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/Makefile | 1 +
drivers/gpu/drm/panthor/panthor_aw.c | 421 +++++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_aw.h | 42 +++
drivers/gpu/drm/panthor/panthor_device.c | 15 +-
drivers/gpu/drm/panthor/panthor_device.h | 4 +
drivers/gpu/drm/panthor/panthor_hw.c | 8 +-
drivers/gpu/drm/panthor/panthor_hw.h | 2 +
7 files changed, 486 insertions(+), 7 deletions(-)
create mode 100644 drivers/gpu/drm/panthor/panthor_aw.c
create mode 100644 drivers/gpu/drm/panthor/panthor_aw.h
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 5d4d0ae64952..aa48212653dc 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0 or MIT
panthor-y := \
+ panthor_aw.o \
panthor_devfreq.o \
panthor_device.o \
panthor_drv.o \
diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
new file mode 100644
index 000000000000..00a52175f3a3
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/platform_device.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/drm_drv.h>
+
+#include "panthor_am_msg.h"
+#include "panthor_aw.h"
+#include "panthor_device.h"
+#include "panthor_fw.h"
+#include "panthor_hw.h"
+#include "panthor_sched.h"
+
+#include "panthor_trace.h"
+
+#define WINDOW_CONTROL_BASE 0x0
+#define WINDOW_CONTROL_SIZE 0x1000
+
+#define WINDOW_STATUS 0x43c
+#define WINDOW_STATUS_IRQ_WINDOW_CONTROL BIT(0)
+#define WINDOW_STATUS_IRQ_GPU_CONTROL BIT(1)
+#define WINDOW_STATUS_IRQ_GPU_POWER BIT(2)
+#define WINDOW_STATUS_IRQ_JOB_CONTROL BIT(3)
+#define WINDOW_STATUS_IRQ_MMU_CONTROL BIT(4)
+#define WINDOW_STATUS_WINDOW_OPEN BIT(31)
+
+#define WINDOW_INT_BASE 0x440
+#define WINDOW_IRQ_MESSAGE BIT(0)
+#define WINDOW_IRQ_INVALID_ACCESS BIT(1)
+#define WINDOW_IRQ_WINDOW_OPENING BIT(2)
+#define WINDOW_IRQ_WINDOW_OPENED BIT(3)
+#define WINDOW_IRQ_WINDOW_CLOSED BIT(4)
+
+#define WINDOW_MESSAGE_BASE 0x460
+
+#define WINDOW_IRQ_MASK \
+ (WINDOW_IRQ_MESSAGE | WINDOW_IRQ_WINDOW_OPENED | WINDOW_IRQ_WINDOW_CLOSED)
+
+#define PANTHOR_AW_HANDSHAKE_TIMEOUT_MS (5 * MSEC_PER_SEC)
+#define PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS (5 * MSEC_PER_SEC)
+#define PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS (5 * MSEC_PER_SEC)
+
+#define panthor_aw_wait_cond(_aw, _cond, _timeout_ms) \
+({ \
+ int __ret = 0; \
+ if (!wait_event_timeout((_aw)->waitqueue, _cond, \
+ msecs_to_jiffies(_timeout_ms))) \
+ if (!(_cond)) \
+ __ret = -ETIMEDOUT; \
+ __ret; \
+})
+
+struct panthor_aw {
+ /** @ptdev: Pointer to Panthor device */
+ struct panthor_device *ptdev;
+
+ /** @iomem: CPU mapping of WINDOW_CONTROL iomem region */
+ void __iomem *iomem;
+
+ /** @irq: IRQ data */
+ struct panthor_irq irq;
+
+ /** @msg: Messaging data */
+ struct panthor_am_msg msg;
+
+ /** @waitqueue: Event wait queue. Not IRQ safe */
+ wait_queue_head_t waitqueue;
+
+ /** @state: Current state of the AW */
+ atomic_t state;
+
+ /** @wq: Scheduler workqueue to dispatch events */
+ struct workqueue_struct *wq;
+
+ /** @msg_retry_work: Work to resend messages */
+ struct work_struct msg_retry_work;
+};
+
+static bool panthor_aw_is_open(struct panthor_aw *aw)
+{
+ return gpu_read(aw->iomem, WINDOW_STATUS) & WINDOW_STATUS_WINDOW_OPEN;
+}
+
+static void panthor_aw_state_set(struct panthor_aw *aw, enum aw_states new)
+{
+ atomic_set(&aw->state, new);
+
+ wake_up_all(&aw->waitqueue);
+}
+
+static bool panthor_aw_state_try_set(struct panthor_aw *aw,
+ enum aw_states expected,
+ enum aw_states new)
+{
+ int old_state = atomic_cmpxchg(&aw->state, expected, new);
+ bool res = (old_state == expected);
+
+ if (res)
+ wake_up_all(&aw->waitqueue);
+
+ return res;
+}
+
+static int panthor_aw_state_wait(struct panthor_aw *aw, enum aw_states target, u32 timeout_ms)
+{
+ return panthor_aw_wait_cond(aw, atomic_read(&aw->state) == target, timeout_ms);
+}
+
+static int panthor_aw_state_wait_transition(struct panthor_aw *aw, u32 timeout_ms)
+{
+ return panthor_aw_wait_cond(
+ aw,
+ (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY ||
+ atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_GRANTED),
+ timeout_ms);
+}
+
+static void panthor_aw_msg_retry_work(struct work_struct *work)
+{
+ struct panthor_aw *aw =
+ container_of(work, struct panthor_aw, msg_retry_work);
+ struct panthor_am_msg *msg = &aw->msg;
+ int ret;
+
+ ret = panthor_am_msg_retry(msg);
+ if (ret == -EINVAL)
+ drm_warn(&aw->ptdev->base, "Send FIFO unexpectedly empty");
+
+ if (ret == -EBUSY || ret == -EAGAIN)
+ queue_work(aw->wq, &aw->msg_retry_work);
+}
+
+static void panthor_aw_send_msg(struct panthor_aw *aw, u64 message)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ int ret;
+
+ ret = panthor_am_msg_send(&aw->msg, message);
+ if (ret == -ENOMEM)
+ drm_err(&ptdev->base, "Send FIFO is full");
+
+ if (ret == -EBUSY) {
+ drm_dbg(&ptdev->base, "Pending messages, scheduling retry work");
+ queue_work(aw->wq, &aw->msg_retry_work);
+ }
+}
+
+static void panthor_aw_handshake_handle(struct panthor_aw *aw, u64 message)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ bool acked = AM_MSG_ACK_GET(message);
+ u8 version = AM_MSG_VERSION_GET(message);
+ int ret;
+
+ ret = panthor_am_msg_version_validate(&aw->msg, version);
+ if (ret == -EOPNOTSUPP)
+ drm_warn(&ptdev->base,
+ "Msg protocol version less than minimum supported (%u < %u)",
+ version, AM_MSG_MIN_SUPPORTED_VERSION);
+
+ if (!acked) {
+ u64 reply = VM_ARB_INIT_MAKE(1, aw->msg.version);
+
+ panthor_aw_send_msg(aw, reply);
+
+ /* TODO: Handle AW restart */
+ }
+
+ panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_INIT, PANTHOR_AW_STATE_READY);
+}
+
+static void panthor_aw_handshake_init(struct panthor_aw *aw)
+{
+ u64 message = VM_ARB_INIT_MAKE(0, AM_MSG_CURRENT_VERSION);
+
+ panthor_aw_send_msg(aw, message);
+}
+
+static void panthor_aw_handle_message(struct panthor_aw *aw)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+ const u64 message = panthor_am_msg_read(&aw->msg);
+ const u8 msg_id = AM_MSG_ID_GET(message);
+
+ /* currently only support ARB_VM_INIT */
+ if (msg_id == ARB_VM_INIT)
+ panthor_aw_handshake_handle(aw, message);
+ else
+ drm_warn(&ptdev->base, "Unsupported msg id (0x%x)", msg_id);
+}
+
+
+static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
+{
+ struct panthor_irq *pirq = data;
+ struct panthor_device *ptdev = pirq->ptdev;
+ struct panthor_aw *aw = ptdev->aw;
+ u32 status;
+
+ scoped_guard(spinlock_irqsave, &pirq->mask_lock) {
+ if (atomic_read(&pirq->state) != PANTHOR_IRQ_STATE_ACTIVE)
+ return IRQ_NONE;
+ }
+
+ status = gpu_read(pirq->iomem, INT_STAT);
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & WINDOW_IRQ_MESSAGE)
+ panthor_aw_handle_message(aw);
+
+ /* TODO: handle WINDOW_CLOSED*/
+
+ if ((status & WINDOW_IRQ_WINDOW_OPENED) && panthor_aw_is_open(aw))
+ panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST,
+ PANTHOR_AW_STATE_GPU_GRANTED);
+
+ gpu_write(pirq->iomem, INT_CLEAR, status);
+
+ return IRQ_HANDLED;
+}
+
+static void panthor_aw_irq_suspend(struct panthor_irq *pirq)
+{
+ scoped_guard(spinlock_irqsave, &pirq->mask_lock) {
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING);
+ gpu_write(pirq->iomem, INT_MASK, 0);
+ }
+ synchronize_irq(pirq->irq);
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED);
+}
+
+static void panthor_aw_irq_resume(struct panthor_irq *pirq)
+{
+ guard(spinlock_irqsave)(&pirq->mask_lock);
+
+ atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE);
+ gpu_write(pirq->iomem, INT_MASK, pirq->mask);
+}
+
+static int panthor_request_aw_irq(struct panthor_device *ptdev,
+ struct panthor_irq *pirq, int irq, u32 mask,
+ void __iomem *iomem)
+{
+ int ret;
+
+ pirq->ptdev = ptdev;
+ pirq->irq = irq;
+ pirq->mask = mask;
+ pirq->iomem = iomem;
+ spin_lock_init(&pirq->mask_lock);
+
+ ret = devm_request_irq(ptdev->base.dev, irq, panthor_aw_irq_raw_hander,
+ IRQF_SHARED, "panthor-aw", pirq);
+ if (ret)
+ return ret;
+
+ panthor_aw_irq_resume(pirq);
+
+ return 0;
+}
+
+static int panthor_aw_request(struct panthor_aw *aw)
+{
+ int ret;
+
+again:
+ switch(atomic_read(&aw->state)) {
+ case PANTHOR_AW_STATE_GPU_GRANTED:
+ return 0;
+
+ case PANTHOR_AW_STATE_READY:
+ break;
+
+ case PANTHOR_AW_STATE_INIT:
+ panthor_aw_handshake_init(aw);
+ ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_HANDSHAKE_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ goto again;
+ default:
+ ret = panthor_aw_state_wait_transition(
+ aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ goto again;
+ }
+
+ if (!panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_GPU_REQUEST))
+ goto again;
+
+ panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
+
+ /* Wait for GPU to be granted */
+ ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS);
+ if (ret) {
+ panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_aw_yield(struct panthor_aw *aw)
+{
+ panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED);
+
+ return panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+}
+
+int panthor_aw_init(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw;
+ int irq;
+ int ret;
+
+ if (!panthor_hw_has_gpu_discover(ptdev))
+ return 0;
+
+ aw = drmm_kzalloc(&ptdev->base, sizeof(*aw), GFP_KERNEL);
+ if (!aw)
+ return -ENOMEM;
+
+ aw->wq = drmm_alloc_ordered_workqueue(&ptdev->base, "panthor-aw-wq", 0);
+ if (!aw->wq)
+ return -ENOMEM;
+
+ aw->ptdev = ptdev;
+ aw->iomem = ptdev->iomem;
+
+ INIT_WORK(&aw->msg_retry_work, panthor_aw_msg_retry_work);
+
+ init_waitqueue_head(&aw->waitqueue);
+ atomic_set(&aw->state, PANTHOR_AW_STATE_INIT);
+
+ panthor_am_msg_init(&aw->msg, aw->iomem + WINDOW_MESSAGE_BASE);
+
+ ptdev->aw = aw;
+
+ irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu");
+ if (irq < 0)
+ return irq;
+
+ ret = panthor_request_aw_irq(ptdev, &aw->irq, irq, WINDOW_IRQ_MASK,
+ aw->iomem + WINDOW_INT_BASE);
+ if (ret)
+ return ret;
+
+ ret = panthor_aw_request(aw);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void panthor_aw_unplug(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+
+ if (!aw)
+ return;
+
+ disable_work_sync(&aw->msg_retry_work);
+
+ panthor_aw_irq_suspend(&aw->irq);
+}
+
+int panthor_aw_resume(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+ int ret;
+
+ if (!aw)
+ return 0;
+
+ panthor_aw_irq_resume(&aw->irq);
+
+ ret = panthor_aw_request(aw);
+ if (ret) {
+ drm_warn(&ptdev->base, "Timedout waiting for GPU to be granted");
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ panthor_aw_irq_suspend(&aw->irq);
+ return ret;
+}
+
+int panthor_aw_suspend(struct panthor_device *ptdev)
+{
+ struct panthor_aw *aw = ptdev->aw;
+ int ret = 0;
+
+ /* suspend hw components directly if AW is not supported */
+ if (!aw)
+ return 0;
+
+ if (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY)
+ goto out_irq_suspend;
+
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_STATE_GPU_STOPPED))
+ ret = panthor_aw_yield(aw);
+ else
+ ret = panthor_aw_state_wait(
+ aw, PANTHOR_AW_STATE_READY,
+ PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+
+out_irq_suspend:
+ panthor_aw_irq_suspend(&aw->irq);
+ return ret;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
new file mode 100644
index 000000000000..c2b89caa87c4
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_AW_H__
+#define __PANTHOR_AW_H__
+
+#include <linux/types.h>
+
+struct panthor_device;
+
+/**
+ * enum aw_states - Enumeration of possible Access Window states
+ */
+enum aw_states {
+ /** @PANTHOR_AW_STATE_INIT: Initial state, prior to handshake. */
+ PANTHOR_AW_STATE_INIT = 0,
+
+ /** @PANTHOR_AW_STATE_READY: Handshake with Resource Group completed. */
+ PANTHOR_AW_STATE_READY,
+
+ /** @PANTHOR_AW_STATE_GPU_REQUEST: AW requested for GPU access. */
+ PANTHOR_AW_STATE_GPU_REQUEST,
+
+ /** @PANTHOR_AW_STATE_GPU_GRANTED: AW is granted GPU access. */
+ PANTHOR_AW_STATE_GPU_GRANTED,
+
+ /** @PANTHOR_AW_STATE_GPU_STOP: AW is requested to stop GPU access. */
+ PANTHOR_AW_STATE_GPU_STOP,
+
+ /** @PANTHOR_AW_STATE_GPU_STOPPED: AW has stopped GPU access. */
+ PANTHOR_AW_STATE_GPU_STOPPED,
+};
+
+int panthor_aw_init(struct panthor_device *ptdev);
+
+void panthor_aw_unplug(struct panthor_device *ptdev);
+
+int panthor_aw_resume(struct panthor_device *ptdev);
+
+int panthor_aw_suspend(struct panthor_device *ptdev);
+
+#endif
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index bd417d6ae8c0..7248e2aa9da2 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -16,6 +16,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
+#include "panthor_aw.h"
#include "panthor_devfreq.h"
#include "panthor_device.h"
#include "panthor_fw.h"
@@ -100,6 +101,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
panthor_gem_shrinker_unplug(ptdev);
panthor_gpu_unplug(ptdev);
panthor_pwr_unplug(ptdev);
+ panthor_aw_unplug(ptdev);
pm_runtime_dont_use_autosuspend(ptdev->base.dev);
pm_runtime_put_sync_suspend(ptdev->base.dev);
@@ -255,10 +257,18 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
goto err_rpm_put;
- ret = panthor_pwr_init(ptdev);
+ ret = panthor_aw_init(ptdev);
if (ret)
goto err_rpm_put;
+ ret = panthor_hw_info_init(ptdev);
+ if (ret)
+ goto err_unplug_aw;
+
+ ret = panthor_pwr_init(ptdev);
+ if (ret)
+ goto err_unplug_aw;
+
ret = panthor_gpu_init(ptdev);
if (ret)
goto err_unplug_pwr;
@@ -315,6 +325,9 @@ int panthor_device_init(struct panthor_device *ptdev)
err_unplug_pwr:
panthor_pwr_unplug(ptdev);
+err_unplug_aw:
+ panthor_aw_unplug(ptdev);
+
err_rpm_put:
pm_runtime_put_sync_suspend(ptdev->base.dev);
return ret;
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 8b2a9bb426fc..a1092d02a1fe 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -18,6 +18,7 @@
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
+struct panthor_aw;
struct panthor_csf;
struct panthor_csf_ctx;
struct panthor_device;
@@ -196,6 +197,9 @@ struct panthor_device {
/** @hw: GPU-specific data. */
struct panthor_hw *hw;
+ /** @aw: AW-specific data */
+ struct panthor_aw *aw;
+
/** @pwr: Power control management data. */
struct panthor_pwr *pwr;
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 52271fb9db52..aa77d1b7f21c 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -326,7 +326,7 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
return overload_shader_present(ptdev);
}
-static int panthor_hw_info_init(struct panthor_device *ptdev)
+int panthor_hw_info_init(struct panthor_device *ptdev)
{
u64 l2_features = ptdev->gpu_info.l2_features;
u32 major, minor, status;
@@ -433,9 +433,5 @@ int panthor_hw_init(struct panthor_device *ptdev)
if (ret)
return ret;
- ret = panthor_hw_bind_device(ptdev);
- if (ret)
- return ret;
-
- return panthor_hw_info_init(ptdev);
+ return panthor_hw_bind_device(ptdev);
}
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 1b2678ea00db..d60860102b95 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -60,6 +60,8 @@ struct panthor_hw {
};
int panthor_hw_init(struct panthor_device *ptdev);
+int panthor_hw_info_init(struct panthor_device *ptdev);
+
int panthor_hw_power_status_register(void);
void panthor_hw_power_status_unregister(void);
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 16/18] drm/panthor: Synchronize HW component PM transitions
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (14 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 15/18] drm/panthor: Add access-window support Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 17/18] drm/panthor: Route HW component PM through access windows Karunika Choo
` (2 subsequent siblings)
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Split hardware component suspend/resume from the top-level device PM
path and protect those transitions with a dedicated mutex and state
flag.
This lets access-window arbitration suspend or resume GPU components
while runtime PM state remains controlled by the outer PM path.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_aw.c | 1 -
drivers/gpu/drm/panthor/panthor_device.c | 50 +++++++++++++++++++++---
drivers/gpu/drm/panthor/panthor_device.h | 9 +++++
3 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index 00a52175f3a3..146907a3098d 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -192,7 +192,6 @@ static void panthor_aw_handle_message(struct panthor_aw *aw)
drm_warn(&ptdev->base, "Unsupported msg id (0x%x)", msg_id);
}
-
static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
{
struct panthor_irq *pirq = data;
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 7248e2aa9da2..a8aedbee7c97 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -179,6 +179,10 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
return ret;
+ ret = drmm_mutex_init(&ptdev->base, &ptdev->pm.hw_component_lock);
+ if (ret)
+ return ret;
+
ret = drmm_mutex_init(&ptdev->base, &ptdev->pm.mmio_lock);
if (ret)
return ret;
@@ -289,6 +293,9 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
goto err_unplug_mmu;
+ atomic_set(&ptdev->pm.hw_component_state,
+ PANTHOR_DEVICE_PM_STATE_ACTIVE);
+
ret = panthor_sched_init(ptdev);
if (ret)
goto err_unplug_fw;
@@ -477,21 +484,55 @@ int panthor_device_mmap_io(struct panthor_device *ptdev, struct vm_area_struct *
return 0;
}
-static int panthor_device_resume_hw_components(struct panthor_device *ptdev)
+int panthor_device_suspend_hw_components(struct panthor_device *ptdev)
+{
+ guard(mutex)(&ptdev->pm.hw_component_lock);
+
+ if (atomic_read(&ptdev->pm.hw_component_state) ==
+ PANTHOR_DEVICE_PM_STATE_SUSPENDED)
+ return 0;
+
+ if (!pm_runtime_suspended(ptdev->base.dev)) {
+ panthor_fw_suspend(ptdev);
+ panthor_mmu_suspend(ptdev);
+ panthor_gpu_suspend(ptdev);
+ panthor_pwr_suspend(ptdev);
+ }
+
+ atomic_set(&ptdev->pm.hw_component_state,
+ PANTHOR_DEVICE_PM_STATE_SUSPENDED);
+
+ return 0;
+}
+
+int panthor_device_resume_hw_components(struct panthor_device *ptdev)
{
int ret;
+ guard(mutex)(&ptdev->pm.hw_component_lock);
+
+ if (atomic_read(&ptdev->pm.hw_component_state) ==
+ PANTHOR_DEVICE_PM_STATE_ACTIVE)
+ return 0;
+
panthor_pwr_resume(ptdev);
panthor_gpu_resume(ptdev);
panthor_mmu_resume(ptdev);
ret = panthor_fw_resume(ptdev);
- if (!ret)
+ if (!ret) {
+ atomic_set(&ptdev->pm.hw_component_state,
+ PANTHOR_DEVICE_PM_STATE_ACTIVE);
return 0;
+ }
panthor_mmu_suspend(ptdev);
panthor_gpu_suspend(ptdev);
panthor_pwr_suspend(ptdev);
+
+ atomic_set(&ptdev->pm.hw_component_state,
+ PANTHOR_DEVICE_PM_STATE_SUSPENDED);
+
return ret;
}
@@ -602,10 +643,7 @@ int panthor_device_suspend(struct device *dev)
* The end of the reset will happen in the resume path though.
*/
panthor_sched_suspend(ptdev);
- panthor_fw_suspend(ptdev);
- panthor_mmu_suspend(ptdev);
- panthor_gpu_suspend(ptdev);
- panthor_pwr_suspend(ptdev);
+ panthor_device_suspend_hw_components(ptdev);
drm_dev_exit(cookie);
}
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index a1092d02a1fe..d7beb7165577 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -350,6 +350,12 @@ struct panthor_device {
/** @recovery_needed: True when a resume attempt failed. */
atomic_t recovery_needed;
+
+ /** @hw_component_lock: Lock protects HW component PM state transitions */
+ struct mutex hw_component_lock;
+
+ /** @hw_component_state: HW component PM state */
+ atomic_t hw_component_state;
} pm;
/** @profile_mask: User-set profiling flags for job accounting. */
@@ -439,6 +445,9 @@ int panthor_device_mmap_io(struct panthor_device *ptdev,
int panthor_device_resume(struct device *dev);
int panthor_device_suspend(struct device *dev);
+int panthor_device_suspend_hw_components(struct panthor_device *ptdev);
+int panthor_device_resume_hw_components(struct panthor_device *ptdev);
+
static inline int panthor_device_resume_and_get(struct panthor_device *ptdev)
{
int ret = pm_runtime_resume_and_get(ptdev->base.dev);
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 17/18] drm/panthor: Route HW component PM through access windows
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (15 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 16/18] drm/panthor: Synchronize HW component PM transitions Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-05-28 15:05 ` [RFC PATCH 18/18] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
2026-06-08 16:27 ` [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Boris Brezillon
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
Make access-window resume and suspend own GPU access when AW support is
present, while keeping the direct HW component PM path for older GPUs.
Handle WINDOW_CLOSED and arbiter restart events, defer post-yield
cleanup to workqueue context, and make panthor_device_resume_and_get()
ensure that GPU access is obtained before callers touch hardware.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_aw.c | 182 ++++++++++++++++++++---
drivers/gpu/drm/panthor/panthor_aw.h | 8 +-
drivers/gpu/drm/panthor/panthor_device.c | 6 +-
drivers/gpu/drm/panthor/panthor_device.h | 16 +-
4 files changed, 187 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index 146907a3098d..7c7637a8a9f1 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -77,6 +77,9 @@ struct panthor_aw {
/** @msg_retry_work: Work to resend messages */
struct work_struct msg_retry_work;
+
+ /** @post_yield_work: Work to yield the GPU */
+ struct work_struct post_yield_work;
};
static bool panthor_aw_is_open(struct panthor_aw *aw)
@@ -118,6 +121,52 @@ static int panthor_aw_state_wait_transition(struct panthor_aw *aw, u32 timeout_m
timeout_ms);
}
+static void panthor_aw_post_yield_work(struct work_struct *work)
+{
+ struct panthor_aw *aw =
+ container_of(work, struct panthor_aw, post_yield_work);
+ struct panthor_device *ptdev = aw->ptdev;
+ struct device *dev = ptdev->base.dev;
+ int ret;
+
+ /* Something else has progressed the state to READY. */
+ if (atomic_read(&aw->state) != PANTHOR_AW_STATE_GPU_STOPPED)
+ return;
+
+ /*
+ * Ensure in-progress resume is completed and prevent future RPM suspend
+ * to keep the clocks turned on when accessing HW registers.
+ */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_barrier(dev);
+
+ ret = panthor_device_suspend_hw_components(ptdev);
+ if (ret)
+ dev_warn(dev, "Failed to suspend hardware components.");
+
+ panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+
+ /*
+ * schedule an immediate tick to re-evaluate if there is any additional
+ * work. This will request access to the GPU again and if the driver has
+ * gone idle, it will yield GPU access via the runtime suspend path.
+ */
+ panthor_sched_resume(ptdev);
+
+ pm_runtime_put_noidle(dev);
+}
+
+static bool panthor_aw_schedule_post_yield_work(struct panthor_aw *aw)
+{
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_STATE_GPU_STOPPED)) {
+ queue_work(aw->wq, &aw->post_yield_work);
+ return true;
+ }
+
+ return false;
+}
+
static void panthor_aw_msg_retry_work(struct work_struct *work)
{
struct panthor_aw *aw =
@@ -166,7 +215,9 @@ static void panthor_aw_handshake_handle(struct panthor_aw *aw, u64 message)
panthor_aw_send_msg(aw, reply);
- /* TODO: Handle AW restart */
+ /* Arbiter was restarted. Window is no longer granted access. */
+ if (panthor_aw_schedule_post_yield_work(aw))
+ drm_info(&ptdev->base, "Arbiter was restarted, yielding GPU");
}
panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_INIT, PANTHOR_AW_STATE_READY);
@@ -192,6 +243,38 @@ static void panthor_aw_handle_message(struct panthor_aw *aw)
drm_warn(&ptdev->base, "Unsupported msg id (0x%x)", msg_id);
}
+static void panthor_aw_handle_window_closed(struct panthor_aw *aw)
+{
+ struct panthor_device *ptdev = aw->ptdev;
+
+ /* Ignore this WINDOW_CLOSED as part of reset operation. */
+ if (panthor_device_reset_is_pending(ptdev))
+ return;
+
+ if (atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_STOPPED ||
+ atomic_read(&aw->state) == PANTHOR_AW_STATE_READY ||
+ atomic_read(&aw->state) == PANTHOR_AW_STATE_INIT) {
+ drm_warn(&ptdev->base, "Unexpected WINDOW_CLOSED received");
+ return;
+ }
+
+ /*
+ * Window may have been closed immediately after opening.
+ * Setting state back to READY will prevent WINDOW_OPENDED from
+ * mistakenly transitioning the state to GRANTED.
+ */
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST,
+ PANTHOR_AW_STATE_READY))
+ return;
+
+ /* Triggerred from messaged-based yield. Unblock its wait */
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_STOPPED_IDLE,
+ PANTHOR_AW_STATE_READY))
+ return;
+
+ panthor_aw_schedule_post_yield_work(aw);
+}
+
static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
{
struct panthor_irq *pirq = data;
@@ -211,7 +294,9 @@ static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
if (status & WINDOW_IRQ_MESSAGE)
panthor_aw_handle_message(aw);
- /* TODO: handle WINDOW_CLOSED*/
+ if (status & WINDOW_IRQ_WINDOW_CLOSED)
+ panthor_aw_handle_window_closed(aw);
+
if ((status & WINDOW_IRQ_WINDOW_OPENED) && panthor_aw_is_open(aw))
panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST,
@@ -264,6 +349,7 @@ static int panthor_request_aw_irq(struct panthor_device *ptdev,
static int panthor_aw_request(struct panthor_aw *aw)
{
+ struct panthor_device *ptdev = aw->ptdev;
int ret;
again:
@@ -271,6 +357,9 @@ static int panthor_aw_request(struct panthor_aw *aw)
case PANTHOR_AW_STATE_GPU_GRANTED:
return 0;
+ case PANTHOR_AW_STATE_GPU_STOPPED:
+ return -EAGAIN;
+
case PANTHOR_AW_STATE_READY:
break;
@@ -278,14 +367,22 @@ static int panthor_aw_request(struct panthor_aw *aw)
panthor_aw_handshake_init(aw);
ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
PANTHOR_AW_HANDSHAKE_TIMEOUT_MS);
- if (ret)
+ if (ret) {
+ drm_err(&ptdev->base,
+ "Timed out waiting for handshake to complete");
return ret;
+ }
+
goto again;
default:
ret = panthor_aw_state_wait_transition(
aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
- if (ret)
+ if (ret) {
+ drm_err(&ptdev->base,
+ "Timed out waiting for AW state transition");
return ret;
+ }
+
goto again;
}
@@ -295,15 +392,20 @@ static int panthor_aw_request(struct panthor_aw *aw)
panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
- /* Wait for GPU to be granted */
- ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_GPU_GRANTED,
- PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS);
+ ret = panthor_aw_wait_cond(
+ aw, atomic_read(&aw->state) != PANTHOR_AW_STATE_GPU_REQUEST,
+ PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS);
if (ret) {
- panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+ if (!panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST,
+ PANTHOR_AW_STATE_READY))
+ goto again;
+
+ drm_err(&ptdev->base,
+ "Timed out waiting for AW GPU to be granted");
return ret;
}
- return 0;
+ goto again;
}
static int panthor_aw_yield(struct panthor_aw *aw)
@@ -335,6 +437,7 @@ int panthor_aw_init(struct panthor_device *ptdev)
aw->iomem = ptdev->iomem;
INIT_WORK(&aw->msg_retry_work, panthor_aw_msg_retry_work);
+ INIT_WORK(&aw->post_yield_work, panthor_aw_post_yield_work);
init_waitqueue_head(&aw->waitqueue);
atomic_set(&aw->state, PANTHOR_AW_STATE_INIT);
@@ -367,6 +470,7 @@ void panthor_aw_unplug(struct panthor_device *ptdev)
return;
disable_work_sync(&aw->msg_retry_work);
+ disable_work_sync(&aw->post_yield_work);
panthor_aw_irq_suspend(&aw->irq);
}
@@ -376,17 +480,48 @@ int panthor_aw_resume(struct panthor_device *ptdev)
struct panthor_aw *aw = ptdev->aw;
int ret;
+ /* resume hw components directly if AW is not supported */
if (!aw)
- return 0;
+ return panthor_device_resume_hw_components(ptdev);
panthor_aw_irq_resume(&aw->irq);
+again:
ret = panthor_aw_request(aw);
+ if (ret)
+ goto err_out;
+
+ ret = panthor_device_resume_hw_components(ptdev);
if (ret) {
- drm_warn(&ptdev->base, "Timedout waiting for GPU to be granted");
+ /* Yield GPU access if HW components failed to resume */
+ if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+ PANTHOR_AW_STATE_STOPPED_IDLE)) {
+ /*
+ * If failure is caused by WINDOW_CLOSED and the IRQ is
+ * handled after this condition, AW may emit a rogue
+ * idle event that will be ignored by the arbitration
+ * scheduler. WINDOW_CLOSED handler will then transition
+ * the state back to READY.
+ */
+ panthor_aw_yield(aw);
+ }
+
goto err_out;
}
+ /*
+ * WINDOW_CLOSED won the race. post_yield_work owns component cleanup
+ * and will transition state to READY after this PM transition is
+ * complete.
+ */
+ if (atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_STOPPED) {
+ ret = -EAGAIN;
+ goto err_out;
+ }
+
+ if (atomic_read(&aw->state) != PANTHOR_AW_STATE_GPU_GRANTED)
+ goto again;
+
return 0;
err_out:
@@ -401,20 +536,33 @@ int panthor_aw_suspend(struct panthor_device *ptdev)
/* suspend hw components directly if AW is not supported */
if (!aw)
- return 0;
+ return panthor_device_suspend_hw_components(ptdev);
if (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY)
goto out_irq_suspend;
+ if (atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_GRANTED) {
+ ret = panthor_device_suspend_hw_components(ptdev);
+ if (ret)
+ goto out_irq_suspend;
+ }
+
if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
- PANTHOR_AW_STATE_GPU_STOPPED))
+ PANTHOR_AW_STATE_STOPPED_IDLE)) {
ret = panthor_aw_yield(aw);
- else
- ret = panthor_aw_state_wait(
- aw, PANTHOR_AW_STATE_READY,
- PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+ if (ret)
+ panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+ }
out_irq_suspend:
panthor_aw_irq_suspend(&aw->irq);
return ret;
}
+
+int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev)
+{
+ if (!ptdev->aw)
+ return 0;
+
+ return panthor_aw_resume(ptdev);
+}
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
index c2b89caa87c4..7a46a1e18093 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.h
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -24,10 +24,10 @@ enum aw_states {
/** @PANTHOR_AW_STATE_GPU_GRANTED: AW is granted GPU access. */
PANTHOR_AW_STATE_GPU_GRANTED,
- /** @PANTHOR_AW_STATE_GPU_STOP: AW is requested to stop GPU access. */
- PANTHOR_AW_STATE_GPU_STOP,
+ /** @PANTHOR_AW_STATE_STOPPED_IDLE: AW has stopped GPU access. */
+ PANTHOR_AW_STATE_STOPPED_IDLE,
- /** @PANTHOR_AW_STATE_GPU_STOPPED: AW has stopped GPU access. */
+ /** @PANTHOR_AW_STATE_GPU_STOPPED: Window was closed, cleanup required. */
PANTHOR_AW_STATE_GPU_STOPPED,
};
@@ -39,4 +39,6 @@ int panthor_aw_resume(struct panthor_device *ptdev);
int panthor_aw_suspend(struct panthor_device *ptdev);
+int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev);
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index a8aedbee7c97..441fe89d2e0a 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -570,11 +570,11 @@ int panthor_device_resume(struct device *dev)
atomic_set(&ptdev->reset.pending, 0);
}
- ret = panthor_device_resume_hw_components(ptdev);
+ ret = panthor_aw_resume(ptdev);
if (ret && ptdev->reset.fast) {
drm_err(&ptdev->base, "Fast reset failed, trying a slow reset");
ptdev->reset.fast = false;
- ret = panthor_device_resume_hw_components(ptdev);
+ ret = panthor_aw_resume(ptdev);
}
if (!ret)
@@ -643,7 +643,7 @@ int panthor_device_suspend(struct device *dev)
* The end of the reset will happen in the resume path though.
*/
panthor_sched_suspend(ptdev);
- panthor_device_suspend_hw_components(ptdev);
+ panthor_aw_suspend(ptdev);
drm_dev_exit(cookie);
}
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index d7beb7165577..d01456637f4c 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -18,6 +18,8 @@
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
+#include "panthor_aw.h"
+
struct panthor_aw;
struct panthor_csf;
struct panthor_csf_ctx;
@@ -464,8 +466,18 @@ static inline int panthor_device_resume_and_get(struct panthor_device *ptdev)
* succeeded. Given resume errors are not expected, this is probably
* something we can live with.
*/
- if (ret && atomic_cmpxchg(&ptdev->pm.recovery_needed, 1, 0) == 1)
- pm_runtime_set_suspended(ptdev->base.dev);
+ if (ret) {
+ if (atomic_cmpxchg(&ptdev->pm.recovery_needed, 1, 0) == 1)
+ pm_runtime_set_suspended(ptdev->base.dev);
+
+ return ret;
+ }
+
+ ret = panthor_aw_ensure_gpu_access(ptdev);
+ if (ret) {
+ pm_runtime_put_autosuspend(ptdev->base.dev);
+ return ret;
+ }
return ret;
}
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* [RFC PATCH 18/18] drm/panthor: Tolerate access-window loss during HW waits
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (16 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 17/18] drm/panthor: Route HW component PM through access windows Karunika Choo
@ 2026-05-28 15:05 ` Karunika Choo
2026-06-08 16:27 ` [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Boris Brezillon
18 siblings, 0 replies; 42+ messages in thread
From: Karunika Choo @ 2026-05-28 15:05 UTC (permalink / raw)
To: dri-devel
Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, linux-kernel
The arbiter can close the access window while the driver is waiting for
GPU, power, or firmware acknowledgements. In that case the pending MMIO
wait cannot complete, but it should not be treated as a hardware timeout
or trigger recovery.
Add a helper to report whether the access window still owns GPU access
and use it to suppress timeout handling once access has been revoked.
Also skip deferred scheduler FW event processing when the window is no
longer available.
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
drivers/gpu/drm/panthor/panthor_aw.c | 8 ++++++++
drivers/gpu/drm/panthor/panthor_aw.h | 2 ++
drivers/gpu/drm/panthor/panthor_gpu.c | 4 +++-
drivers/gpu/drm/panthor/panthor_pwr.c | 27 +++++++++++++++++++------
drivers/gpu/drm/panthor/panthor_sched.c | 9 ++++++---
5 files changed, 40 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index 7c7637a8a9f1..f23022e2b4cd 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -566,3 +566,11 @@ int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev)
return panthor_aw_resume(ptdev);
}
+
+bool panthor_aw_has_gpu_access(struct panthor_device *ptdev)
+{
+ if (!ptdev->aw)
+ return true;
+
+ return (atomic_read(&ptdev->aw->state) == PANTHOR_AW_STATE_GPU_GRANTED);
+}
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
index 7a46a1e18093..1da613fa330a 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.h
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -41,4 +41,6 @@ int panthor_aw_suspend(struct panthor_device *ptdev);
int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev);
+bool panthor_aw_has_gpu_access(struct panthor_device *ptdev);
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index 3c88a25287bf..129bb3779ebd 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -17,6 +17,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
+#include "panthor_aw.h"
#include "panthor_device.h"
#include "panthor_device_io.h"
#include "panthor_gpu.h"
@@ -352,7 +353,8 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
msecs_to_jiffies(100))) {
spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags);
if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 &&
- !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED))
+ !(gpu_read(gpu->irq.iomem, INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED) &&
+ panthor_aw_has_gpu_access(ptdev))
ret = -ETIMEDOUT;
else
ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED;
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
index b833e4559e45..0c3fa311c1af 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -10,6 +10,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
+#include "panthor_aw.h"
#include "panthor_device.h"
#include "panthor_device_io.h"
#include "panthor_gpu_regs.h"
@@ -118,7 +119,8 @@ static int panthor_pwr_reset(struct panthor_device *ptdev, u32 reset_cmd)
msecs_to_jiffies(PWR_RESET_TIMEOUT_MS))) {
guard(spinlock_irqsave)(&ptdev->pwr->reqs_lock);
- if (reset_pending(ptdev) && !reset_irq_raised(ptdev)) {
+ if (reset_pending(ptdev) && !reset_irq_raised(ptdev) &&
+ panthor_aw_has_gpu_access(ptdev)) {
drm_err(&ptdev->base, "RESET timed out (0x%x)", reset_cmd);
return -ETIMEDOUT;
}
@@ -262,9 +264,14 @@ static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd,
panthor_pwr_write_command(ptdev, pwr_cmd, mask);
- ret = gpu_read64_poll_timeout(pwr->iomem, ready_reg, val, (mask & val) == expected_val,
+ ret = gpu_read64_poll_timeout(pwr->iomem, ready_reg, val,
+ ((mask & val) == expected_val ||
+ !panthor_aw_has_gpu_access(ptdev)),
100, timeout_us);
if (ret) {
+ if (!panthor_aw_has_gpu_access(ptdev))
+ return 0;
+
drm_err(&ptdev->base,
"timeout waiting on %s power domain transition, cmd(0x%x), arg(0x%llx)",
get_domain_name(domain), pwr_cmd, mask);
@@ -326,9 +333,13 @@ static int retract_domain(struct panthor_device *ptdev, u32 domain)
* allow-flag will be set with delegated-flag being cleared.
*/
ret = gpu_read64_poll_timeout(pwr->iomem, PWR_STATUS, val,
- ((delegated_mask | allow_mask) & val) == allow_mask, 10,
- PWR_TRANSITION_TIMEOUT_US);
+ (((delegated_mask | allow_mask) & val) == allow_mask ||
+ !panthor_aw_has_gpu_access(ptdev)),
+ 10, PWR_TRANSITION_TIMEOUT_US);
if (ret) {
+ if (!panthor_aw_has_gpu_access(ptdev))
+ return 0;
+
drm_err(&ptdev->base, "Retracting %s domain timeout, cmd(0x%x)",
get_domain_name(domain), pwr_cmd);
return ret;
@@ -383,9 +394,13 @@ static int delegate_domain(struct panthor_device *ptdev, u32 domain)
* allow-flag will be cleared with delegated-flag being set.
*/
ret = gpu_read64_poll_timeout(pwr->iomem, PWR_STATUS, val,
- ((delegated_mask | allow_mask) & val) == delegated_mask,
+ (((delegated_mask | allow_mask) & val) == delegated_mask ||
+ !panthor_aw_has_gpu_access(ptdev)),
10, PWR_TRANSITION_TIMEOUT_US);
if (ret) {
+ if (!panthor_aw_has_gpu_access(ptdev))
+ return 0;
+
drm_err(&ptdev->base, "Delegating %s domain timeout, cmd(0x%x)",
get_domain_name(domain), pwr_cmd);
return ret;
@@ -512,7 +527,7 @@ void panthor_pwr_l2_power_off(struct panthor_device *ptdev)
const u64 pwr_status = gpu_read64(pwr->iomem, PWR_STATUS);
/* Abort if L2 power off constraints are not satisfied */
- if (!(pwr_status & l2_allow_mask)) {
+ if (!(pwr_status & l2_allow_mask) && panthor_aw_has_gpu_access(ptdev)) {
drm_warn(&ptdev->base, "Power off L2 domain not allowed");
return;
}
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 5b34032deff8..f4bfb8fab0dd 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -1875,7 +1875,10 @@ static void process_fw_events_work(struct work_struct *work)
u32 events = atomic_xchg(&sched->fw_events, 0);
struct panthor_device *ptdev = sched->ptdev;
- mutex_lock(&sched->lock);
+ guard(mutex)(&sched->lock);
+
+ if (!panthor_aw_has_gpu_access(ptdev))
+ return;
if (events & JOB_INT_GLOBAL_IF) {
sched_process_global_irq_locked(ptdev);
@@ -1888,8 +1891,6 @@ static void process_fw_events_work(struct work_struct *work)
sched_process_csg_irq_locked(ptdev, csg_id);
events &= ~BIT(csg_id);
}
-
- mutex_unlock(&sched->lock);
}
/**
@@ -1983,6 +1984,8 @@ static int csgs_upd_ctx_apply_locked(struct panthor_device *ptdev,
csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
ret = panthor_fw_csg_wait_acks(ptdev, csg_id, req_mask, &acked, 100);
+ if (ret && !panthor_aw_has_gpu_access(ptdev))
+ ret = 0;
if (acked & CSG_ENDPOINT_CONFIG)
csg_slot_sync_priority_locked(ptdev, csg_id);
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support
2026-05-28 15:05 [RFC PATCH 00/18] drm/panthor: Add Mali v15 AM virtualization support Karunika Choo
` (17 preceding siblings ...)
2026-05-28 15:05 ` [RFC PATCH 18/18] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
@ 2026-06-08 16:27 ` Boris Brezillon
18 siblings, 0 replies; 42+ messages in thread
From: Boris Brezillon @ 2026-06-08 16:27 UTC (permalink / raw)
To: Karunika Choo
Cc: dri-devel, nd, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
linux-kernel
On Thu, 28 May 2026 16:05:28 +0100
Karunika Choo <karunika.choo@arm.com> wrote:
> This series adds initial support for Mali v15 AM GPUs and the
> HW-assisted virtualization blocks around them.
>
> The first patches make the core panthor code less dependent on fixed
> register layouts by caching decoded GPU_ID fields and moving register
> base offsets into the HW description. Mali v15 support is then added on
> top, including 64-bit GPU_ID decoding and optional devfreq handling for
> systems where frequency control is owned by AM_GOVERNOR.
>
> The rest of the series introduces the AM_SYSTEM, AM_PARTITION_CONTROL,
> AM_RESOURCE_GROUP, arbitration scheduler and access-window pieces needed
> to coordinate GPU ownership between the arbiter and VM-side panthor
> instances. Finally, HW component PM is routed through access windows and
> waits are taught to tolerate loss of the access window.
>
> This series has a soft dependency on firmware scheduling support that
> will be added later to handle YIELD_NOW interrupts.
>
> The resulting support is intentionally minimal, but provides the
> driver-side plumbing needed for Mali v15 AM systems.
>
> Kind regards,
> Karunika Choo
>
> Karunika Choo (18):
> drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
> drm/panthor: Move register access helpers out of panthor_device.h
> drm/panthor: Parse and store GPU_ID fields
> drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs
> drm/panthor: Move register base offsets to the HW description
> drm/panthor: Derive MMU AS register addresses from base and stride
> drm/panthor: Add Mali v15 hardware support
> drm/panthor: Skip devfreq when no OPP table is present
The prep patches look mostly okay to me (baring a few nitpicks).
> drm/panthor: Add basic AM_SYSTEM support
> drm/panthor: Add AM_PARTITION_CONTROL support
> drm/panthor: Add AM message helpers
> drm/panthor: Add AM_RESOURCE_GROUP support
> drm/panthor: Add arbitration scheduler
> drm/panthor: Route arbitration events
> drm/panthor: Add access-window support
> drm/panthor: Synchronize HW component PM transitions
> drm/panthor: Route HW component PM through access windows
> drm/panthor: Tolerate access-window loss during HW waits
For those, I think I'd like to have the full picture before providing a
proper review. Right now it's unclear what the FW-based YIELD looks
like, and the retry-mechanism on an access-window-lost seems a bit
fragile (the only places where I've seen this tested is in iopoll
loops, but AW can go away at any time).
>
> drivers/gpu/drm/panthor/Kconfig | 14 +
> drivers/gpu/drm/panthor/Makefile | 3 +
> drivers/gpu/drm/panthor/arbitration/Makefile | 17 +
> .../panthor/arbitration/panthor_arbitration.h | 45 ++
> .../arbitration/panthor_arbitration_drv.c | 199 +++++
> .../arbitration/panthor_arbitration_sched.c | 692 ++++++++++++++++++
> .../arbitration/panthor_arbitration_sched.h | 25 +
> .../arbitration/panthor_partition_control.c | 390 ++++++++++
> .../arbitration/panthor_partition_control.h | 27 +
> .../arbitration/panthor_resource_group.c | 288 ++++++++
> .../arbitration/panthor_resource_group.h | 20 +
> drivers/gpu/drm/panthor/panthor_am_msg.h | 157 ++++
> drivers/gpu/drm/panthor/panthor_aw.c | 576 +++++++++++++++
> drivers/gpu/drm/panthor/panthor_aw.h | 46 ++
> drivers/gpu/drm/panthor/panthor_devfreq.c | 13 +-
> drivers/gpu/drm/panthor/panthor_device.c | 69 +-
> drivers/gpu/drm/panthor/panthor_device.h | 134 ++--
> drivers/gpu/drm/panthor/panthor_device_io.h | 81 ++
> drivers/gpu/drm/panthor/panthor_drv.c | 1 +
> drivers/gpu/drm/panthor/panthor_fw.c | 8 +-
> drivers/gpu/drm/panthor/panthor_fw_regs.h | 2 -
> drivers/gpu/drm/panthor/panthor_gpu.c | 14 +-
> .../drm/panthor/panthor_gpu_discover_regs.h | 42 ++
> drivers/gpu/drm/panthor/panthor_gpu_regs.h | 2 -
> drivers/gpu/drm/panthor/panthor_heap.c | 3 +-
> drivers/gpu/drm/panthor/panthor_hw.c | 163 ++++-
> drivers/gpu/drm/panthor/panthor_hw.h | 45 +-
> drivers/gpu/drm/panthor/panthor_mmu.c | 36 +-
> drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 +-
> drivers/gpu/drm/panthor/panthor_pwr.c | 32 +-
> drivers/gpu/drm/panthor/panthor_pwr_regs.h | 3 -
> drivers/gpu/drm/panthor/panthor_sched.c | 9 +-
> drivers/gpu/drm/panthor/system/Makefile | 12 +
> .../gpu/drm/panthor/system/panthor_system.c | 298 ++++++++
> include/uapi/drm/panthor_drm.h | 20 +-
> 35 files changed, 3338 insertions(+), 171 deletions(-)
> create mode 100644 drivers/gpu/drm/panthor/arbitration/Makefile
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
> create mode 100644 drivers/gpu/drm/panthor/arbitration/panthor_resource_group.h
> create mode 100644 drivers/gpu/drm/panthor/panthor_am_msg.h
> create mode 100644 drivers/gpu/drm/panthor/panthor_aw.c
> create mode 100644 drivers/gpu/drm/panthor/panthor_aw.h
> create mode 100644 drivers/gpu/drm/panthor/panthor_device_io.h
> create mode 100644 drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
> create mode 100644 drivers/gpu/drm/panthor/system/Makefile
> create mode 100644 drivers/gpu/drm/panthor/system/panthor_system.c
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 42+ messages in thread