All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support
@ 2026-11-10 15:38 Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 01/28] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
                   ` (27 more replies)
  0 siblings, 28 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel; +Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel

This series adds HW-assisted para-virtualization support for Mali v15
GPUs. v15 GPUs provide 16 register sets for job submission known as
access windows (AWs). The GPU hardware itself is then time-sliced across
each requesting access window.

v2:
- Rebased on drm-misc-next and collected R-bs.
- Moved the Mali 5th-Gen AM GPU binding to a separate schema.
  Extended the AM system and arbitration bindings to describe
  resource assignments for GPUs that support partitioning.
- Corrected neural accelerator and GPU ID naming. Preserved GPU ID
  log output for older GPUs and fixed MMU interrupt initialization.
- Fixed runtime PM rollback and driver cleanup. Protected register
  access from shared IRQ handlers while the hardware is powered off.
- Strengthened AM protocol-version checks. Reworked message retries,
  arbitration command retries and timeout handling.
- Protected access-window assignments with a lock and saved requests
  received before assignment. Assigned all access windows to
  partition 0 when its mask is absent or zero.
- Reworked access-window request, grant and yield handling. Resent
  pending GPU requests after an arbiter restart.
- Stopped scheduler tick work before system suspend. Fixed resume
  retries, autosuspend after yield and cleanup during device removal.
- Improved handling of GPU access loss during firmware waits and
  reset. Cleared stale software reset state on resume.
- Added partition recovery during probe to ensure partitions reach
  RESET before arbitration starts.
- Bumped the DRM version to 1.10 for the extended gpu_info fields.
- Link to v1: https://lore.kernel.org/all/20260922204535.2850094-1-karunika.choo@arm.com/
v1:
- Rebased on drm-misc-next.
- Added device-tree bindings for the GPU and virtualization components.
- Improved GPU identification and register handling.
- Updated AM messaging to protocol v2.
- Improved GPU scheduling and recovery from timeouts and failed
  commands.
- Added access-window assignments through device tree and earlier GPU
  release when another window is waiting.
- Improved power management, driver setup and cleanup.
- Fixed races involving GPU resets, loss of access and late grants.
- Kept frequency reporting available without devfreq and documented
  scheduler timeout settings.
- Link to RFC: https://lore.kernel.org/all/20260528150546.3168527-1-karunika.choo@arm.com/

Karunika Choo (28):
  drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
  drm/panthor: Rename neural accelerator feature macro to NX
  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
  dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  drm/panthor: Add Mali v15 hardware support
  drm/panthor: Skip devfreq when no OPP table is present
  dt-bindings: gpu: panthor: Document AM system bindings
  drm/panthor: Add AM_SYSTEM platform driver
  dt-bindings: gpu: panthor: Document panthor-arbitration bindings
  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 AW assignment tracking
  drm/panthor: Handle partition control INVALID_COMMAND interrupt
  drm/panthor: Request AW to yield GPU access on idle
  drm/panthor: Add access-window support
  drm/panthor: Synchronize HW component PM transitions
  drm/panthor: Quiesce scheduler tick during system suspend
  drm/panthor: Route HW component PM through access windows
  drm/panthor: Tolerate access-window loss during HW waits
  drm/panthor: Prevent missed post-yield cleanup due to reset
  drm/panthor: Reset partitions before starting arbitration

 .../testing/sysfs-driver-panthor-arbitration  |  18 +
 .../gpu/arm,mali-gen5-am-arbitration.yaml     | 198 ++++
 .../bindings/gpu/arm,mali-gen5-am-system.yaml | 196 ++++
 .../bindings/gpu/arm,mali-gen5-am.yaml        |  90 ++
 MAINTAINERS                                   |   2 +
 drivers/gpu/drm/panthor/Kconfig               |  20 +
 drivers/gpu/drm/panthor/Makefile              |   3 +
 drivers/gpu/drm/panthor/arbitration/Makefile  |  15 +
 .../panthor/arbitration/panthor_arbitration.h |  73 ++
 .../arbitration/panthor_arbitration_drv.c     | 451 +++++++++
 .../arbitration/panthor_arbitration_sched.c   | 882 ++++++++++++++++++
 .../arbitration/panthor_arbitration_sched.h   |  45 +
 .../arbitration/panthor_partition_control.c   | 525 +++++++++++
 .../arbitration/panthor_partition_control.h   |  29 +
 .../arbitration/panthor_resource_group.c      | 373 ++++++++
 .../arbitration/panthor_resource_group.h      |  20 +
 drivers/gpu/drm/panthor/panthor_am_msg.h      | 182 ++++
 drivers/gpu/drm/panthor/panthor_aw.c          | 689 ++++++++++++++
 drivers/gpu/drm/panthor/panthor_aw.h          |  57 ++
 drivers/gpu/drm/panthor/panthor_devfreq.c     |  29 +-
 drivers/gpu/drm/panthor/panthor_device.c      | 112 ++-
 drivers/gpu/drm/panthor/panthor_device.h      | 146 ++-
 drivers/gpu/drm/panthor/panthor_device_io.h   |  84 ++
 drivers/gpu/drm/panthor/panthor_drv.c         |  13 +-
 drivers/gpu/drm/panthor/panthor_fw.c          |  27 +-
 drivers/gpu/drm/panthor/panthor_fw.h          |   2 +
 drivers/gpu/drm/panthor/panthor_fw_regs.h     |   2 -
 drivers/gpu/drm/panthor/panthor_gpu.c         |  13 +-
 .../drm/panthor/panthor_gpu_discover_regs.h   |  42 +
 drivers/gpu/drm/panthor/panthor_gpu_regs.h    |   4 +-
 drivers/gpu/drm/panthor/panthor_heap.c        |   3 +-
 drivers/gpu/drm/panthor/panthor_hw.c          | 209 ++++-
 drivers/gpu/drm/panthor/panthor_hw.h          |  59 +-
 drivers/gpu/drm/panthor/panthor_mmu.c         |  47 +-
 drivers/gpu/drm/panthor/panthor_mmu_regs.h    |  23 +-
 drivers/gpu/drm/panthor/panthor_pwr.c         |  36 +-
 drivers/gpu/drm/panthor/panthor_pwr_regs.h    |   2 -
 drivers/gpu/drm/panthor/panthor_sched.c       |  21 +
 drivers/gpu/drm/panthor/panthor_sched.h       |   2 +
 drivers/gpu/drm/panthor/system/Makefile       |  13 +
 .../drm/panthor/system/panthor_system_drv.c   | 344 +++++++
 include/uapi/drm/panthor_drm.h                |  23 +-
 42 files changed, 4939 insertions(+), 185 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-panthor-arbitration
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-arbitration.yaml
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
 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_drv.c

--
2.43.0


^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v2 01/28] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX Karunika Choo
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Nicolas Frattaroli

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.

Fixes: c40b50c3cfbe ("drm/panthor: Implement reading shader_present from nvmem")
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu
v1:
- Picked up R-bs from Boris and Steve
- Added Fixes tag

 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 ea1e222ef1210..34dc4b9d9dc09 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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 01/28] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:47   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 03/28] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Rename GPU_FEATURES_NEURAL_ENGINE to GPU_FEATURES_NX.
Update the panthor_hw helper to use the new name.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
 drivers/gpu/drm/panthor/panthor_gpu_regs.h | 2 +-
 drivers/gpu/drm/panthor/panthor_hw.h       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_gpu_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_regs.h
index 5340966caef37..c7581b6d4c5be 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu_regs.h
@@ -67,7 +67,7 @@
 
 #define GPU_FEATURES					0x60
 #define   GPU_FEATURES_RAY_INTERSECTION			BIT(2)
-#define   GPU_FEATURES_NEURAL_ENGINE			BIT(4)
+#define   GPU_FEATURES_NX				BIT(4)
 #define   GPU_FEATURES_RAY_TRAVERSAL			BIT(5)
 
 #define GPU_TIMESTAMP_OFFSET				0x88
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index eeca936f44bae..f770dc40e6216 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -64,7 +64,7 @@ static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
 static inline bool panthor_hw_has_neural(struct panthor_device *ptdev)
 {
 	return (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14 &&
-		ptdev->gpu_info.gpu_features & GPU_FEATURES_NEURAL_ENGINE);
+		ptdev->gpu_info.gpu_features & GPU_FEATURES_NX);
 }
 
 #endif /* __PANTHOR_HW_H__ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 03/28] drm/panthor: Move register access helpers out of panthor_device.h
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 01/28] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 04/28] drm/panthor: Parse and store GPU_ID fields Karunika Choo
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Move register access helpers out of panthor_device.h to allow reuse by
other panthor drivers that will be added to enable v15 support.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu
- Fix includes raised by sashiko
- Link: https://lore.kernel.org/all/20260922205441.263661F000FF@smtp.kernel.org/
v1:
- Includes "panthor_device_io.h" directly in "panthor_device.h" instead
  of peppering multiple files with the header.

 drivers/gpu/drm/panthor/panthor_device.h    | 73 +-----------------
 drivers/gpu/drm/panthor/panthor_device_io.h | 84 +++++++++++++++++++++
 2 files changed, 86 insertions(+), 71 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 b55a3f9edd414..4add31b12cfd7 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -19,6 +19,8 @@
 #include <drm/gpu_scheduler.h>
 #include <drm/panthor_drm.h>
 
+#include "panthor_device_io.h"
+
 struct panthor_csf;
 struct panthor_csf_ctx;
 struct panthor_device;
@@ -495,77 +497,6 @@ panthor_exception_is_fault(u32 exception_code)
 const char *panthor_exception_name(struct panthor_device *ptdev,
 				   u32 exception_code);
 
-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)
-
 #define INT_RAWSTAT 0x0
 #define INT_CLEAR   0x4
 #define INT_MASK    0x8
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 0000000000000..ce417c64abb44
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_device_io.h
@@ -0,0 +1,84 @@
+/* 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/io.h>
+#include <linux/iopoll.h>
+#include <linux/wordpart.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__ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 04/28] drm/panthor: Parse and store GPU_ID fields
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (2 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 03/28] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 05/28] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu
- Avoid modifying the printed id field for existing GPUs.
- Link: https://lore.kernel.org/all/20260922205659.3C4F21F000FF@smtp.kernel.org/
v1:
- changed struct panthor_gpu_id into a flat structure instead of
  inconsistent groupings.

 drivers/gpu/drm/panthor/panthor_device.h | 29 ++++++++++++++++++++++++
 drivers/gpu/drm/panthor/panthor_fw.c     |  4 ++--
 drivers/gpu/drm/panthor/panthor_gpu.c    |  2 +-
 drivers/gpu/drm/panthor/panthor_hw.c     | 24 ++++++++++++++------
 drivers/gpu/drm/panthor/panthor_hw.h     |  4 ++--
 5 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 4add31b12cfd7..f0a96ff0f13da 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -111,6 +111,32 @@ struct panthor_irq {
 	enum panthor_irq_state state;
 };
 
+/**
+ * struct panthor_gpu_id - Parsed GPU_ID fields
+ */
+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;
+};
+
 /**
  * enum panthor_device_profiling_mode - Profiling state
  */
@@ -170,6 +196,9 @@ struct panthor_device {
 	/** @mmu_info: MMU info */
 	struct drm_panthor_mmu_info mmu_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 4f1fab66a13bf..13efed3a2ea72 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -809,8 +809,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 b8df5663f04e4..c5c37f22993d6 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 34dc4b9d9dc09..854874a9f322a 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -119,9 +119,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);
@@ -245,9 +245,9 @@ 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",
@@ -273,7 +273,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++) {
@@ -295,10 +295,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 f770dc40e6216..c4a490bc39de6 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -58,12 +58,12 @@ 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;
 }
 
 static inline bool panthor_hw_has_neural(struct panthor_device *ptdev)
 {
-	return (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14 &&
+	return (ptdev->gpu_id.arch_major >= 14 &&
 		ptdev->gpu_info.gpu_features & GPU_FEATURES_NX);
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 05/28] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (3 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 04/28] drm/panthor: Parse and store GPU_ID fields Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 06/28] drm/panthor: Move register base offsets to the HW description Karunika Choo
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Daniel Almeida, Alice Ryhl, rust-for-linux

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_ID_WIDE when the compatibility value indicates a v15 GPU, and
populate both the cached GPU_ID fields and the uAPI gpu_id_wide field.

This allows userspace and the driver to identify v15 GPUs correctly.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu.
- Rename the register macro from GPU_WIDE_ID to GPU_ID_WIDE.
- Rename the uAPI field from gpu_wide_id to gpu_id_wide.
- Print the upper 32 bits of GPU_ID_WIDE as the logged 'id' for v15 and
  later GPUs. Keep the existing output for earlier GPUs. Continuation
  fix for previous sashiko comment.
- Link: https://lore.kernel.org/all/20260922205659.3C4F21F000FF@smtp.kernel.org/
v1:
- Decode selected GPU_ID format after checking compatibility value.
- Document GPU_DISCOVER and GPU_CONTROL register offset differences
  between GPUs.
- Fix mask generation in panthor_gpu_discover_regs.h

 .../drm/panthor/panthor_gpu_discover_regs.h   | 19 ++++++
 drivers/gpu/drm/panthor/panthor_hw.c          | 62 ++++++++++++++-----
 include/uapi/drm/panthor_drm.h                | 13 ++++
 3 files changed, 80 insertions(+), 14 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 0000000000000..6896c263f76b6
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
@@ -0,0 +1,19 @@
+/* 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__
+
+#include <linux/bits.h>
+
+#define GPU_ID_WIDE					0x0
+#define   GPU_WIDE_COMPAT				0xF
+#define   GPU_WIDE_ARCH_MAJOR(x)			(((x) & GENMASK_U64(63, 56)) >> 56)
+#define   GPU_WIDE_ARCH_MINOR(x)			(((x) & GENMASK_U64(55, 48)) >> 48)
+#define   GPU_WIDE_ARCH_REV(x)				(((x) & GENMASK_U64(47, 40)) >> 40)
+#define   GPU_WIDE_PROD_MAJOR(x)			(((x) & GENMASK_U64(39, 32)) >> 32)
+#define   GPU_WIDE_VER_MAJOR(x)				(((x) & GENMASK_U64(23, 16)) >> 16)
+#define   GPU_WIDE_VER_MINOR(x)				(((x) & GENMASK_U64(15, 8)) >> 8)
+#define   GPU_WIDE_VER_STATUS(x)			((x) & GENMASK_U64(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 854874a9f322a..30f5dfe500fb0 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -3,11 +3,13 @@
 
 #include <linux/nvmem-consumer.h>
 #include <linux/platform_device.h>
+#include <linux/wordpart.h>
 
 #include <drm/drm_print.h>
 
 #include "panthor_device.h"
 #include "panthor_gpu.h"
+#include "panthor_gpu_discover_regs.h"
 #include "panthor_gpu_regs.h"
 #include "panthor_hw.h"
 #include "panthor_pwr.h"
@@ -238,21 +240,30 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev)
 
 static int panthor_hw_info_init(struct panthor_device *ptdev)
 {
-	u32 major, minor, status;
+	u32 prod_id, major, minor, status;
 	int ret;
 
 	ret = panthor_gpu_info_init(ptdev);
 	if (ret)
 		return ret;
 
+	/*
+	 * The logged 'id' contains four 4-bit architecture and product fields
+	 * in pre-v15 GPUs. In v15+ GPUs, this is now four 8-bit fields.
+	 * Preserve the pre-v15 format for compatibility. Tools decoding these
+	 * fields must handle the wider format for v15+ GPUs.
+	 */
+	if (ptdev->gpu_id.arch_major < 15)
+		prod_id = upper_16_bits(ptdev->gpu_info.gpu_id);
+	else
+		prod_id = upper_32_bits(ptdev->gpu_info.gpu_id_wide);
+
 	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,
-		 major, minor, status);
+	drm_info(&ptdev->base, "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
+		 get_gpu_model_name(ptdev), prod_id, major, minor, status);
 
 	drm_info(&ptdev->base,
 		 "Features: L2:%#x Tiler:%#x Mem:%#x MMU:%#x AS:%#x",
@@ -296,18 +307,41 @@ 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);
+	if (GPU_ARCH_MAJOR(gpu_id32) == GPU_WIDE_COMPAT) {
+		/*
+		 * GPU_DISCOVER register block always starts at offset zero,
+		 * so does the GPU_CONTROL register block on pre-v15 GPUs.
+		 */
+		void __iomem *discover = ptdev->iomem;
+		u64 gpu_id64 = gpu_read64(discover, GPU_ID_WIDE);
+		if (!gpu_id64)
+			return -ENXIO;
+
+		ptdev->gpu_info.gpu_id_wide = 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 a2ff0f4ec6915..09b7f7931e4f4 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -376,6 +376,19 @@ struct drm_panthor_gpu_info {
 
 	/** @gpu_features: Bitmask describing supported GPU-wide features */
 	__u64 gpu_features;
+
+	/**
+	 * @gpu_id_wide: 64-bit GPU ID for v15 and later GPUs. @gpu_id is 0 when
+	 * this is populated.
+	 */
+	__u64 gpu_id_wide;
+#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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 06/28] drm/panthor: Move register base offsets to the HW description
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (4 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 05/28] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 07/28] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu
v1:
- rebased on drm-misc-next
- use a generic panthor_hw_regbank structure for all register offsets.

 drivers/gpu/drm/panthor/panthor_fw.c       |  2 +-
 drivers/gpu/drm/panthor/panthor_fw_regs.h  |  2 --
 drivers/gpu/drm/panthor/panthor_gpu.c      |  4 +--
 drivers/gpu/drm/panthor/panthor_gpu_regs.h |  2 --
 drivers/gpu/drm/panthor/panthor_hw.c       | 23 +++++++++++++++--
 drivers/gpu/drm/panthor/panthor_hw.h       | 29 ++++++++++++++++++++--
 drivers/gpu/drm/panthor/panthor_pwr.c      |  2 +-
 drivers/gpu/drm/panthor/panthor_pwr_regs.h |  2 --
 8 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 13efed3a2ea72..679ea1d233d53 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1500,7 +1500,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->regs.mcu_control.offset;
 	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 b2e59cfc22b00..a3492f7f913f9 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 c5c37f22993d6..ab54119314da0 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -159,7 +159,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->regs.gpu_control.offset;
 	spin_lock_init(&gpu->reqs_lock);
 	init_waitqueue_head(&gpu->reqs_acked);
 	mutex_init(&gpu->cache_flush_lock);
@@ -176,7 +176,7 @@ int panthor_gpu_init(struct panthor_device *ptdev)
 		return irq;
 
 	ret = panthor_irq_request(ptdev, &ptdev->gpu->irq, irq,
-				  ptdev->iomem + GPU_INT_BASE, "gpu",
+				  gpu->iomem + GPU_INT_BASE, "gpu",
 				  panthor_gpu_irq_threaded_handler);
 	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 c7581b6d4c5be..60819c4ace428 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 30f5dfe500fb0..9a7d0a2edb870 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -38,6 +38,14 @@ static struct panthor_hw panthor_hw_arch_v10 = {
 		.power_changed_off = panthor_gpu_power_changed_off,
 		.power_changed_on = panthor_gpu_power_changed_on,
 	},
+	.regs = {
+		.gpu_control = {
+			.offset = 0x0,
+		},
+		.mcu_control = {
+			.offset = 0x700,
+		},
+	},
 };
 
 static struct panthor_hw panthor_hw_arch_v14 = {
@@ -46,6 +54,17 @@ static struct panthor_hw panthor_hw_arch_v14 = {
 		.l2_power_off = panthor_pwr_l2_power_off,
 		.l2_power_on = panthor_pwr_l2_power_on,
 	},
+	.regs = {
+		.gpu_control = {
+			.offset = 0x0,
+		},
+		.mcu_control = {
+			.offset = 0x700,
+		},
+		.pwr_control = {
+			.offset = 0x800,
+		},
+	},
 };
 
 static struct panthor_hw_entry panthor_hw_match[] = {
@@ -197,7 +216,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->regs.gpu_control.offset;
 
 	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 +242,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->regs.pwr_control.offset;
 
 		/* 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 c4a490bc39de6..0a82152d772ad 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -27,14 +27,39 @@ struct panthor_hw_ops {
 	void (*power_changed_off)(struct panthor_device *ptdev);
 };
 
+/** struct panthor_hw_regbank - Register offset and instance stride information */
+struct panthor_hw_regbank {
+	/** @offset: Register offset relative to &struct panthor_device.iomem */
+	u32 offset;
+
+	/**
+	 * @stride: Register offset between instances, or 0 for non-instanced
+	 * registers
+	 */
+	u32 stride;
+};
+
+/** struct panthor_hw_regs_layout - Hardware register bank layout */
+struct panthor_hw_regs_layout {
+	/** @gpu_control: GPU_CONTROL register bank layout */
+	struct panthor_hw_regbank gpu_control;
+
+	/** @pwr_control: PWR_CONTROL register bank layout */
+	struct panthor_hw_regbank pwr_control;
+
+	/** @mcu_control: MCU_CONTROL register bank layout */
+	struct panthor_hw_regbank mcu_control;
+};
+
 /**
  * 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;
+
+	/** @regs: Hardware register bank layout */
+	struct panthor_hw_regs_layout regs;
 };
 
 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 b1000001325ea..1cfd4b658c81b 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -502,7 +502,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->regs.pwr_control.offset;
 	spin_lock_init(&pwr->reqs_lock);
 	init_waitqueue_head(&pwr->reqs_acked);
 	ptdev->pwr = pwr;
diff --git a/drivers/gpu/drm/panthor/panthor_pwr_regs.h b/drivers/gpu/drm/panthor/panthor_pwr_regs.h
index 80d8e1e522298..e1f3153285bd7 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_pwr_regs.h
@@ -4,8 +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)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 07/28] drm/panthor: Derive MMU AS register addresses from base and stride
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (5 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 06/28] drm/panthor: Move register base offsets to the HW description Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema Karunika Choo
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Picked up R-b from Liviu
- Avoid enabling all AS IRQs on init and replace with explicit IRQ clear
  to preserve init behaviour. AS IRQs are enabled as part of
  panthor_mmu_as_enable(). This also prevents accessing NULL iomem
  pointers for initialized AS during init per sashiko's comment.
- Link: https://lore.kernel.org/all/20260922210016.0C0961F000FF@smtp.kernel.org/
v1:
- align with updated panthor_hw_regbank structure.
- moved AS iomem tracking into panthor_as_slot and introduced as_iomem()
  to retrieve it.

 drivers/gpu/drm/panthor/panthor_hw.c       |  8 ++++
 drivers/gpu/drm/panthor/panthor_hw.h       |  3 ++
 drivers/gpu/drm/panthor/panthor_mmu.c      | 47 ++++++++++++++--------
 drivers/gpu/drm/panthor/panthor_mmu_regs.h | 23 +++++------
 4 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 9a7d0a2edb870..3a344fa292168 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -45,6 +45,10 @@ static struct panthor_hw panthor_hw_arch_v10 = {
 		.mcu_control = {
 			.offset = 0x700,
 		},
+		.mmu_as_control = {
+			.offset = 0x2400,
+			.stride = 0x40,
+		},
 	},
 };
 
@@ -64,6 +68,10 @@ static struct panthor_hw panthor_hw_arch_v14 = {
 		.pwr_control = {
 			.offset = 0x800,
 		},
+		.mmu_as_control = {
+			.offset = 0x2400,
+			.stride = 0x40,
+		},
 	},
 };
 
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 0a82152d772ad..4cb603a3acef6 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -49,6 +49,9 @@ struct panthor_hw_regs_layout {
 
 	/** @mcu_control: MCU_CONTROL register bank layout */
 	struct panthor_hw_regbank mcu_control;
+
+	/** @mmu_as_control: MMU_AS_CONTROL register bank layout */
+	struct panthor_hw_regbank mmu_as_control;
 };
 
 /**
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index d1c29eec46f21..718160c0779ae 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -35,6 +35,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"
@@ -49,15 +50,15 @@ struct panthor_vm;
 struct panthor_as_slot {
 	/** @vm: VM bound to this slot. NULL is no VM is bound. */
 	struct panthor_vm *vm;
+
+	/** @iomem: CPU mapping for MMU_AS_CONTROL iomem region */
+	void __iomem *iomem;
 };
 
 /**
  * struct panthor_mmu - MMU related data
  */
 struct panthor_mmu {
-	/** @iomem: CPU mapping of MMU_AS_CONTROL iomem region */
-	void __iomem *iomem;
-
 	/** @irq: The MMU irq. */
 	struct panthor_irq irq;
 
@@ -533,6 +534,11 @@ static void free_pt(void *cookie, void *data, size_t size)
 	kmem_cache_free(pt_cache, data);
 }
 
+static void __iomem *as_iomem(struct panthor_mmu *mmu, u32 as_nr)
+{
+	return mmu->as.slots[as_nr].iomem;
+}
+
 static int wait_ready(struct panthor_device *ptdev, u32 as_nr)
 {
 	struct panthor_mmu *mmu = ptdev->mmu;
@@ -542,7 +548,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(as_iomem(mmu, as_nr), AS_STATUS, val,
 						   !(val & AS_STATUS_AS_ACTIVE), 10, 100000);
 
 	if (ret) {
@@ -560,7 +566,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(as_iomem(ptdev->mmu, as_nr), AS_COMMAND, cmd);
 		status = wait_ready(ptdev, as_nr);
 	}
 
@@ -603,9 +609,9 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
 
 	panthor_irq_enable_events(&ptdev->mmu->irq, MMU_INT_AS_FAULT(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(as_iomem(mmu, as_nr), AS_TRANSTAB, transtab);
+	gpu_write64(as_iomem(mmu, as_nr), AS_MEMATTR, memattr);
+	gpu_write64(as_iomem(mmu, as_nr), AS_TRANSCFG, transcfg);
 
 	return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
 }
@@ -627,9 +633,9 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr)
 			return ret;
 	}
 
-	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(as_iomem(mmu, as_nr), AS_TRANSTAB, 0);
+	gpu_write64(as_iomem(mmu, as_nr), AS_MEMATTR, 0);
+	gpu_write64(as_iomem(mmu, as_nr), AS_TRANSCFG, AS_TRANSCFG_ADRMODE_UNMAPPED);
 
 	ret = as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
 	if (ret)
@@ -1799,7 +1805,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(as_iomem(ptdev->mmu, vm->as.id), AS_LOCKADDR,
 			    pack_region_range(ptdev, &start, &size));
 
 		/* If the lock succeeded, update the locked_region info. */
@@ -1861,8 +1867,8 @@ static void panthor_mmu_irq_handler(struct panthor_irq *pirq, 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(as_iomem(mmu, as), AS_FAULTSTATUS);
+		addr = gpu_read64(as_iomem(mmu, as), AS_FAULTADDRESS);
 
 		/* decode the fault status */
 		exception_type = fault_status & 0xFF;
@@ -3429,8 +3435,11 @@ static void panthor_mmu_info_init(struct panthor_device *ptdev)
 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_regbank *regbank = &ptdev->hw->regs.mmu_as_control;
 	struct panthor_mmu *mmu;
 	int ret, irq;
+	u32 as_id;
 
 	panthor_mmu_info_init(ptdev);
 
@@ -3449,7 +3458,13 @@ 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 = regbank->offset + (regbank->stride * as_id);
+
+		guard(mutex)(&mmu->as.slots_lock);
+		mmu->as.slots[as_id].iomem = ptdev->iomem + offset;
+	}
+
 	ptdev->mmu = mmu;
 
 	irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "mmu");
@@ -3479,7 +3494,7 @@ int panthor_mmu_init(struct panthor_device *ptdev)
 	if (ret)
 		return ret;
 
-	panthor_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0));
+	gpu_write(mmu->irq.iomem, INT_CLEAR, panthor_mmu_fault_mask(ptdev, ~0));
 	panthor_irq_resume(&mmu->irq);
 	return 0;
 }
diff --git a/drivers/gpu/drm/panthor/panthor_mmu_regs.h b/drivers/gpu/drm/panthor/panthor_mmu_regs.h
index 1ec7f5ef2b196..82661283b46ab 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu_regs.h
@@ -11,13 +11,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) | \
@@ -29,8 +24,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
@@ -38,16 +33,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)
@@ -65,6 +60,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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (6 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 07/28] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-11  8:33   ` Krzysztof Kozlowski
  2026-11-10 15:38 ` [PATCH v2 09/28] drm/panthor: Add Mali v15 hardware support Karunika Choo
                   ` (19 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	devicetree

Add a separate device-tree schema for Mali 5th-Gen AM GPUs.

AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
cooling or GPU supply properties.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
  separate arm,mali-gen5-am.yaml schema.
- Move the AM GPU example to the new schema and add the interrupt
  binding headers.
- Add the new schema to the Panthor MAINTAINERS entry.

 .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml

diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
new file mode 100644
index 0000000000000..6349888e1ae2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Mali 5th Gen AM GPU
+
+maintainers:
+  - Liviu Dudau <liviu.dudau@arm.com>
+  - Boris Brezillon <boris.brezillon@collabora.com>
+
+properties:
+  $nodename:
+    pattern: '^gpu@[a-f0-9]+$'
+
+  compatible:
+    const: arm,mali-gen5-am
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: Job interrupt
+      - description: MMU interrupt
+      - description: GPU interrupt
+
+  interrupt-names:
+    items:
+      - const: job
+      - const: mmu
+      - const: gpu
+
+  clocks:
+    minItems: 1
+    maxItems: 3
+
+  clock-names:
+    minItems: 1
+    items:
+      - const: core
+      - enum:
+          - coregroup
+          - stacks
+      - const: stacks
+
+  nvmem-cells:
+    items:
+      - description: bitmask of functional shader cores
+
+  nvmem-cell-names:
+    items:
+      - const: shader-present
+
+  power-domains:
+    minItems: 1
+    maxItems: 5
+
+  power-domain-names:
+    minItems: 1
+    maxItems: 5
+
+  dma-coherent: true
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    gpu@100000 {
+        compatible = "arm,mali-gen5-am";
+        reg = <0x100000 0x480000>;
+        interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH 0>,
+                     <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH 0>,
+                     <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH 0>;
+        interrupt-names = "job", "mmu", "gpu";
+        clocks = <&gpu_clock 0>, <&gpu_clock 1>, <&gpu_clock 2>;
+        clock-names = "core", "coregroup", "stacks";
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 1c3aec082b0ab..bdf3cc1044c3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2400,6 +2400,7 @@ M:	Liviu Dudau <liviu.dudau@arm.com>
 L:	dri-devel@lists.freedesktop.org
 S:	Supported
 T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:	Documentation/devicetree/bindings/gpu/arm,mali-gen5-am*.yaml
 F:	Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
 F:	drivers/gpu/drm/ci/xfails/panthor*
 F:	drivers/gpu/drm/panthor/
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 09/28] drm/panthor: Add Mali v15 hardware support
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (7 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Daniel Almeida, Alice Ryhl, rust-for-linux

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 and bump drm minor to 10.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- check as_count is non-zero before generating as_present mask per
  sashiko's comment.
- Link: https://lore.kernel.org/all/20260922205812.C473B1F000FF@smtp.kernel.org/
- clean up l2_features value assignment.
- removed duplicated GPU_FEATURES macro definitions
- Picked up R-b from Liviu
v1:
- make COHERENCY_FEATURES register read use cached gpu_info values.
- Added Mali-G2 name variants and NX aware model identification.
- fix premature reading of l2_features gpu_info field during
  panthor_hw_info_init().
- build as_present using GENMASK.

 drivers/gpu/drm/panthor/panthor_drv.c         |  5 +-
 drivers/gpu/drm/panthor/panthor_fw.c          |  1 +
 drivers/gpu/drm/panthor/panthor_gpu.c         |  3 +-
 .../drm/panthor/panthor_gpu_discover_regs.h   | 23 +++++
 drivers/gpu/drm/panthor/panthor_heap.c        |  3 +-
 drivers/gpu/drm/panthor/panthor_hw.c          | 96 ++++++++++++++++++-
 drivers/gpu/drm/panthor/panthor_hw.h          | 13 +++
 include/uapi/drm/panthor_drm.h                | 10 +-
 8 files changed, 145 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index dc8d0191aef42..e9b71ff5ee53f 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1785,6 +1785,8 @@ static void panthor_debugfs_init(struct drm_minor *minor)
  * - 1.8 - extends DEV_QUERY_TIMESTAMP_INFO with flags
  * - 1.9 - adds DRM_PANTHOR_DEV_QUERY_MMU_INFO query
  *       - adds DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE flag
+ * - 1.10 - adds thread_num_active_granularity, gpu_rev_wide, l2_features_wide,
+ *          and gpu_id_wide to drm_panthor_gpu_info
  */
 static const struct drm_driver panthor_drm_driver = {
 	.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
@@ -1798,7 +1800,7 @@ static const struct drm_driver panthor_drm_driver = {
 	.name = "panthor",
 	.desc = "Panthor DRM driver",
 	.major = 1,
-	.minor = 9,
+	.minor = 10,
 
 	.gem_prime_import_sg_table = panthor_gem_prime_import_sg_table,
 	.gem_prime_import = panthor_gem_prime_import,
@@ -1881,6 +1883,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 679ea1d233d53..1449d9a66302e 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1563,3 +1563,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.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index ab54119314da0..2e1b00e86cdea 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -503,8 +503,7 @@ int panthor_gpu_coherency_init(struct panthor_device *ptdev)
 	/* Check if the ACE-Lite coherency protocol is actually supported by the GPU.
 	 * ACE protocol has never been supported for command stream frontend GPUs.
 	 */
-	if ((gpu_read(ptdev->gpu->iomem, GPU_COHERENCY_FEATURES) &
-		      GPU_COHERENCY_PROT_BIT(ACE_LITE))) {
+	if ((ptdev->gpu_info.coherency_features & GPU_COHERENCY_PROT_BIT(ACE_LITE))) {
 		ptdev->gpu_info.selected_coherency = GPU_COHERENCY_ACE_LITE;
 		return 0;
 	}
diff --git a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
index 6896c263f76b6..05e90950ec6ad 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu_discover_regs.h
@@ -16,4 +16,27 @@
 #define   GPU_WIDE_VER_MINOR(x)				(((x) & GENMASK_U64(15, 8)) >> 8)
 #define   GPU_WIDE_VER_STATUS(x)			((x) & GENMASK_U64(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_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 99311abdf1e91..afb7d68abc874 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 3a344fa292168..e8e899c638128 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0 or MIT
 /* Copyright 2025 ARM Limited. All rights reserved. */
 
+#include <linux/bits.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/platform_device.h>
 #include <linux/wordpart.h>
@@ -75,6 +76,29 @@ 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,
+	},
+	.regs = {
+		.gpu_control = {
+			.offset = 0x3000,
+		},
+		.mcu_control = {
+			.offset = 0x3100,
+		},
+		.pwr_control = {
+			.offset = 0x3800,
+		},
+		.mmu_as_control = {
+			.offset = 0x2800,
+			.stride = 0x80,
+		},
+	},
+};
+
 static struct panthor_hw_entry panthor_hw_match[] = {
 	{
 		.arch_min = 10,
@@ -86,6 +110,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)
@@ -153,8 +182,11 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
 						gpu_id->prod_major);
 	const bool ray_intersection = !!(ptdev->gpu_info.gpu_features &
 					 GPU_FEATURES_RAY_INTERSECTION);
+	const bool neural_present = !!(ptdev->gpu_info.gpu_features &
+				       GPU_FEATURES_NX);
 	const u8 shader_core_count = hweight64(ptdev->gpu_info.shader_present);
 
+
 	switch (product_id) {
 	case GPU_PROD_ID_MAKE(10, 2):
 		return "Mali-G710";
@@ -197,6 +229,18 @@ 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-NX";
+	case GPU_PROD_ID_MAKE(15, 1):
+		if (neural_present)
+			return "Mali-G2-Premium-NX";
+
+		return "Mali-G2-Premium";
+	case GPU_PROD_ID_MAKE(15, 3):
+		if (neural_present)
+			return "Mali-G2-Pro-NX";
+
+		return "Mali-G2-Pro";
 	}
 
 	return "(Unknown Mali GPU)";
@@ -220,7 +264,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;
 
@@ -261,12 +305,57 @@ 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->regs.pwr_control.offset;
+	u64 texture_features;
+	u32 as_count;
+
+	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);
+
+	/* The upper 32-bits of following registers do not contain any information (yet) */
+	ptdev->gpu_info.gpu_features = gpu_read64(ptdev->iomem, 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 */
+	as_count = MMU_FEATURES_AS_COUNT(ptdev->gpu_info.mmu_features);
+	if (as_count)
+		ptdev->gpu_info.as_present = GENMASK(as_count - 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;
 	u32 prod_id, major, minor, status;
 	int ret;
 
@@ -288,13 +377,14 @@ static int panthor_hw_info_init(struct panthor_device *ptdev)
 	major = ptdev->gpu_id.ver_major;
 	minor = ptdev->gpu_id.ver_minor;
 	status = ptdev->gpu_id.ver_status;
+	l2_features = panthor_hw_get_l2_features(ptdev);
 
 	drm_info(&ptdev->base, "%s id 0x%x major 0x%x minor 0x%x status 0x%x",
 		 get_gpu_model_name(ptdev), prod_id, 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 4cb603a3acef6..fe31c1088af5a 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -95,4 +95,17 @@ static inline bool panthor_hw_has_neural(struct panthor_device *ptdev)
 		ptdev->gpu_info.gpu_features & GPU_FEATURES_NX);
 }
 
+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 09b7f7931e4f4..da9f8f82413cf 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -371,8 +371,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;
@@ -389,6 +389,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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (8 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 09/28] drm/panthor: Add Mali v15 hardware support Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:03   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
                   ` (17 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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 dev_pm_opp_get_opp_table()
returns -ENODEV, and guard the devfreq helper paths against a missing
devfreq instance. Also make it so that frequency can be queried even if
the devfreq instance is missing.

This keeps devfreq enabled for existing platforms while allowing v15 AM
systems to probe without a local devfreq setup.

Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Directly call functions instead of storing function pointers.
- Picked up R-b from Liviu
v1:
- Only skip panthor_devfreq init when OPP lookup returns -ENODEV instead
  of direct DT-property check.
- Support frequency query without devfreq.

 drivers/gpu/drm/panthor/panthor_devfreq.c | 29 +++++++++++++++++------
 drivers/gpu/drm/panthor/panthor_hw.h      |  6 +++++
 2 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
index 2249b41ca4afe..d1814b5197221 100644
--- a/drivers/gpu/drm/panthor/panthor_devfreq.c
+++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
@@ -12,6 +12,7 @@
 
 #include "panthor_devfreq.h"
 #include "panthor_device.h"
+#include "panthor_hw.h"
 
 /**
  * struct panthor_devfreq - Device frequency management
@@ -164,6 +165,19 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
 	 */
 	table = dev_pm_opp_get_opp_table(dev);
 	if (IS_ERR_OR_NULL(table)) {
+		/*
+		 * On v15 GPUs, frequency scaling is handled outside of panthor
+		 * by the AM_GOVERNOR block, so the GPU DT node may not provide
+		 * an OPP table. In such cases, do not initialize devfreq for
+		 * panthor.
+		 */
+		if (panthor_hw_is_gen5_am(ptdev->base.dev) &&
+		    PTR_ERR(table) == -ENODEV) {
+			drmm_kfree(&ptdev->base, pdevfreq);
+			ptdev->devfreq = NULL;
+			return 0;
+		}
+
 		ret = devm_pm_opp_set_regulators(dev, reg_names);
 		if (ret && ret != -ENODEV) {
 			if (ret != -EPROBE_DEFER)
@@ -267,7 +281,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 +293,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 +304,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 +320,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,10 +337,11 @@ unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
 	unsigned long freq = 0;
 	int ret;
 
-	if (!pdevfreq->devfreq)
-		return 0;
+	if (!pdevfreq || !pdevfreq->devfreq)
+		ret = panthor_devfreq_get_cur_freq(ptdev->base.dev, &freq);
+	else
+		ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
 
-	ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
 	if (ret)
 		return 0;
 
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index fe31c1088af5a..dee641c698661 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -108,4 +108,10 @@ static inline u64 panthor_hw_get_l2_features(struct panthor_device *ptdev)
 	return ptdev->gpu_info.l2_features;
 }
 
+static inline bool panthor_hw_is_gen5_am(struct device *dev)
+{
+	return device_property_match_string(dev, "compatible",
+					    "arm,mali-gen5-am") >= 0;
+}
+
 #endif /* __PANTHOR_HW_H__ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (9 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-11  8:29   ` Krzysztof Kozlowski
  2026-11-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	devicetree

Mali 5th-Gen AM GPUs expose an AM_SYSTEM block for GPU-wide control and
the AM_ASSIGN block for HW resource assignment outside the GPU
job submission register window.

Document the system node compatible, register and interrupt resources,
GPU clock, and assignment properties for HW resource assignment.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Add an optional AM_ASSIGN register range. Require this range when the
  assignments node is present.
- Describe resource-group assignments for AXI buses, partitions, slices
  and access windows on partition-capable GPUs.
- Allow gpu-arbitration child nodes and the #address-cells, #size-cells
  and ranges properties.
- Remove the panthor-system node-name restriction.
- Remove gpu-supply from the schema and example.
- Add an example with assignments for partition-capable GPU with four
  resource groups.
- Move the MAINTAINERS update to the GPU schema patch.

 .../bindings/gpu/arm,mali-gen5-am-system.yaml | 196 ++++++++++++++++++
 1 file changed, 196 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml

diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
new file mode 100644
index 0000000000000..9e0477b8b723a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
@@ -0,0 +1,196 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright 2026 ARM Limited. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am-system.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Mali 5th-Gen GPU System Device
+
+maintainers:
+  - Liviu Dudau <liviu.dudau@arm.com>
+  - Boris Brezillon <boris.brezillon@collabora.com>
+
+properties:
+  compatible:
+    items:
+      - const: arm,mali-gen5-am-system
+
+  reg:
+    minItems: 1
+    maxItems: 2
+
+  reg-names:
+    minItems: 1
+    items:
+      - const: system
+      - const: assign
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  dma-coherent: true
+
+  "#address-cells": true
+
+  "#size-cells": true
+
+  ranges: true
+
+  assignments:
+    type: object
+    additionalProperties: false
+    description:
+      Resource group HW resource assignment. Only valid for partition-capable
+      GPUs.
+
+    properties:
+      "#address-cells":
+        const: 1
+      "#size-cells":
+        const: 0
+
+    required:
+      - "#address-cells"
+      - "#size-cells"
+
+    patternProperties:
+      "^resource-group@[0-3]$":
+        type: object
+        additionalProperties: false
+
+        properties:
+          reg:
+            items:
+              - maximum: 3
+            description: Resource group number.
+
+          arm,axi-bus:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 1
+            default: 0
+            description:
+              Static resource-group to AXI bus assignment. Valid field values
+              are 0 for AXI_A and 1 for AXI_B.
+
+          arm,partition-mask:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 0xf
+            default: 0
+            description:
+              Static partition-control to resource-group assignment. Bit N
+              selects partition control ID N. Mask must be disjoint across
+              resource groups. Unassigned partitions will be assigned to
+              resource group 0 by default.
+
+          arm,slice-mask:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 0xff
+            default: 0
+            description:
+              Static slice to resource-group assignment. Bit N selects slice ID
+              N. Mask must be disjoint across resource groups. Unassigned slices
+              will be assigned to resource group 0 by default.
+
+          arm,access-window-mask:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 0xffff
+            default: 0
+            description:
+              Static access-window to resource-group assignment.  Bit N selects
+              access window ID N. Masks must be disjoint across resource groups.
+              Unassigned access-windows will be assigned to resource group 0 by
+              default.
+
+        required:
+          - reg
+
+patternProperties:
+  '^gpu-arbitration@[a-f0-9]+$':
+    $ref: arm,mali-gen5-am-arbitration.yaml#
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - clocks
+
+additionalProperties: false
+
+allOf:
+  - if:
+      required:
+        - assignments
+    then:
+      properties:
+        reg:
+          minItems: 2
+        reg-names:
+          minItems: 2
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    gpu-system@68000000 {
+      compatible = "arm,mali-gen5-am-system";
+      reg = <0x68000000 0x10000>;
+      reg-names = "system";
+      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&mali_gpu_clk>;
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    gpu-system@68000000 {
+      compatible = "arm,mali-gen5-am-system";
+      reg = <0x68000000 0x10000>, <0x68010000 0x10000>;
+      reg-names = "system", "assign";
+      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&mali_gpu_clk>;
+
+      assignments {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        resource-group@0 {
+          reg = <0>;
+          arm,axi-bus = <0>;
+          arm,partition-mask = <0x1>;
+          arm,slice-mask = <0x3>;
+          arm,access-window-mask = <0xf>;
+        };
+
+        resource-group@1 {
+          reg = <1>;
+          arm,axi-bus = <0>;
+          arm,partition-mask = <0x2>;
+          arm,slice-mask = <0xc>;
+          arm,access-window-mask = <0xf0>;
+        };
+
+        resource-group@2 {
+          reg = <2>;
+          arm,axi-bus = <1>;
+          arm,partition-mask = <0x4>;
+          arm,slice-mask = <0x30>;
+          arm,access-window-mask = <0xf00>;
+        };
+
+        resource-group@3 {
+          reg = <3>;
+          arm,axi-bus = <1>;
+          arm,partition-mask = <0x8>;
+          arm,slice-mask = <0xc0>;
+          arm,access-window-mask = <0xf000>;
+        };
+      };
+    };
+
+...
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (10 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:57   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 13/28] dt-bindings: gpu: panthor: Document panthor-arbitration bindings Karunika Choo
                   ` (15 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Liam Girdwood, Mark Brown

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.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Fix driver suspension during init unwind and termination paths.
- Handle cases where CONFIG_PM=n
- Fixes following issues raised by sashiko:
  - Add rollback handling for failed regulator suspend.
  - Use runtime pm ops macro
  - Add module device table
- Link: https://lore.kernel.org/all/20260922205909.5648C1F000FF@smtp.kernel.org/
- Register size comments are unsubstantiated.
v1:
- Renames panthor_system.c to panthor_system_drv.c to facilitate adding
  another source file for v15-AE support.
- Explicitly obtain named "system" IOMEM resource.
- Explicitly use "gpu-supply" regulator instead of inferring it from the
  "clock-names" DT property.
- Populate/depopulate child devices.
- Clear stale RESET_COMPLETED interrupts before reset.

 drivers/gpu/drm/panthor/Kconfig               |  10 +
 drivers/gpu/drm/panthor/Makefile              |   1 +
 drivers/gpu/drm/panthor/system/Makefile       |  13 +
 .../drm/panthor/system/panthor_system_drv.c   | 344 ++++++++++++++++++
 4 files changed, 368 insertions(+)
 create mode 100644 drivers/gpu/drm/panthor/system/Makefile
 create mode 100644 drivers/gpu/drm/panthor/system/panthor_system_drv.c

diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
index 911e7f4810c39..e17c93035e8b7 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -20,3 +20,13 @@ 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
+	  Enable support for the AM_SYSTEM block found on Arm Mali 5th Gen
+	  AM GPUs. This block provides GPU-wide control outside the main GPU
+	  register window, including reset handling and management of shared
+	  clock and regulator resources.
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index dd15d52a88bac..488a5e03efbc4 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 0000000000000..018b569655b67
--- /dev/null
+++ b/drivers/gpu/drm/panthor/system/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+# Copyright 2026 ARM Limited. All rights reserved.
+
+obj-$(CONFIG_DRM_PANTHOR_SYSTEM) += panthor_system.o
+
+panthor_system-y := \
+	panthor_system_drv.o
+
+INCLUDES = \
+	-I$(src)/..
+
+ccflags-y += $(INCLUDES)
+subdir-ccflags-y += $(INCLUDES)
diff --git a/drivers/gpu/drm/panthor/system/panthor_system_drv.c b/drivers/gpu/drm/panthor/system/panthor_system_drv.c
new file mode 100644
index 0000000000000..e70dba4058f38
--- /dev/null
+++ b/drivers/gpu/drm/panthor/system/panthor_system_drv.c
@@ -0,0 +1,344 @@
+// 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_SYS_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 regulator *regulator = NULL;
+	int ret;
+
+	regulator = devm_regulator_get_optional(sdev->dev, "gpu");
+	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 gpu-supply");
+	}
+
+	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;
+
+	/* Clear stale RESET_COMPLETED IRQs before issuing reset */
+	gpu_write64(sdev->iomem, AM_SYS_IRQ_CLEAR, AM_SYS_IRQ_RESET_COMPLETED);
+
+	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);
+	int ret, clk_ret;
+
+	panthor_system_clk_suspend(sdev);
+
+	ret = panthor_system_regulator_suspend(sdev);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	clk_ret = panthor_system_clk_resume(sdev);
+	if (clk_ret)
+		return clk_ret;
+
+	return ret;
+}
+
+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 void panthor_system_fini(struct device *dev)
+{
+	int ret;
+
+	pm_runtime_disable(dev);
+
+	if (!pm_runtime_status_suspended(dev)) {
+		ret = panthor_system_suspend(dev);
+		if (ret) {
+			dev_warn(dev, "Failed to power off during teardown: %d\n", ret);
+			return;
+		}
+	}
+
+	pm_runtime_set_suspended(dev);
+}
+
+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_byname(pdev, "system");
+	if (IS_ERR(iomem)) {
+		return dev_err_probe(dev, PTR_ERR(iomem), "Failed to ioremap \"system\"");
+	}
+
+	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);
+
+	pm_runtime_enable(dev);
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret) {
+		dev_err_probe(dev, ret, "Failed to resume device");
+		goto err_rpm_disable;
+	}
+
+	if (!IS_ENABLED(CONFIG_PM)) {
+		ret = panthor_system_resume(dev);
+		if (ret) {
+			dev_err_probe(dev, ret, "Failed to resume device");
+			goto err_rpm_disable;
+		}
+	}
+
+	ret = panthor_system_reset(sdev);
+	if (ret) {
+		dev_err(dev, "Failed to reset GPU");
+		goto err_rpm_put;
+	}
+
+	/* Probe any child devices */
+	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+	if (ret)
+		goto err_depopulate;
+
+	pm_runtime_put_sync_suspend(dev);
+	return 0;
+
+err_depopulate:
+	of_platform_depopulate(dev);
+err_rpm_put:
+	panthor_system_fini(dev);
+	pm_runtime_put_noidle(dev);
+	return ret;
+
+err_rpm_disable:
+	pm_runtime_disable(dev);
+	return ret;
+}
+
+static void panthor_system_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret < 0)
+		dev_warn(dev, "failed to resume during remove: %d", ret);
+
+	of_platform_depopulate(dev);
+	panthor_system_fini(dev);
+
+	if (ret >= 0)
+		pm_runtime_put_noidle(dev);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(panthor_system_pm_ops,
+				 panthor_system_suspend,
+				 panthor_system_resume,
+				 NULL);
+
+static const struct of_device_id panthor_system_dt_match[] = {
+	{ .compatible = "arm,mali-gen5-am-system" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, panthor_system_dt_match);
+
+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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 13/28] dt-bindings: gpu: panthor: Document panthor-arbitration bindings
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (11 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	devicetree

Mali 5th-Gen AM GPUs expose partition-control and resource-group
registers outside the GPU node.

Document named register and interrupt resources in the arbitration node.
The pcN and rgN names identify the hardware blocks.

Document initial access-window and slice assignments in partition child
nodes. The reg property identifies the partition-control index.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Return to using named register and interrupt arrays instead of
  individual entries in child nodes.
- Added register and interrupt entries for partition-capable GPUs
  (rg[0-3], pc[0-3]).
- Introduce partition nodes to define default access-window and slice
  masks.
- Point child Panthor device node to new arm,mali-gen5-am.yaml
- Added partition-capable GPU example entry.

v2:
- Introduce assignment_lock to protect assignments allowing serialized
  lookup without the need for READ_ONCE or WRITE_ONCE.
- Add pending_aw_request_mask to track and replay pending requests
  arriving before AW assignments are initialized.

 .../gpu/arm,mali-gen5-am-arbitration.yaml     | 198 ++++++++++++++++++
 1 file changed, 198 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-arbitration.yaml

diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-arbitration.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-arbitration.yaml
new file mode 100644
index 0000000000000..633dd823bd3d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-arbitration.yaml
@@ -0,0 +1,198 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright 2026 ARM Limited. All rights reserved.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am-arbitration.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Mali 5th-Gen GPU Arbitration Device
+
+maintainers:
+  - Liviu Dudau <liviu.dudau@arm.com>
+  - Boris Brezillon <boris.brezillon@collabora.com>
+
+description:
+  The arbitration device manages GPU partition-control and resource-group
+  blocks. Register and interrupt names pcN and rgN identify their hardware
+  indices. Partition child nodes describe initial resource assignments,
+  with reg identifying the partition-control index. Access-window and slice
+  indices are global to the GPU, each resource may be assigned to at most one
+  partition.
+
+properties:
+  compatible:
+    const: arm,mali-gen5-am-arbitration
+
+  reg:
+    minItems: 2
+    maxItems: 8
+
+  reg-names:
+    minItems: 2
+    maxItems: 8
+    items:
+      enum: [pc0, pc1, pc2, pc3, rg0, rg1, rg2, rg3]
+
+  interrupts:
+    minItems: 2
+    maxItems: 8
+
+  interrupt-names:
+    minItems: 2
+    maxItems: 8
+    items:
+      enum: [pc0, pc1, pc2, pc3, rg0, rg1, rg2, rg3]
+
+  "#address-cells": true
+  "#size-cells": true
+  ranges: true
+
+  partitions:
+    type: object
+    additionalProperties: false
+
+    properties:
+      "#address-cells":
+        const: 1
+      "#size-cells":
+        const: 0
+
+    required:
+      - "#address-cells"
+      - "#size-cells"
+
+    patternProperties:
+      "^partition@[0-3]$":
+        type: object
+        additionalProperties: false
+        description:
+          Initial partition resource assignments.
+
+        properties:
+          reg:
+            items:
+              - maximum: 3
+            description: Partition control number.
+
+          arm,access-window-mask:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 0xffff
+            default: 0
+            description:
+              Initial access window assignment mask. Bit N selects global
+              access window ID N. Masks must be disjoint across partitions.
+              If omitted on partitioning-capable GPUs, no access windows are
+              assigned by default. Non-partitioning GPUs will have all AWs
+              assigned to pc0 if value is 0 or not defined.
+
+          arm,slice-mask:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            maximum: 0xff
+            default: 0
+            description:
+              Initial slice assignment mask. Bit N selects global slice ID N.
+              Masks must be disjoint across partitions. If omitted, no
+              partitions are created by default. This property is only valid for
+              partition-capable GPUs.
+
+        required:
+          - reg
+
+patternProperties:
+  "^gpu@[0-9a-f]+$":
+    $ref: arm,mali-gen5-am.yaml#
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - interrupt-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    gpu-arbitration@50000 {
+      compatible = "arm,mali-gen5-am-arbitration";
+      reg = <0x50000 0x10000>,
+            <0xC0000 0x10000>;
+      reg-names = "pc0", "rg0";
+      interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH 0>,
+                    <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH 0>;
+      interrupt-names = "pc0", "rg0";
+
+      partitions {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        partition@0 {
+            reg = <0x0>;
+            arm,access-window-mask = <0xffff>;
+        };
+      };
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    gpu-arbitration@40000 {
+      compatible = "arm,mali-gen5-am-arbitration";
+      reg = <0x40000 0x10000>,
+            <0x50000 0x10000>,
+            <0xC0000 0x10000>;
+      reg-names = "pc0", "pc1", "rg0";
+      interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH 0>,
+                    <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH 0>,
+                    <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH 0>;
+      interrupt-names = "pc0", "pc1", "rg0";
+
+      partitions {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        partition@0 {
+            reg = <0x0>;
+            arm,access-window-mask = <0xf>;
+            arm,slice-mask = <0x3>;
+        };
+        partition@1 {
+            reg = <0x1>;
+            arm,access-window-mask = <0xf0>;
+            arm,slice-mask = <0xc>;
+        };
+      };
+    };
+
+    gpu-arbitration@60000 {
+      compatible = "arm,mali-gen5-am-arbitration";
+      reg = <0x60000 0x10000>,
+            <0x70000 0x10000>,
+            <0xD0000 0x10000>;
+      reg-names = "pc2", "pc3", "rg1";
+      interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH 0>,
+                    <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH 0>,
+                    <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH 0>;
+      interrupt-names = "pc2", "pc3", "rg1";
+
+      partitions {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        partition@2 {
+            reg = <0x2>;
+            arm,access-window-mask = <0xf00>;
+            arm,slice-mask = <0x30>;
+        };
+        partition@3 {
+            reg = <0x3>;
+            arm,access-window-mask = <0xf000>;
+            arm,slice-mask = <0xc0>;
+        };
+      };
+    };
+
+...
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (12 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 13/28] dt-bindings: gpu: panthor: Document panthor-arbitration bindings Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:10   ` sashiko-bot
  2026-11-11  5:43   ` kernel test robot
  2026-11-10 15:38 ` [PATCH v2 15/28] drm/panthor: Add AM message helpers Karunika Choo
                   ` (13 subsequent siblings)
  27 siblings, 2 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Mali v15 AM GPUs expose AM_PARTITION_CONTROL blocks to manage partition
access windows.

Add a Panthor arbitration platform driver and connect it to Kconfig and
the build system.

Get partition-control register ranges and interrupts from named
resources of the arbitration device. Use pcN names to identify
partition-control blocks.

Provide helpers to open, close and yield access windows.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Revert child node discovery changes from v1 for register and
  interrupts.
- Fixes the following issues raised by sashiko:
  - Use DEV_PM_OPS and MODULE_DEVICE_TABLE macros
  - Change %llx to %pa for res.start
  - Simplify and clean up runtime PM handling.
  - Relax read_poll_timeout due to longer timeouts
  - Extend pc->lock to state checks to serialize against state changes
    triggered by partition commands.
  - Remove implicit close during window_open.
  - CLOSE_WINDOW during termination only when runtime PM is available.
  - Add irq_lock to prevent MMIO access as a result of shared IRQ  while
    GPU is powered off.
  - Add check for allocation failure from devm_kasprintf.
- Link: https://lore.kernel.org/all/20260922205802.1AC3E1F000FF@smtp.kernel.org/
v1:
- Updated DT structure and accompanying parsing to handle child nodes
  instead of named platform resources.
- Add child device population/depopulation.
- Remove blocking waits on partition open, close and yield operations.
- Adds YIELD_IDLE helper.

 drivers/gpu/drm/panthor/Kconfig               |  10 +
 drivers/gpu/drm/panthor/Makefile              |   1 +
 drivers/gpu/drm/panthor/arbitration/Makefile  |  13 +
 .../panthor/arbitration/panthor_arbitration.h |  27 ++
 .../arbitration/panthor_arbitration_drv.c     | 142 +++++++
 .../arbitration/panthor_partition_control.c   | 379 ++++++++++++++++++
 .../arbitration/panthor_partition_control.h   |  29 ++
 7 files changed, 601 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 e17c93035e8b7..04b11b933290e 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -30,3 +30,13 @@ config DRM_PANTHOR_SYSTEM
 	  AM GPUs. This block provides GPU-wide control outside the main GPU
 	  register window, including reset handling and management of shared
 	  clock and regulator resources.
+
+config DRM_PANTHOR_ARBITRATION
+	tristate "HW-assisted virtualization for Mali 5th Gen AM GPUs"
+	depends on DRM_PANTHOR
+	depends on OF
+	help
+	  Enable support for the arbitration blocks found on Arm Mali 5th Gen
+	  AM GPUs. These blocks manage partition controls, resource groups and
+	  access-window scheduling, allowing multiple access windows to share
+	  GPU resources under hardware-assisted arbitration.
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 488a5e03efbc4..5d4d0ae64952e 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 0000000000000..b0b6b2e3edf8d
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/Makefile
@@ -0,0 +1,13 @@
+# 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 0000000000000..fc3610aefae29
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_ARBITRATION_H__
+#define __PANTHOR_ARBITRATION_H__
+
+#include <linux/ioport.h>
+#include <linux/fwnode.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 0000000000000..a27d24c3679fa
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -0,0 +1,142 @@
+// 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 <linux/of_address.h>
+#include <linux/of.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);
+
+	pm_runtime_enable(dev);
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto err_disable_pm;
+
+	ret = panthor_partition_control_init(adev);
+	if (ret)
+		goto err_term;
+
+	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+	if (ret)
+		goto err_depopulate;
+
+	pm_runtime_set_autosuspend_delay(dev, PANTHOR_PM_AUTOSUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+
+	pm_runtime_put_autosuspend(dev);
+
+	return 0;
+
+err_depopulate:
+	of_platform_depopulate(dev);
+
+err_term:
+	panthor_partition_control_term(adev);
+	pm_runtime_put_sync_suspend(dev);
+
+err_disable_pm:
+	pm_runtime_disable(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)
+		dev_warn(adev->dev, "failed to resume during remove: %d", ret);
+
+	of_platform_depopulate(adev->dev);
+
+	pm_runtime_disable(adev->dev);
+
+	panthor_partition_control_term(adev);
+
+	if (ret >= 0)
+		pm_runtime_put_noidle(adev->dev);
+
+	pm_runtime_dont_use_autosuspend(adev->dev);
+	pm_runtime_set_suspended(adev->dev);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(panthor_arbitration_pm_ops,
+				 panthor_arbitration_runtime_suspend,
+				 panthor_arbitration_runtime_resume,
+				 NULL);
+
+static const struct of_device_id panthor_arbitration_of_match[] = {
+	{ .compatible = "arm,mali-gen5-am-arbitration" },
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, panthor_arbitration_of_match);
+
+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 0000000000000..330d0bf5fd375
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.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 AW_UNASSIGNED				-1
+
+#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;
+
+	/** @irq_lock: Lock protecting IRQ state. */
+	spinlock_t irq_lock;
+
+	/** @irq_suspended: Flag indicating if IRQ was suspended. */
+	bool irq_suspended;
+
+	/** @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;
+};
+
+static void partition_irq_suspend(struct panthor_partition_control *pc)
+{
+	scoped_guard(spinlock_irqsave, &pc->irq_lock) {
+		pc->irq_suspended = true;
+
+		if (!pm_runtime_status_suspended(pc->dev))
+			gpu_write(pc->iomem, AM_PART_IRQ_MASK, 0);
+	}
+	synchronize_irq(pc->irq);
+}
+
+static void partition_irq_resume(struct panthor_partition_control *pc)
+{
+	guard(spinlock_irqsave)(&pc->irq_lock);
+	pc->irq_suspended = false;
+	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(partition_state_get, partition_state,
+				 partition_state == state,
+				 PART_REG_POLL_SLEEP_US,
+				 PART_STATE_TRANSITION_TIMEOUT_US,
+				 false, pc);
+}
+
+static int yield_now(struct panthor_partition_control *pc)
+{
+	guard(spinlock_irqsave)(&pc->lock);
+
+	if (partition_state_get(pc) == PART_STATE_RESET)
+		return 0;
+
+	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
+		return -EAGAIN;
+
+	gpu_write(pc->iomem, AM_PART_COMMAND,
+		  AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW));
+
+	return 0;
+}
+
+static int yield_idle(struct panthor_partition_control *pc)
+{
+	guard(spinlock_irqsave)(&pc->lock);
+
+	if (partition_state_get(pc) == PART_STATE_RESET)
+		return 0;
+
+	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
+		return -EAGAIN;
+
+	gpu_write(pc->iomem, AM_PART_COMMAND,
+		  AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE));
+
+	return 0;
+}
+
+static int window_close(struct panthor_partition_control *pc)
+{
+	guard(spinlock_irqsave)(&pc->lock);
+
+	if (partition_state_get(pc) == PART_STATE_RESET)
+		return 0;
+
+	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
+		return -EAGAIN;
+
+	pc->closing = true;
+
+	gpu_write(pc->iomem, AM_PART_COMMAND,
+		  AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW));
+
+	return 0;
+}
+
+static int window_open(struct panthor_partition_control *pc, u8 aw_id)
+{
+	if (aw_id >= AM_ARB_MAX_AW_COUNT)
+		return -EINVAL;
+
+	guard(spinlock_irqsave)(&pc->lock);
+
+	if (partition_state_get(pc) == PART_STATE_WINDOW_OPEN) {
+		u8 cur_aw = partition_aw_get(pc);
+
+		if (cur_aw == aw_id)
+			return 0;
+
+		dev_warn(pc->dev,
+			 "%s: OPEN_WINDOW(%u): Already opened for aw=%u, state=%u. Closing...",
+			 pc->name, aw_id, cur_aw, partition_state_get(pc));
+
+		return -EBUSY;
+	}
+
+	gpu_write(pc->iomem, AM_PART_COMMAND,
+		  AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) | AM_PART_SET_WINDOW(aw_id));
+
+	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 = AW_UNASSIGNED;
+
+		/* RESET_DONE from CLOSE_WINDOW */
+		if (pc->closing)
+			pc->closing = false;
+	}
+}
+
+static irqreturn_t partition_irq_raw_handler(int irq, void *data)
+{
+	struct panthor_partition_control *pc = data;
+	u32 status;
+
+	scoped_guard(spinlock_irqsave, &pc->irq_lock) {
+		if (pc->irq_suspended)
+			return IRQ_NONE;
+	}
+
+	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;
+
+		partition_irq_suspend(pc);
+
+		if (!pm_runtime_status_suspended(pc->dev))
+			if (!WARN_ON(window_close(pc) < 0))
+				WARN_ON(partition_state_wait(pc, PART_STATE_RESET));
+	}
+}
+
+static int partition_control_init(struct panthor_arbitration *adev, int i)
+{
+	struct panthor_partition_control *pc;
+	struct device *dev = adev->dev;
+	struct irq_data *irqdata;
+	struct resource *res;
+	void __iomem *iomem;
+	u32 irq_flags = 0;
+	char *name;
+	int irq;
+	int ret;
+
+	if (adev->pc[i])
+		return dev_warn_probe(dev, -ENODEV,
+				      "partition %u already present", i);
+
+	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
+	if (!pc)
+		return -ENOMEM;
+
+	name = devm_kasprintf(dev, GFP_KERNEL, "pc%d", i);
+	if (!name)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(to_platform_device(dev),
+					   IORESOURCE_MEM, name);
+	if (!res)
+		return -ENODEV;
+
+	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;
+
+	spin_lock_init(&pc->irq_lock);
+	pc->irq_suspended = true;
+
+	spin_lock_init(&pc->lock);
+	pc->current_aw = AW_UNASSIGNED;
+
+	irq = platform_get_irq_byname(to_platform_device(dev), name);
+	if (irq < 0)
+		return irq;
+
+	pc->irq = irq;
+
+	irqdata = irq_get_irq_data(irq);
+	if (irqdata)
+		irq_flags = irqd_get_trigger_type(irqdata);
+
+	ret = devm_request_irq(
+		dev, irq, partition_irq_raw_handler, irq_flags | IRQF_SHARED,
+		devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), pc);
+	if (ret)
+		return ret;
+
+	adev->pc[i] = pc;
+
+	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);
+}
+
+int panthor_partition_control_yield_idle(struct panthor_partition_control *pc)
+{
+	return yield_idle(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 0000000000000..9a688d7bbdb1f
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.h
@@ -0,0 +1,29 @@
+/* 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);
+
+int panthor_partition_control_yield_idle(struct panthor_partition_control *pc);
+
+#endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 15/28] drm/panthor: Add AM message helpers
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (13 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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>
---
v2:
- Tighten protocol validation flow by separating the remote version and
  reply_version to allow local validation of protocol support.
v1:
- Raises minimum/current protocol support to v2. (v1 was for G78AE)
- Removes ARB_VM_GPU_STOP.
- Return -ENOSPC instead of -ENOMEM on full outgoing FIFO.

 drivers/gpu/drm/panthor/panthor_am_msg.h | 182 +++++++++++++++++++++++
 1 file changed, 182 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 0000000000000..bc828e254517c
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_am_msg.h
@@ -0,0 +1,182 @@
+/* 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 ARB_VM_PROTOCOL_V1			0x1
+#define ARB_VM_PROTOCOL_V2			0x2
+
+#define AM_MSG_MIN_SUPPORTED_VERSION		ARB_VM_PROTOCOL_V2
+#define AM_MSG_CURRENT_VERSION			ARB_VM_PROTOCOL_V2
+
+#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_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;
+	msg->version = 0;
+}
+
+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 -ENOSPC;
+
+		/*
+		 * 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, bool acked,
+						  u8 *reply_version)
+{
+	guard(spinlock_irqsave)(&msg->lock);
+
+	if (reply_version == NULL)
+		return -EINVAL;
+
+	msg->version = version;
+
+	if (version < AM_MSG_MIN_SUPPORTED_VERSION) {
+		*reply_version = AM_MSG_MIN_SUPPORTED_VERSION;
+		return -EOPNOTSUPP;
+	}
+
+	if (acked && version > AM_MSG_CURRENT_VERSION) {
+		*reply_version = AM_MSG_CURRENT_VERSION;
+		return -EOPNOTSUPP;
+	}
+
+	msg->version = min(version, AM_MSG_CURRENT_VERSION);
+	*reply_version = msg->version;
+
+	return 0;
+}
+
+static inline bool panthor_am_msg_supported(struct panthor_am_msg *msg)
+{
+	guard(spinlock_irqsave)(&msg->lock);
+
+	return msg->version >= AM_MSG_MIN_SUPPORTED_VERSION &&
+	       msg->version <= AM_MSG_CURRENT_VERSION;
+}
+
+#endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (14 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 15/28] drm/panthor: Add AM message helpers Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:07   ` sashiko-bot
  2026-11-11  8:30   ` kernel test robot
  2026-11-10 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
                   ` (11 subsequent siblings)
  27 siblings, 2 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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>
---
v2:
- Revert child node parsing from v1.
- Tighten protocol validation and reject incompatible messages.
- Drain pending messages on suspend.
- Fixes the following issues raised by sashiko:
  - Simplify and clean up rutnime PM handling and termination paths.
  - change message retry work to delayed work.
  - Add irq_lock to prevent MMIO access as a result of shared IRQ while
    GPU is powered off.
  - Fix irq number storage.
  - Add check for allocation failure from devm_kasprintf
- Link: https://lore.kernel.org/all/20260922205638.7B1DE1F000FF@smtp.kernel.org/
v1:
- Updated DT structure and accompanying parsing to handle child nodes
  instead of named platform resources.
- Hold runtime PM reference during message retries.
- Fix suspend resume ordering.

 drivers/gpu/drm/panthor/arbitration/Makefile  |   3 +-
 .../panthor/arbitration/panthor_arbitration.h |   5 +
 .../arbitration/panthor_arbitration_drv.c     |  15 +
 .../arbitration/panthor_resource_group.c      | 373 ++++++++++++++++++
 .../arbitration/panthor_resource_group.h      |  20 +
 5 files changed, 415 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 b0b6b2e3edf8d..2d2684321d978 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 fc3610aefae29..9612bfc6c4417 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -9,8 +9,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
 
 /**
@@ -22,6 +24,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 a27d24c3679fa..0e8ffd3c1e6da 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -12,6 +12,7 @@
 
 #include "panthor_arbitration.h"
 #include "panthor_partition_control.h"
+#include "panthor_resource_group.h"
 
 #define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
 
@@ -20,6 +21,10 @@ static int panthor_arbitration_runtime_suspend(struct device *dev)
 	struct panthor_arbitration *adev = dev_get_drvdata(dev);
 	int ret = 0;
 
+	ret = panthor_resource_group_suspend(adev);
+	if (ret)
+		return ret;
+
 	ret = panthor_partition_control_suspend(adev);
 	if (ret)
 		return ret;
@@ -36,6 +41,10 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
 	if (ret)
 		return ret;
 
+	ret = panthor_resource_group_resume(adev);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -66,6 +75,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_term;
 
+	ret = panthor_resource_group_init(adev);
+	if (ret)
+		goto err_term;
+
 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
 	if (ret)
 		goto err_depopulate;
@@ -81,6 +94,7 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 	of_platform_depopulate(dev);
 
 err_term:
+	panthor_resource_group_term(adev);
 	panthor_partition_control_term(adev);
 	pm_runtime_put_sync_suspend(dev);
 
@@ -106,6 +120,7 @@ static void panthor_arbitration_remove(struct platform_device *pdev)
 	pm_runtime_disable(adev->dev);
 
 	panthor_partition_control_term(adev);
+	panthor_resource_group_term(adev);
 
 	if (ret >= 0)
 		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 0000000000000..e413cfd9e6d25
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/workqueue.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)
+
+#define AM_RG_RETRY_DELAY_US			10
+
+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;
+
+	/** @irq_lock: Lock protecting IRQ state. */
+	spinlock_t irq_lock;
+
+	/** @irq_suspended: Flag indicating if IRQ was suspended. */
+	bool irq_suspended;
+
+	/** @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 delayed_work 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.work);
+	bool retry = false;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(rg->dev);
+	if (ret) {
+		dev_warn(rg->dev, "Failed to resume to retry messages");
+		return;
+	}
+
+	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 unexpectedly empty", aw_id);
+	}
+
+	if (retry)
+		mod_delayed_work(rg->wq, &rg->msg_retry_work,
+				 usecs_to_jiffies(AM_RG_RETRY_DELAY_US));
+
+	pm_runtime_put_autosuspend(rg->dev);
+}
+
+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 == -ENOSPC)
+		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);
+		mod_delayed_work(rg->wq, &rg->msg_retry_work,
+				 usecs_to_jiffies(AM_RG_RETRY_DELAY_US));
+	}
+}
+
+static void rg_send_init_msg(struct panthor_resource_group *rg, u8 aw_id,
+			     u8 ack, u8 version)
+{
+	u64 message = ARB_VM_INIT_MAKE(ack, version);
+
+	rg_send_msg(rg, aw_id, message);
+}
+
+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);
+	u8 reply_version = AM_MSG_CURRENT_VERSION;
+	int ret;
+
+	ret = panthor_am_msg_version_validate(&aw_data->msg, version, acked,
+					      &reply_version);
+	if (ret == -EOPNOTSUPP)
+		dev_warn(rg->dev,
+			 "AW%u protocol version (%u) not supported (min: %u, max: %u)",
+			 aw_id, version, AM_MSG_MIN_SUPPORTED_VERSION,
+			 AM_MSG_CURRENT_VERSION);
+
+	/* AW initiated the handshake, reply with supported version */
+	if (!acked)
+		rg_send_init_msg(rg, aw_id, 1, reply_version);
+}
+
+static void rg_handshake_init(struct panthor_resource_group *rg)
+{
+	for (u8 aw_id = 0; aw_id < AM_ARB_MAX_AW_COUNT; aw_id++)
+		rg_send_init_msg(rg, aw_id, 0, AM_MSG_CURRENT_VERSION);
+}
+
+static void rg_handle_message(struct panthor_resource_group *rg, u8 aw_id,
+			      u64 message)
+{
+	struct rg_aw_data *aw_data = &rg->aw_data[aw_id];
+	u8 msg_id = AM_MSG_ID_GET(message);
+
+	if (!msg_id)
+		return;
+
+	if (msg_id == VM_ARB_INIT) {
+		rg_respond_to_handshake(rg, aw_id, message);
+		return;
+	}
+
+	if (!panthor_am_msg_supported(&aw_data->msg)) {
+		dev_warn_ratelimited(
+			rg->dev, "Unsupported AW%u (message protocol)", aw_id);
+		return;
+	}
+
+	switch (msg_id) {
+	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;
+
+	scoped_guard(spinlock_irqsave, &rg->irq_lock) {
+		if (rg->irq_suspended)
+			return IRQ_NONE;
+	}
+
+	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_drain_pending_messages(struct panthor_resource_group *rg)
+{
+	unsigned long message_mask;
+	u8 aw_id;
+
+	if (pm_runtime_status_suspended(rg->dev))
+		return;
+
+	message_mask = gpu_read(rg->iomem, AM_RG_IRQ_RAWSTAT) & AM_RG_MESSAGE_MASK;
+
+	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, message_mask);
+}
+
+static void rg_irq_suspend(struct panthor_resource_group *rg)
+{
+	scoped_guard(spinlock_irqsave, &rg->irq_lock) {
+		rg->irq_suspended = true;
+
+		if (!pm_runtime_status_suspended(rg->dev))
+			gpu_write(rg->iomem, AM_RG_IRQ_MASK, 0);
+	}
+
+	synchronize_irq(rg->irq);
+}
+
+static void rg_irq_resume(struct panthor_resource_group *rg)
+{
+	guard(spinlock_irqsave)(&rg->irq_lock);
+	rg->irq_suspended = false;
+	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);
+		rg_drain_pending_messages(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_delayed_work_sync(&rg->msg_retry_work);
+		rg_irq_suspend(rg);
+	}
+}
+
+static int resource_group_init(struct panthor_arbitration *adev, int i)
+{
+	struct panthor_resource_group *rg;
+	struct device *dev = adev->dev;
+	struct irq_data *irqdata;
+	struct resource *res;
+	void __iomem *iomem;
+	u32 irq_flags = 0;
+	char *name;
+	int irq;
+	int ret;
+
+	if (adev->rg[i])
+		return dev_warn_probe(dev, -ENODEV,
+				      "resource-group %u already present", i);
+
+	rg = devm_kzalloc(dev, sizeof(*rg), GFP_KERNEL);
+	if (!rg)
+		return -ENOMEM;
+
+	name = devm_kasprintf(dev, GFP_KERNEL, "rg%d", i);
+	if (!name)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(to_platform_device(dev),
+					   IORESOURCE_MEM, name);
+	if (!res)
+		return -ENODEV;
+
+	iomem = devm_ioremap_resource(dev, res);
+	if (IS_ERR(iomem))
+		return dev_err_probe(dev, PTR_ERR(iomem), "%s: Failed to ioremap", name);
+
+	rg->name = name;
+	rg->dev = dev;
+	rg->iomem = iomem;
+
+	spin_lock_init(&rg->irq_lock);
+	rg->irq_suspended = true;
+
+	rg->wq = devm_alloc_ordered_workqueue(dev, "panthor-%s-wq", WQ_FREEZABLE, name);
+	if (!rg->wq)
+		return -ENOMEM;
+
+	INIT_DELAYED_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));
+
+	irq = platform_get_irq_byname(to_platform_device(dev), name);
+	if (irq < 0)
+		return irq;
+
+	rg->irq = irq;
+
+	irqdata = irq_get_irq_data(irq);
+	if (irqdata)
+		irq_flags = irqd_get_trigger_type(irqdata);
+
+	ret = devm_request_irq(
+		dev, irq, rg_irq_raw_handler, irq_flags | IRQF_SHARED,
+		devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), rg);
+	if (ret)
+		return ret;
+
+	adev->rg[i] = rg;
+
+	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 0000000000000..b8633abdd8eb8
--- /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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 17/28] drm/panthor: Add arbitration scheduler
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (15 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:05   ` sashiko-bot
  2026-11-11  0:43   ` kernel test robot
  2026-11-10 15:38 ` [PATCH v2 18/28] drm/panthor: Route arbitration events Karunika Choo
                   ` (10 subsequent siblings)
  27 siblings, 2 replies; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Add a per-partition scheduler that queues access-window GPU requests and
grants GPU access to queued windows in turn.

An uncontended access window can retain access after its timeslice
expires. The scheduler requests a yield when the timeslice has expired
and another window is waiting and force-closes the active window if it
does not yield in time.

Use hrtimers for GPU timeslices and stop/close completion deadlines.
Start stop and close timers after successful command submission.

Retry stop and close commands from workqueue context when submission
returns -EAGAIN. Use scheduler reset work to recover from other
command errors and completion timeouts.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Use sched_id instead of looking it up via the aw_id for sched to AW
  operations.
- Fix stale timer callback from running on a newer lease generation.
- Fix following issues raised by sashiko:
  - Simplify scheduler tear-down path.
  - Fix timeout value truncation.
  - Introduce retry path for commands that fail due to possibility of
    partition commands being rejected due to transient state transition.
  - Add check for allocation failure from devm_kasprintf
  - Rollback failed suspend / resume.
- Link: https://lore.kernel.org/all/20260922210022.394441F00893@smtp.kernel.org/
v1:
- Move grant, stop and close operations from workqueues into IRQ-safe
  paths. Retains work for scheduler recovery.
- Add closing phase and timer to ensure CLOSE_WINDOW completes in due
  time, otherwise enter recovery handling.
- Rename "grant_timeout_us" to "request_timeout" and "yield_timeout_us"
  to "yield_timeout". Both units are now in milliseconds.
- Add ABI documentation

 .../testing/sysfs-driver-panthor-arbitration  |  18 +
 MAINTAINERS                                   |   1 +
 drivers/gpu/drm/panthor/arbitration/Makefile  |   1 +
 .../panthor/arbitration/panthor_arbitration.h |   9 +
 .../arbitration/panthor_arbitration_drv.c     |  31 +
 .../arbitration/panthor_arbitration_sched.c   | 838 ++++++++++++++++++
 .../arbitration/panthor_arbitration_sched.h   |  38 +
 7 files changed, 936 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-panthor-arbitration
 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/Documentation/ABI/testing/sysfs-driver-panthor-arbitration b/Documentation/ABI/testing/sysfs-driver-panthor-arbitration
new file mode 100644
index 0000000000000..fbd21270a2412
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-panthor-arbitration
@@ -0,0 +1,18 @@
+What:		/sys/module/panthor_arbitration/parameters/request_timeout
+Date:		September 2026
+KernelVersion:	7.2.0
+Contact:	Arm Ltd.
+Description:
+		Controls how long, in milliseconds, the arbitration scheduler
+		allows an access window to keep the GPU before asking it to
+		yield when another access window is waiting.
+
+What:		/sys/module/panthor_arbitration/parameters/yield_timeout
+Date:		September 2026
+KernelVersion:	7.2.0
+Contact:	Arm Ltd.
+Description:
+		Controls how long, in milliseconds, the arbitration scheduler
+		waits for an access window to yield the GPU after a stop request.
+		If the timeout expires, the scheduler force-closes the access
+		window.
diff --git a/MAINTAINERS b/MAINTAINERS
index bdf3cc1044c3e..4be924fdf6e29 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2400,6 +2400,7 @@ M:	Liviu Dudau <liviu.dudau@arm.com>
 L:	dri-devel@lists.freedesktop.org
 S:	Supported
 T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:	Documentation/ABI/testing/sysfs-driver-panthor-arbitration
 F:	Documentation/devicetree/bindings/gpu/arm,mali-gen5-am*.yaml
 F:	Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
 F:	drivers/gpu/drm/ci/xfails/panthor*
diff --git a/drivers/gpu/drm/panthor/arbitration/Makefile b/drivers/gpu/drm/panthor/arbitration/Makefile
index 2d2684321d978..22ed68948fee3 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 9612bfc6c4417..8d4c0aab12666 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -6,8 +6,10 @@
 
 #include <linux/ioport.h>
 #include <linux/fwnode.h>
+#include <linux/types.h>
 
 struct device;
+struct panthor_arbitration_sched;
 struct panthor_partition_control;
 struct panthor_resource_group;
 
@@ -27,6 +29,13 @@ struct panthor_arbitration {
 
 	/** @rg: Pointer array to resource group data */
 	struct panthor_resource_group *rg[AM_ARB_MAX_RG_COUNT];
+
+	/** @sched: Pointer to scheduler data. 1 sched per partition */
+	struct panthor_arbitration_sched *sched[AM_ARB_MAX_PC_COUNT];
 };
 
+int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 sched_id, u8 aw_id);
+int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id);
+int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_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 0e8ffd3c1e6da..f26dcdcbc9198 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 
 #include "panthor_arbitration.h"
+#include "panthor_arbitration_sched.h"
 #include "panthor_partition_control.h"
 #include "panthor_resource_group.h"
 
@@ -21,6 +22,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_resource_group_suspend(adev);
 	if (ret)
 		return ret;
@@ -45,6 +50,26 @@ 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;
+}
+
+/* Arbiter to AW events */
+int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 sched_id, u8 aw_id)
+{
+	return 0;
+}
+
+int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id)
+{
+	return 0;
+}
+
+int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id)
+{
 	return 0;
 }
 
@@ -71,6 +96,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_disable_pm;
 
+	ret = panthor_arbitration_sched_init(adev);
+	if (ret)
+		goto err_term;
+
 	ret = panthor_partition_control_init(adev);
 	if (ret)
 		goto err_term;
@@ -94,6 +123,7 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 	of_platform_depopulate(dev);
 
 err_term:
+	panthor_arbitration_sched_term(adev);
 	panthor_resource_group_term(adev);
 	panthor_partition_control_term(adev);
 	pm_runtime_put_sync_suspend(dev);
@@ -119,6 +149,7 @@ static void panthor_arbitration_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(adev->dev);
 
+	panthor_arbitration_sched_term(adev);
 	panthor_partition_control_term(adev);
 	panthor_resource_group_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 0000000000000..23768739e9ef5
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -0,0 +1,838 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/kfifo.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include "panthor_arbitration.h"
+#include "panthor_arbitration_sched.h"
+#include "panthor_resource_group.h"
+
+static u32 request_timeout = 1 * MSEC_PER_SEC;
+static u32 yield_timeout = 1 * MSEC_PER_SEC;
+
+module_param(request_timeout, uint, 0644);
+MODULE_PARM_DESC(request_timeout, "Time the GPU will be granted to the AW in milliseconds.");
+
+module_param(yield_timeout, uint, 0644);
+MODULE_PARM_DESC(yield_timeout, "Time, in milliseconds, within which the AW must yield 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_CLOSING,
+	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;
+};
+
+enum panthor_arbitration_retry_work {
+	PANTHOR_ARB_WORK_STOP = 0,
+	PANTHOR_ARB_WORK_CLOSE,
+	PANTHOR_ARB_WORK_COUNT,
+};
+
+struct panthor_arbitration_sched {
+	/** @dev: Device pointer */
+	struct device *dev;
+
+	/** @id: Scheduler index. Sched N maps to partition control N */
+	u8 id;
+
+	/** @name: Scheduler 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;
+
+	/** @reset_work: Work to reset the scheduler */
+	struct panthor_arbitration_work reset_work;
+
+	/** @retry_work: Work to retry an operation */
+	struct panthor_arbitration_work retry_work;
+
+	/** @pending_retry_work: Retry work that is pending */
+	DECLARE_BITMAP(pending_retry_work, PANTHOR_ARB_WORK_COUNT);
+
+	/** @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;
+
+	/** @close_timer: Timer to indicate close window has lapsed */
+	struct panthor_arbitration_timer close_timer;
+
+	/** @disabled: Scheduler disabled. No further scheduling can happen */
+	u32 disabled;
+
+	/**
+	 * @grant_previously_expired: Grant period previously expired but was
+	 * not asked to stop
+	 */
+	bool grant_previously_expired;
+};
+
+static int arb_sched_on_stopped_locked(struct panthor_arbitration_sched *sched, u8 aw_id);
+
+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 u32 panthor_arbitration_stop_timeout(struct panthor_arbitration_sched *sched)
+{
+	u64 timeout = (u64)READ_ONCE(request_timeout) + READ_ONCE(yield_timeout);
+
+	if (timeout > (u64)INT_MAX) {
+		dev_warn_once(sched->dev, "request/yield timeout too large.");
+		return INT_MAX;
+	}
+
+	return timeout;
+}
+
+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;
+}
+
+/* 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,
+				u32 timeout_ms)
+{
+	if (!wait_event_timeout(
+		    sched->waitqueue, arb_sched_phase_reached(sched, phase),
+		    msecs_to_jiffies(timeout_ms))) {
+
+		if (!arb_sched_phase_reached(sched, phase))
+			return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+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_queue_retry_work(struct panthor_arbitration_sched *sched,
+				       enum panthor_arbitration_retry_work work)
+{
+	lockdep_assert_held(&sched->lock);
+
+	if (!test_and_set_bit(work, sched->pending_retry_work))
+		arb_sched_queue_work(sched, &sched->retry_work);
+}
+
+static int __arb_sched_stop_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = panthor_arbitration_on_stop(to_adev(sched), sched->id);
+	if (ret == -EAGAIN) {
+		arb_sched_queue_retry_work(sched, PANTHOR_ARB_WORK_STOP);
+	} else if (ret) {
+		dev_err(sched->dev, "%s: Failed to send yield to AW%u. %d",
+			sched->name, sched->active_aw_id, ret);
+
+		arb_sched_queue_work(sched, &sched->reset_work);
+	}
+
+	return ret;
+}
+
+static void arb_sched_stop_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = __arb_sched_stop_locked(sched);
+	if (!ret) {
+		if (sched->phase != ARB_SCHED_PHASE_STOPPING) {
+			dev_warn(sched->dev, "%s: Stop ACKED when not yielding",
+				 sched->name);
+			return;
+		}
+
+		sched->stop_timer.epoch = sched->epoch;
+		hrtimer_start(&sched->stop_timer.timer,
+			      ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
+	}
+}
+
+static void arb_sched_grant_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = panthor_arbitration_on_grant(to_adev(sched), sched->id, sched->active_aw_id);
+	if (!ret) {
+		if (sched->phase != ARB_SCHED_PHASE_GRANTING) {
+			dev_warn(sched->dev, "%s: Grant ACKED when not granting",
+				 sched->name);
+			return;
+		}
+
+		if (sched->disabled) {
+			sched->phase = ARB_SCHED_PHASE_STOPPING;
+			arb_sched_stop_locked(sched);
+		} else {
+			sched->phase = ARB_SCHED_PHASE_GRANTED;
+
+			sched->grant_timer.epoch = sched->epoch;
+			hrtimer_start(&sched->grant_timer.timer,
+				      ms_to_ktime(request_timeout),
+				      HRTIMER_MODE_REL);
+		}
+
+		wake_up_all(&sched->waitqueue);
+	} else if (ret < 0) {
+		dev_err(sched->dev, "%s: Failed to grant access to AW%u. %d",
+			sched->name, sched->active_aw_id, ret);
+
+		arb_sched_queue_work(sched, &sched->reset_work);
+	}
+}
+
+static int __arb_sched_close_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = panthor_arbitration_on_close(to_adev(sched), sched->id);
+	if (ret == -EAGAIN) {
+		arb_sched_queue_retry_work(sched, PANTHOR_ARB_WORK_CLOSE);
+	} else if (ret < 0) {
+		dev_err(sched->dev, "%s: Failed to close AW%u. %d", sched->name,
+			sched->active_aw_id, ret);
+
+		arb_sched_queue_work(sched, &sched->reset_work);
+	}
+
+	return ret;
+}
+
+static void arb_sched_close_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = __arb_sched_close_locked(sched);
+	if (!ret) {
+		sched->close_timer.epoch = sched->epoch;
+		hrtimer_start(&sched->close_timer.timer,
+			      ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
+	}
+}
+
+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_stop_locked(sched);
+
+	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)
+		return;
+
+	if (sched->active_aw_id < 0)
+		return;
+
+	sched->phase = ARB_SCHED_PHASE_LOST;
+
+	arb_sched_close_locked(sched);
+
+	wake_up_all(&sched->waitqueue);
+}
+
+static int arb_sched_force_close(struct panthor_arbitration_sched *sched)
+{
+	scoped_guard(spinlock_irqsave, &sched->lock)
+		arb_sched_force_close_locked(sched);
+
+	return arb_sched_wait_phase(sched, ARB_SCHED_PHASE_IDLE,
+				    panthor_arbitration_stop_timeout(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_grant_locked(sched);
+
+	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_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, reset_work);
+
+	guard(spinlock_irqsave)(&sched->lock);
+	if (sched->epoch != w->epoch)
+		return;
+
+	if (sched->active_aw_id != w->aw_id)
+		return;
+
+	dev_warn(sched->dev, "%s: scheduler reset. active_aw=%u",
+			sched->name, sched->active_aw_id);
+
+	arb_sched_force_close_locked(sched);
+}
+
+static void arb_sched_retry_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, retry_work);
+	DECLARE_BITMAP(work_tmp, PANTHOR_ARB_WORK_COUNT);
+
+	guard(spinlock_irqsave)(&sched->lock);
+	if (bitmap_empty(sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT))
+		return;
+
+	bitmap_copy(work_tmp, sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT);
+	bitmap_zero(sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT);
+
+	if (sched->epoch != w->epoch || sched->active_aw_id != w->aw_id)
+		return;
+
+	switch (sched->phase) {
+	case ARB_SCHED_PHASE_STOPPING:
+		if (test_bit(PANTHOR_ARB_WORK_STOP, work_tmp))
+			arb_sched_stop_locked(sched);
+		break;
+	case ARB_SCHED_PHASE_CLOSING:
+	case ARB_SCHED_PHASE_LOST:
+		if (test_bit(PANTHOR_ARB_WORK_CLOSE, work_tmp))
+			arb_sched_close_locked(sched);
+		break;
+	default:
+		break;
+	}
+}
+
+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 (hrtimer_is_queued(timer))
+		return HRTIMER_NORESTART;
+
+	if (sched->epoch != atmr->epoch ||
+	    sched->phase != ARB_SCHED_PHASE_STOPPING)
+		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 (hrtimer_is_queued(timer))
+		return HRTIMER_NORESTART;
+
+	if (sched->epoch != atmr->epoch)
+		return HRTIMER_NORESTART;
+
+	arb_sched_request_stop_locked(sched);
+
+	return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart
+panthor_arbitration_sched_on_close_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, close_timer);
+
+	guard(spinlock_irqsave)(&sched->lock);
+
+	if (hrtimer_is_queued(timer))
+		return HRTIMER_NORESTART;
+
+	if (sched->epoch != atmr->epoch)
+		return HRTIMER_NORESTART;
+
+	if (sched->phase != ARB_SCHED_PHASE_CLOSING &&
+	    sched->phase != ARB_SCHED_PHASE_LOST)
+		return HRTIMER_NORESTART;
+
+	dev_warn(sched->dev,
+		 "%s: AW%d failed to close in time. Resetting scheduler...",
+		 sched->name, sched->active_aw_id);
+
+	arb_sched_queue_work(sched, &sched->reset_work);
+
+	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 */
+	if (!kfifo_put(&sched->queue, aw_id)) {
+		dev_err(sched->dev, "%s: request queue unexpectedly full",
+			sched->name);
+		return -ENOSPC;
+	}
+	set_bit(aw_id, sched->queued_mask);
+
+	/* No active AW, schedule next AW */
+	if (sched->active_aw_id < 0)
+		return arb_sched_next_locked(sched);
+
+	/*
+	 * 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;
+}
+
+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 &&
+	    sched->phase != ARB_SCHED_PHASE_STOPPING)
+		return 0;
+
+	sched->phase = ARB_SCHED_PHASE_CLOSING;
+	arb_sched_close_locked(sched);
+
+	return 0;
+}
+
+static int arb_sched_on_stopped_locked(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+	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;
+	bitmap_zero(sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT);
+
+	wake_up_all(&sched->waitqueue);
+
+	return arb_sched_next_locked(sched);
+}
+
+int panthor_arbitration_sched_on_stopped(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+	guard(spinlock_irqsave)(&sched->lock);
+
+	return arb_sched_on_stopped_locked(sched, aw_id);
+}
+
+static void arbitration_sched_disable_work(struct panthor_arbitration_sched *sched)
+{
+	disable_work_sync(&sched->reset_work.work);
+	disable_work_sync(&sched->retry_work.work);
+}
+
+static void arbitration_sched_cancel(struct panthor_arbitration_sched *sched)
+{
+	panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_PM);
+	arbitration_sched_disable_work(sched);
+	hrtimer_cancel(&sched->grant_timer.timer);
+	hrtimer_cancel(&sched->stop_timer.timer);
+	hrtimer_cancel(&sched->close_timer.timer);
+}
+
+static void arbitration_sched_term(struct panthor_arbitration_sched *sched)
+{
+	panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_TERM);
+
+	if (!pm_runtime_status_suspended(sched->dev))
+		WARN_ON(panthor_arbitration_sched_stop(sched));
+
+	arbitration_sched_cancel(sched);
+}
+
+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);
+	if (!sched->name)
+		return -ENOMEM;
+
+	sched->id = i;
+	sched->dev = dev;
+
+	sched->wq = devm_alloc_ordered_workqueue(dev, "%s-wq", 0, sched->name);
+	if (!sched->wq)
+		return -ENOMEM;
+
+	INIT_WORK(&sched->reset_work.work, arb_sched_reset_work);
+	INIT_WORK(&sched->retry_work.work, arb_sched_retry_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);
+	hrtimer_setup(&sched->close_timer.timer,
+		      panthor_arbitration_sched_on_close_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)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+		struct panthor_arbitration_sched *sched = adev->sched[i];
+
+		if (!sched)
+			continue;
+
+		panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_PM);
+		ret = panthor_arbitration_sched_stop(sched);
+		if (ret)
+			goto err_rollback;
+	}
+
+	return 0;
+
+err_rollback:
+	for (; i >= 0; i--) {
+		struct panthor_arbitration_sched *sched = adev->sched[i];
+
+		if (!sched)
+			continue;
+
+		panthor_arbitration_sched_disable_clear(sched, ARB_SCHED_DISABLE_PM);
+		panthor_arbitration_sched_start(sched);
+	}
+
+	return ret;
+}
+
+int panthor_arbitration_sched_resume(struct panthor_arbitration *adev)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+		struct panthor_arbitration_sched *sched = adev->sched[i];
+
+		if (!sched)
+			continue;
+
+		panthor_arbitration_sched_disable_clear(sched, ARB_SCHED_DISABLE_PM);
+		ret = panthor_arbitration_sched_start(sched);
+		if (ret)
+			goto err_rollback;
+	}
+
+	return 0;
+
+err_rollback:
+	for (; i >= 0; i--) {
+		struct panthor_arbitration_sched *sched = adev->sched[i];
+
+		if (!sched)
+			continue;
+
+		panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_PM);
+		panthor_arbitration_sched_stop(sched);
+	}
+
+	return ret;
+}
+
+/**
+ * panthor_arbitration_sched_stop - Stop active aw and pause scheduler
+ *
+ * @sched: Pointer to arbitration 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_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,
+				   panthor_arbitration_stop_timeout(sched));
+	if (ret) {
+		/*
+		 * TODO: handle the timeout here by escalating the error when
+		 * reset escalation is supported.
+		 */
+		ret = arb_sched_force_close(sched);
+	}
+
+	/* Should no longer have any further activity. Cancel timer. */
+	hrtimer_cancel(&sched->grant_timer.timer);
+	hrtimer_cancel(&sched->stop_timer.timer);
+	hrtimer_cancel(&sched->close_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)
+{
+	return arb_sched_next(sched);
+}
+
+void panthor_arbitration_sched_disable_set(struct panthor_arbitration_sched *sched,
+					   enum panthor_arb_sched_disable_reason reason)
+{
+	guard(spinlock_irqsave)(&sched->lock);
+
+	sched->disabled |= BIT(reason);
+}
+
+void panthor_arbitration_sched_disable_clear(struct panthor_arbitration_sched *sched,
+					     enum panthor_arb_sched_disable_reason reason)
+{
+	guard(spinlock_irqsave)(&sched->lock);
+
+	sched->disabled &= ~BIT(reason);
+}
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 0000000000000..4d0f72838d218
--- /dev/null
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
@@ -0,0 +1,38 @@
+/* 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;
+
+enum panthor_arb_sched_disable_reason {
+	/** @ARB_SCHED_DISABLE_PM: scheduler disabled due to PM suspend */
+	ARB_SCHED_DISABLE_PM = 0,
+
+	/** @ARB_SCHED_DISABLE_TERM: scheduler disabled for termination */
+	ARB_SCHED_DISABLE_TERM,
+};
+
+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);
+
+void panthor_arbitration_sched_disable_set(struct panthor_arbitration_sched *sched,
+					   enum panthor_arb_sched_disable_reason reason);
+void panthor_arbitration_sched_disable_clear(struct panthor_arbitration_sched *sched,
+					     enum panthor_arb_sched_disable_reason reason);
+
+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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 18/28] drm/panthor: Route arbitration events
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (16 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:19   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
                   ` (9 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Connect resource-group message handling, the arbitration scheduler and
partition control.

Forward GPU request and idle messages to the scheduler. When a partition
reset completes after firmware yields GPU access or a CLOSE_WINDOW
command, report the active access window as stopped.

Add callbacks that let the scheduler grant GPU access, request a yield
and close access windows.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Align with sched to AW interface changes.
v1:
- Notify arbitration scheduler when RESET_DONE completes due to
  close_window, as well as due to yield by Panthor FW.

 .../panthor/arbitration/panthor_arbitration.h |  6 ++++
 .../arbitration/panthor_arbitration_drv.c     | 28 +++++++++++++++++--
 .../arbitration/panthor_partition_control.c   |  9 ++++++
 .../arbitration/panthor_resource_group.c      |  4 +--
 4 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 8d4c0aab12666..1350a5982a93b 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -34,6 +34,12 @@ 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 sched_id, u8 aw_id);
 int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id);
 int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id);
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index f26dcdcbc9198..48016d0d88557 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -57,20 +57,42 @@ 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 sched_id, u8 aw_id)
 {
-	return 0;
+	/* 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 sched_id)
 {
-	return 0;
+	/* TODO: AW to PC assignment */
+	return panthor_partition_control_yield_now(adev->pc[0]);
 }
 
 int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id)
 {
-	return 0;
+	/* TODO: AW to PC assignment */
+	return panthor_partition_control_close_window(adev->pc[0]);
 }
 
 static int panthor_arbitration_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
index 330d0bf5fd375..d8fdafbf31864 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -198,13 +198,22 @@ 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 = AW_UNASSIGNED;
 
 		/* RESET_DONE from CLOSE_WINDOW */
 		if (pc->closing)
 			pc->closing = false;
+		if (aw_id >= 0)
+			notify_stopped = true;
 	}
+
+	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 e413cfd9e6d25..26b22461f6d39 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
@@ -163,10 +163,10 @@ static void rg_handle_message(struct panthor_resource_group *rg, u8 aw_id,
 
 	switch (msg_id) {
 	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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 19/28] drm/panthor: Add AW assignment tracking
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (17 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 18/28] drm/panthor: Route arbitration events Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:03   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
                   ` (8 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Track the scheduler assigned to each access window. Initialize
assignments from the access-window masks in partition child DT nodes.

Use the AW mapping to route request, idle and stopped events to the
assigned scheduler. Use the scheduler ID to select the partition for
grant, stop and close commands.

Record requests from unassigned access windows and forward them after
assignment. Keep schedulers disabled while they have no assigned access
windows. Assign all AWs to partition 0 if DT property is not set or is
zero.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Assign AWs to pc0 by default if no arm,access-window-mask is 0 or not
  set.
- Introduce assignment_lock to protect assignments allowing serialized
  lookup without the need for READ_ONCE or WRITE_ONCE.
- Add pending_aw_request_mask to track and replay pending requests
  arriving before AW assignments are initialized.

 .../panthor/arbitration/panthor_arbitration.h |  23 ++
 .../arbitration/panthor_arbitration_drv.c     | 244 +++++++++++++++++-
 .../arbitration/panthor_arbitration_sched.c   |   2 +
 .../arbitration/panthor_arbitration_sched.h   |   3 +
 4 files changed, 260 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 1350a5982a93b..9a6c251d962de 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -4,9 +4,11 @@
 #ifndef __PANTHOR_ARBITRATION_H__
 #define __PANTHOR_ARBITRATION_H__
 
+#include <linux/bits.h>
 #include <linux/ioport.h>
 #include <linux/fwnode.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 
 struct device;
 struct panthor_arbitration_sched;
@@ -16,6 +18,12 @@ 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
+#define AM_ARB_MAX_AW_MASK			GENMASK(AM_ARB_MAX_AW_COUNT - 1, 0)
+
+struct panthor_arbitration_dt_info {
+	/** @default_aw_masks: Default per-partition AW assignments */
+	u32 default_aw_masks[AM_ARB_MAX_PC_COUNT];
+};
 
 /**
  * struct panthor_arbitration - Arbitration device
@@ -32,6 +40,21 @@ struct panthor_arbitration {
 
 	/** @sched: Pointer to scheduler data. 1 sched per partition */
 	struct panthor_arbitration_sched *sched[AM_ARB_MAX_PC_COUNT];
+
+	/** @dt_info: Properties parsed from the device tree */
+	struct panthor_arbitration_dt_info dt_info;
+
+	/** @assignment_lock: Protects assignments */
+	spinlock_t assignment_lock;
+
+	/** @aw_sched_map: Tracks assignment of AW to sched instance */
+	s8 aw_sched_map[AM_ARB_MAX_AW_COUNT];
+
+	/**
+	 * @pending_aw_request_mask: Bitmap of unassigned AWs with pending
+	 * requests
+	 */
+	DECLARE_BITMAP(pending_aw_request_mask, AM_ARB_MAX_AW_COUNT);
 };
 
 /* AW to Arbiter events */
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
index 48016d0d88557..6479a7602868c 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0 or MIT
 /* Copyright 2026 ARM Limited. All rights reserved. */
 
+#include <linux/bits.h>
 #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/property.h>
 #include <linux/types.h>
 #include <linux/of_address.h>
 #include <linux/of.h>
@@ -17,6 +19,28 @@
 
 #define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
 
+#define DT_ASSIGNED_AW				"arm,access-window-mask"
+
+static int to_assigned_sched_idx(struct panthor_arbitration *adev, u8 aw_id)
+{
+	int idx = -1;
+
+	lockdep_assert_held(&adev->assignment_lock);
+
+	if (aw_id >= AM_ARB_MAX_AW_COUNT) {
+		dev_err(adev->dev, "Invalid AW ID (%u)", aw_id);
+		return -EINVAL;
+	}
+
+	idx = adev->aw_sched_map[aw_id];
+	if (idx < 0 || idx >= AM_ARB_MAX_PC_COUNT || !adev->sched[idx]) {
+		dev_warn_ratelimited(adev->dev, "AW%u not assigned to any sched", aw_id);
+		return -ENOENT;
+	}
+
+	return idx;
+};
+
 static int panthor_arbitration_runtime_suspend(struct device *dev)
 {
 	struct panthor_arbitration *adev = dev_get_drvdata(dev);
@@ -57,42 +81,226 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
 	return 0;
 }
 
+static u32 arbitration_get_sched_aw_mask(struct panthor_arbitration *adev,
+					 u8 sched_id)
+{
+	u32 aw_mask = 0;
+
+	lockdep_assert_held(&adev->assignment_lock);
+
+	for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++) {
+		if (adev->aw_sched_map[i] == sched_id)
+			aw_mask |= BIT(i);
+	}
+
+	return aw_mask;
+}
+
+static int arbitration_dt_parse_assigned_aw(struct panthor_arbitration *adev)
+{
+	struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
+	struct device *dev = adev->dev;
+	struct fwnode_handle *partitions __free(fwnode_handle) = NULL;
+	u32 seen_mask = 0;
+	u8 seen_partitions = 0;
+	int ret;
+
+	partitions = device_get_named_child_node(dev, "partitions");
+	if (!partitions)
+		return 0;
+
+	fwnode_for_each_child_node_scoped(partitions, child) {
+		u32 pc_id;
+
+		if (!fwnode_name_eq(child, "partition"))
+			continue;
+
+		ret = fwnode_property_read_u32(child, "reg", &pc_id);
+		if (ret) {
+			dev_warn(dev, "Missing or invalid 'reg'");
+			continue;
+		}
+
+		if (pc_id >= AM_ARB_MAX_PC_COUNT) {
+			dev_warn(dev,
+				 "partition %u >= %u not supported",
+				 pc_id, AM_ARB_MAX_PC_COUNT);
+			continue;
+		}
+
+		if (BIT(pc_id) & seen_partitions) {
+			dev_warn(dev, "partition %u already present",
+				 pc_id);
+			continue;
+		}
+
+		seen_partitions |= BIT(pc_id);
+
+		if (!fwnode_property_present(child, DT_ASSIGNED_AW))
+			continue;
+
+		ret = fwnode_property_read_u32(child, DT_ASSIGNED_AW,
+					       &dt_info->default_aw_masks[pc_id]);
+		if (ret)
+			continue;
+	}
+
+	for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+		u32 aw_mask = dt_info->default_aw_masks[i];
+
+		if (aw_mask & ~GENMASK(AM_ARB_MAX_AW_COUNT - 1, 0))
+			return dev_err_probe(dev, -EINVAL, "%s[%d] entry exceeds max AW count",
+					     DT_ASSIGNED_AW, i);
+
+		if (aw_mask & seen_mask)
+			return dev_err_probe(dev, -EINVAL, "%s entries overlap",
+					     DT_ASSIGNED_AW);
+
+		seen_mask |= aw_mask;
+	}
+
+	return 0;
+}
+
+static int arbitration_dt_parse_properties(struct panthor_arbitration *adev)
+{
+	return arbitration_dt_parse_assigned_aw(adev);
+}
+
+static int arbitration_sched_try_start(struct panthor_arbitration *adev, u8 sched_id)
+{
+	struct panthor_arbitration_sched *sched;
+	u32 aw_mask;
+
+	if (!adev->sched[sched_id])
+		return -EINVAL;
+
+	sched = adev->sched[sched_id];
+
+	scoped_guard(spinlock_irqsave, &adev->assignment_lock)
+		aw_mask = arbitration_get_sched_aw_mask(adev, sched_id);
+
+	if (aw_mask)
+		panthor_arbitration_sched_disable_clear(sched, ARB_SCHED_DISABLE_RESOURCE);
+	else
+		panthor_arbitration_sched_disable_set(sched, ARB_SCHED_DISABLE_RESOURCE);
+
+	return panthor_arbitration_sched_start(sched);
+}
+
+static void arbitration_retry_pending_requests(struct panthor_arbitration *adev, u32 mask)
+{
+	unsigned long aw_mask = mask;
+	u8 aw_id;
+
+	for_each_set_bit(aw_id, &aw_mask, AM_ARB_MAX_AW_COUNT) {
+		int ret;
+
+		if (!test_and_clear_bit(aw_id, adev->pending_aw_request_mask))
+			continue;
+
+		ret = panthor_arbitration_on_request(adev, aw_id);
+		if (ret && ret != -EEXIST)
+			set_bit(aw_id, adev->pending_aw_request_mask);
+	}
+}
+
+static int arbitration_resource_init(struct panthor_arbitration *adev)
+{
+	struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
+
+	/*
+	 * If arm,access-window-mask is not defined or set to 0, assign AWs to
+	 * PC0 by default.
+	 */
+	if (!dt_info->default_aw_masks[0])
+		dt_info->default_aw_masks[0] = AM_ARB_MAX_AW_MASK;
+
+	for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
+		unsigned long pc_aw_mask = dt_info->default_aw_masks[i];
+		u8 aw_id;
+		int ret;
+
+		if (!adev->pc[i] || !adev->sched[i])
+			continue;
+
+		scoped_guard(spinlock_irqsave, &adev->assignment_lock) {
+			for_each_set_bit(aw_id, &pc_aw_mask, AM_ARB_MAX_AW_COUNT)
+				adev->aw_sched_map[aw_id] = i;
+		}
+
+		ret = arbitration_sched_try_start(adev, i);
+		if (ret)
+			return ret;
+	}
+
+	arbitration_retry_pending_requests(adev, AM_ARB_MAX_AW_MASK);
+
+	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 idx;
+
+	guard(spinlock_irqsave)(&adev->assignment_lock);
+
+	idx = to_assigned_sched_idx(adev, aw_id);
+
+	if (idx == -ENOENT) {
+		set_bit(aw_id, adev->pending_aw_request_mask);
+
+		return 0;
+	} else if (idx < 0) {
+		return idx;
+	}
+
+	return panthor_arbitration_sched_on_request(adev->sched[idx], 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 idx;
+
+	guard(spinlock_irqsave)(&adev->assignment_lock);
+
+	idx = to_assigned_sched_idx(adev, aw_id);
+
+	if (idx < 0)
+		return idx;
+
+	return panthor_arbitration_sched_on_idle(adev->sched[idx], 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);
+	int idx;
+
+	guard(spinlock_irqsave)(&adev->assignment_lock);
+
+	idx = to_assigned_sched_idx(adev, aw_id);
+
+	if (idx < 0)
+		return idx;
+
+	return panthor_arbitration_sched_on_stopped(adev->sched[idx], aw_id);
 }
 
 /* Arbiter to AW events */
 int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 sched_id, u8 aw_id)
 {
-	/* TODO: AW to PC assignment */
-	return panthor_partition_control_open_window(adev->pc[0], aw_id);
+	return panthor_partition_control_open_window(adev->pc[sched_id], aw_id);
 }
 
 int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id)
 {
-	/* TODO: AW to PC assignment */
-	return panthor_partition_control_yield_now(adev->pc[0]);
+	return panthor_partition_control_yield_now(adev->pc[sched_id]);
 }
 
 int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id)
 {
-	/* TODO: AW to PC assignment */
-	return panthor_partition_control_close_window(adev->pc[0]);
+	return panthor_partition_control_close_window(adev->pc[sched_id]);
 }
 
 static int panthor_arbitration_probe(struct platform_device *pdev)
@@ -110,8 +318,16 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 
 	adev->dev = dev;
 
+	spin_lock_init(&adev->assignment_lock);
+	for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++)
+		adev->aw_sched_map[i] = -1;
+
 	dev_set_drvdata(dev, adev);
 
+	ret = arbitration_dt_parse_properties(adev);
+	if (ret)
+		return ret;
+
 	pm_runtime_enable(dev);
 
 	ret = pm_runtime_resume_and_get(dev);
@@ -130,6 +346,10 @@ static int panthor_arbitration_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_term;
 
+	ret = arbitration_resource_init(adev);
+	if (ret)
+		goto err_term;
+
 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
 	if (ret)
 		goto err_depopulate;
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
index 23768739e9ef5..95a7685a5b3d6 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -662,6 +662,8 @@ static int arbitration_sched_init(struct panthor_arbitration *adev, int i)
 	INIT_KFIFO(sched->queue);
 	sched->active_aw_id = -1;
 	sched->phase = ARB_SCHED_PHASE_IDLE;
+	/* Scheduler is disabled until its resources are configured */
+	sched->disabled = BIT(ARB_SCHED_DISABLE_RESOURCE);
 
 	hrtimer_setup(&sched->grant_timer.timer,
 		      panthor_arbitration_sched_on_grant_timeout,
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
index 4d0f72838d218..187124ac39a5e 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
@@ -15,6 +15,9 @@ enum panthor_arb_sched_disable_reason {
 
 	/** @ARB_SCHED_DISABLE_TERM: scheduler disabled for termination */
 	ARB_SCHED_DISABLE_TERM,
+
+	/** @ARB_SCHED_DISABLE_RESOURCE: No resource assigned. scheduler disabled. */
+	ARB_SCHED_DISABLE_RESOURCE,
 };
 
 int panthor_arbitration_sched_init(struct panthor_arbitration *adev);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (18 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:16   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle Karunika Choo
                   ` (7 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Partition commands can be rejected while the hardware changes state.

Track the latest command and its target access window. Handle
INVALID_COMMAND interrupts without waiting for state transitions
in IRQ context.

Retry yield and close commands unless the partition is in RESET.
Retry open commands unless the partition is in WINDOW_OPEN.

If an open command finds the partition open for a different access
window, request scheduler recovery.

Handle INVALID_COMMAND before RESET_DONE. Clear the saved command
when handling RESET_DONE.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- make error notification hold assignment_lock during sched_id lookup.
- Track YIELD_IDLE and update invalid command handling to prevent
  untracked commands from triggering the interrupt for valid commands
  that pass.
- Move invalid command handling before RESET_DONE.

 .../panthor/arbitration/panthor_arbitration.h |   2 +
 .../arbitration/panthor_arbitration_drv.c     |  16 +++
 .../arbitration/panthor_arbitration_sched.c   |  10 ++
 .../arbitration/panthor_arbitration_sched.h   |   4 +
 .../arbitration/panthor_partition_control.c   | 109 +++++++++++++++---
 5 files changed, 128 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 9a6c251d962de..02e1debf1d75a 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -67,4 +67,6 @@ int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 sched_id,
 int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id);
 int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id);
 
+int panthor_arbitration_notify_err(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 6479a7602868c..dc63e85ace3de 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -303,6 +303,22 @@ int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id)
 	return panthor_partition_control_close_window(adev->pc[sched_id]);
 }
 
+int panthor_arbitration_notify_err(struct panthor_arbitration *adev, u8 aw_id)
+{
+	int idx;
+
+	guard(spinlock_irqsave)(&adev->assignment_lock);
+
+	idx = to_assigned_sched_idx(adev, aw_id);
+
+	if (idx < 0)
+		return idx;
+
+	panthor_arbitration_sched_reset(adev->sched[idx], aw_id);
+
+	return 0;
+}
+
 static int panthor_arbitration_probe(struct platform_device *pdev)
 {
 	struct panthor_arbitration *adev;
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
index 95a7685a5b3d6..16bd0fac2514e 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -838,3 +838,13 @@ void panthor_arbitration_sched_disable_clear(struct panthor_arbitration_sched *s
 
 	sched->disabled &= ~BIT(reason);
 }
+
+void panthor_arbitration_sched_reset(struct panthor_arbitration_sched *sched, u8 aw_id)
+{
+	guard(spinlock_irqsave)(&sched->lock);
+
+	if (aw_id != sched->active_aw_id)
+		return;
+
+	arb_sched_queue_work(sched, &sched->reset_work);
+}
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
index 187124ac39a5e..7e192dfc7756b 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.h
@@ -6,6 +6,8 @@
 
 #include <linux/types.h>
 
+#include "panthor_arbitration.h"
+
 struct panthor_arbitration;
 struct panthor_arbitration_sched;
 
@@ -34,6 +36,8 @@ void panthor_arbitration_sched_disable_set(struct panthor_arbitration_sched *sch
 void panthor_arbitration_sched_disable_clear(struct panthor_arbitration_sched *sched,
 					     enum panthor_arb_sched_disable_reason reason);
 
+void panthor_arbitration_sched_reset(struct panthor_arbitration_sched *sched, u8 aw_id);
+
 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);
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
index d8fdafbf31864..3b472403f49d1 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -29,6 +29,7 @@
 
 #define AM_PART_COMMAND				0x100
 #define   AM_PART_SET_COMMAND(x)		FIELD_PREP(GENMASK(7, 0), x)
+#define   AM_PART_GET_COMMAND(x)		FIELD_GET(GENMASK(7, 0), x)
 #define     PART_CMD_YIELD_IDLE			0x10
 #define     PART_CMD_YIELD_NOW			0x11
 #define     PART_CMD_CLOSE_WINDOW		0x20
@@ -43,6 +44,15 @@
 #define PART_REG_POLL_SLEEP_US			10
 #define PART_STATE_TRANSITION_TIMEOUT_US	5000000
 
+/** struct panthor_partition_cmd - Partition command data */
+struct panthor_partition_cmd {
+	/** @cmd: AM_PARTITION_COMMAND value */
+	u32 cmd;
+
+	/** @aw_id: AW ID the command was sent for */
+	int aw_id;
+};
+
 /**
  * struct panthor_partition_control - Partition control data
  */
@@ -73,6 +83,9 @@ struct panthor_partition_control {
 
 	/** @closing: synchronous closing of the partition */
 	bool closing;
+
+	/** @last_cmd: Last sent command */
+	struct panthor_partition_cmd last_cmd;
 };
 
 static void partition_irq_suspend(struct panthor_partition_control *pc)
@@ -118,6 +131,23 @@ static int partition_state_wait(struct panthor_partition_control *pc, u32 state)
 				 false, pc);
 }
 
+static inline void
+partition_command_write_untracked(struct panthor_partition_control *pc, u32 cmd)
+{
+	gpu_write(pc->iomem, AM_PART_COMMAND, cmd);
+}
+
+static void partition_command_write(struct panthor_partition_control *pc,
+				    u32 cmd, int aw_id)
+{
+	lockdep_assert_held(&pc->lock);
+
+	partition_command_write_untracked(pc, cmd);
+
+	pc->last_cmd.cmd = cmd;
+	pc->last_cmd.aw_id = aw_id;
+}
+
 static int yield_now(struct panthor_partition_control *pc)
 {
 	guard(spinlock_irqsave)(&pc->lock);
@@ -128,8 +158,8 @@ static int yield_now(struct panthor_partition_control *pc)
 	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
 		return -EAGAIN;
 
-	gpu_write(pc->iomem, AM_PART_COMMAND,
-		  AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW));
+	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW),
+				pc->current_aw);
 
 	return 0;
 }
@@ -144,8 +174,8 @@ static int yield_idle(struct panthor_partition_control *pc)
 	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
 		return -EAGAIN;
 
-	gpu_write(pc->iomem, AM_PART_COMMAND,
-		  AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE));
+	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE),
+				pc->current_aw);
 
 	return 0;
 }
@@ -162,8 +192,8 @@ static int window_close(struct panthor_partition_control *pc)
 
 	pc->closing = true;
 
-	gpu_write(pc->iomem, AM_PART_COMMAND,
-		  AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW));
+	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW),
+				pc->current_aw);
 
 	return 0;
 }
@@ -188,8 +218,8 @@ static int window_open(struct panthor_partition_control *pc, u8 aw_id)
 		return -EBUSY;
 	}
 
-	gpu_write(pc->iomem, AM_PART_COMMAND,
-		  AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) | AM_PART_SET_WINDOW(aw_id));
+	partition_command_write(pc, (AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) |
+				     AM_PART_SET_WINDOW(aw_id)), aw_id);
 
 	pc->current_aw = aw_id;
 
@@ -201,10 +231,16 @@ static void partition_handle_reset_done(struct panthor_partition_control *pc)
 	bool notify_stopped = false;
 	int aw_id;
 
+	gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, PART_RESET_DONE);
+
 	scoped_guard(spinlock_irqsave, &pc->lock) {
 		aw_id = pc->current_aw;
 		pc->current_aw = AW_UNASSIGNED;
 
+		/* invalidate last command */
+		pc->last_cmd.aw_id = AW_UNASSIGNED;
+		pc->last_cmd.cmd = 0;
+
 		/* RESET_DONE from CLOSE_WINDOW */
 		if (pc->closing)
 			pc->closing = false;
@@ -216,6 +252,55 @@ static void partition_handle_reset_done(struct panthor_partition_control *pc)
 		panthor_arbitration_on_stopped(dev_get_drvdata(pc->dev), aw_id);
 }
 
+static void partition_handle_invalid_cmd(struct panthor_partition_control *pc)
+{
+	bool on_err = false;
+	int aw_id;
+
+	gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, PART_INVALID_COMMAND);
+
+	scoped_guard(spinlock_irqsave, &pc->lock) {
+		if (!pc->last_cmd.cmd)
+			return;
+
+		if (pc->last_cmd.aw_id < 0)
+			return;
+
+		if (pc->last_cmd.aw_id != pc->current_aw)
+			return;
+
+		switch (AM_PART_GET_COMMAND(pc->last_cmd.cmd)) {
+		case PART_CMD_YIELD_IDLE:
+		case PART_CMD_YIELD_NOW:
+		case PART_CMD_CLOSE_WINDOW:
+			if (partition_state_get(pc) != PART_STATE_RESET) {
+				partition_command_write(pc, pc->last_cmd.cmd,
+							pc->last_cmd.aw_id);
+				return;
+			}
+			break;
+		case PART_CMD_OPEN_WINDOW:
+			if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN) {
+				partition_command_write(pc, pc->last_cmd.cmd,
+							pc->last_cmd.aw_id);
+				return;
+			} else if (partition_aw_get(pc) == pc->last_cmd.aw_id) {
+				return;
+			}
+
+			on_err = true;
+			aw_id = pc->last_cmd.aw_id;
+
+			break;
+		default:
+			return;
+		}
+	}
+
+	if (on_err)
+		panthor_arbitration_notify_err(dev_get_drvdata(pc->dev), aw_id);
+}
+
 static irqreturn_t partition_irq_raw_handler(int irq, void *data)
 {
 	struct panthor_partition_control *pc = data;
@@ -230,13 +315,11 @@ static irqreturn_t partition_irq_raw_handler(int irq, void *data)
 	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);
+		partition_handle_invalid_cmd(pc);
 
-	gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, status);
+	if (status & PART_RESET_DONE)
+		partition_handle_reset_done(pc);
 
 	return IRQ_HANDLED;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (19 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:15   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
                   ` (6 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

v15 GPU introduces a YIELD_IDLE command/interrupt to signal an access
window to relinquish GPU access as soon as it is idle. With FW v2, the
GPU is immediately yielded when it detects the GPU is idle and the
YIELD_IDLE interrupt is raised.

For latency and CPU scheduling reasons, we want to avoid constantly
yielding the GPU the moment it goes idle when there is only a singular
access window requesting access. As such, we only send YIELD_IDLE when:

- The GPU is granted to an access window and there are other access
  windows requesting GPU access.
- A new GPU_REQUEST was received and the currently active access window
  was the singular requesting AW.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Align with arbitration scheduler retry work changes
- Align with sched-to-AW interface changes.

 .../panthor/arbitration/panthor_arbitration.h |  1 +
 .../arbitration/panthor_arbitration_drv.c     |  5 +++
 .../arbitration/panthor_arbitration_sched.c   | 34 ++++++++++++++++++-
 3 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
index 02e1debf1d75a..0fcaf393a7d33 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration.h
@@ -67,6 +67,7 @@ int panthor_arbitration_on_grant(struct panthor_arbitration *adev, u8 sched_id,
 int panthor_arbitration_on_stop(struct panthor_arbitration *adev, u8 sched_id);
 int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id);
 
+int panthor_arbitration_notify_yield_idle(struct panthor_arbitration *adev, u8 sched_id);
 int panthor_arbitration_notify_err(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 dc63e85ace3de..4bd558d1984e7 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
@@ -303,6 +303,11 @@ int panthor_arbitration_on_close(struct panthor_arbitration *adev, u8 sched_id)
 	return panthor_partition_control_close_window(adev->pc[sched_id]);
 }
 
+int panthor_arbitration_notify_yield_idle(struct panthor_arbitration *adev, u8 sched_id)
+{
+	return panthor_partition_control_yield_idle(adev->pc[sched_id]);
+}
+
 int panthor_arbitration_notify_err(struct panthor_arbitration *adev, u8 aw_id)
 {
 	int idx;
diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
index 16bd0fac2514e..f137b73deaa32 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
@@ -53,6 +53,7 @@ struct panthor_arbitration_work {
 enum panthor_arbitration_retry_work {
 	PANTHOR_ARB_WORK_STOP = 0,
 	PANTHOR_ARB_WORK_CLOSE,
+	PANTHOR_ARB_WORK_YIELD_IDLE,
 	PANTHOR_ARB_WORK_COUNT,
 };
 
@@ -186,6 +187,17 @@ static void arb_sched_queue_retry_work(struct panthor_arbitration_sched *sched,
 		arb_sched_queue_work(sched, &sched->retry_work);
 }
 
+static void arb_sched_yield_idle_locked(struct panthor_arbitration_sched *sched)
+{
+	int ret;
+
+	lockdep_assert_held(&sched->lock);
+
+	ret = panthor_arbitration_notify_yield_idle(to_adev(sched), sched->id);
+	if (ret == -EAGAIN)
+		arb_sched_queue_retry_work(sched, PANTHOR_ARB_WORK_YIELD_IDLE);
+}
+
 static int __arb_sched_stop_locked(struct panthor_arbitration_sched *sched)
 {
 	int ret;
@@ -382,6 +394,10 @@ static int arb_sched_next_locked(struct panthor_arbitration_sched *sched)
 
 	arb_sched_grant_locked(sched);
 
+	if (sched->phase == ARB_SCHED_PHASE_GRANTED &&
+	    !kfifo_is_empty(&sched->queue))
+		arb_sched_yield_idle_locked(sched);
+
 	wake_up_all(&sched->waitqueue);
 
 	return 0;
@@ -431,6 +447,12 @@ static void arb_sched_retry_work(struct work_struct *work)
 		return;
 
 	switch (sched->phase) {
+	case ARB_SCHED_PHASE_IDLE:
+	case ARB_SCHED_PHASE_GRANTING:
+	case ARB_SCHED_PHASE_GRANTED:
+		if (test_bit(PANTHOR_ARB_WORK_YIELD_IDLE, work_tmp))
+			arb_sched_yield_idle_locked(sched);
+		break;
 	case ARB_SCHED_PHASE_STOPPING:
 		if (test_bit(PANTHOR_ARB_WORK_STOP, work_tmp))
 			arb_sched_stop_locked(sched);
@@ -551,8 +573,18 @@ int panthor_arbitration_sched_on_request(struct panthor_arbitration_sched *sched
 	 * Grant timer for the current AW has previously expired, request it to
 	 * yield the GPU.
 	 */
-	if (sched->grant_previously_expired)
+	if (sched->grant_previously_expired) {
 		arb_sched_request_stop_locked(sched);
+		return 0;
+	}
+
+	/*
+	 * GPU was previously granted to a lone requesting AW, YIELD_IDLE was
+	 * not sent to it. Send YIELD_IDLE to it now that multiple AWs are
+	 * requesting for the GPU.
+	 */
+	if (sched->phase == ARB_SCHED_PHASE_GRANTED && kfifo_len(&sched->queue) == 1)
+		arb_sched_yield_idle_locked(sched);
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 22/28] drm/panthor: Add access-window support
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (20 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:20   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
                   ` (5 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

Add the VM-side access-window component for Mali v15 GPUs.

Negotiate the AM message version, request GPU access from the
arbiter, and wait for the window-open interrupt. Yield GPU access
during suspend.

Keep hardware binding in panthor_hw_init(). Call
panthor_hw_info_init() to read GPU information after
panthor_aw_init() obtains GPU access.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Introduce spinlock to protect and serialize state acquisition and
  updates.
- Make panthor_aw_request recheck states after wait to handle unexpected
  state transitions and prevent timing out waiting on an invalid state.
- Tighten protocol version checks and reject sending GPU_REQUEST to
  incompatible arbiter.
- Handle lost GPU_REQUEST message if arbiter was restarted
- Add WINDOW_CLOSED and WINDOW_OPENED handling.
- Fix indentation for IRQ_NONE return in the IRQ raw handler.
- clean up IRQ state during failed initialization and termination.
- Handle yielding of the GPU during termination.
- Split GPU_STOPPED state into YIELDING and SUSPEND_PENDING. Removed old
  GPU_STOP state as we no longer support the ARB_VM_GPU_STOP message.
v1:
- Adapt AW IRQ handling to the updated IRQ locking/state interfaces and
  reuse panthor_irq_suspend().
- Handle -ENOSPC from AM message sending when the outgoing FIFO is full.

 drivers/gpu/drm/panthor/Makefile         |   1 +
 drivers/gpu/drm/panthor/panthor_aw.c     | 498 +++++++++++++++++++++++
 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, 563 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 5d4d0ae64952e..aa48212653dc3 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 0000000000000..5ab1d44f0a22e
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2026 ARM Limited. All rights reserved. */
+
+#include <linux/platform_device.h>
+#include <linux/spinlock.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;
+
+	/** @lock: Protects @state */
+	spinlock_t lock;
+
+	/** @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 panthor_aw_state new)
+{
+	atomic_set(&aw->state, new);
+
+	wake_up_all(&aw->waitqueue);
+}
+
+static bool panthor_aw_state_try_set(struct panthor_aw *aw,
+				     enum panthor_aw_state expected,
+				     enum panthor_aw_state 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 panthor_aw_state target, u32 timeout_ms)
+{
+	return panthor_aw_wait_cond(aw, atomic_read(&aw->state) == target, 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 == -ENOSPC)
+		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);
+	u8 reply_version = AM_MSG_CURRENT_VERSION;
+	int ret;
+
+	ret = panthor_am_msg_version_validate(&aw->msg, version, acked,
+					      &reply_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, reply_version);
+
+		panthor_aw_send_msg(aw, reply);
+
+		guard(spinlock_irqsave)(&aw->lock);
+
+		/*
+		 * If state is GPU_REQUEST, arbiter previously completed
+		 * handshake, New INIT could mean that the arbiter was restarted
+		 * and we need to resend GPU_REQUEST.
+		 */
+		if (atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_REQUEST) {
+			panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
+			return;
+		}
+	}
+
+	scoped_guard(spinlock_irqsave, &aw->lock)
+		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 void panthor_aw_handle_window_closed(struct panthor_aw *aw)
+{
+	struct panthor_device *ptdev = aw->ptdev;
+
+	guard(spinlock_irqsave)(&aw->lock);
+
+	if (panthor_aw_is_open(aw))
+		return;
+
+	switch (atomic_read(&aw->state)) {
+	case PANTHOR_AW_STATE_GPU_GRANTED:
+		/* TODO: schedule cleanup */
+		break;
+	case PANTHOR_AW_STATE_GPU_REQUEST:
+		/*
+		 * Window may have been closed immediately after opening.
+		 * Setting state back to READY will prevent WINDOW_OPENED from
+		 * mistakenly transitioning the state to GRANTED.
+		 */
+	case PANTHOR_AW_STATE_YIELDING:
+		/* Triggered from messaged-based yield. Unblock its wait */
+		panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+		break;
+	default:
+		drm_warn(&ptdev->base, "Unexpected WINDOW_CLOSED received");
+		break;
+	}
+}
+
+static void panthor_aw_handle_window_opened(struct panthor_aw *aw)
+{
+	guard(spinlock_irqsave)(&aw->lock);
+
+	if (!panthor_aw_is_open(aw))
+		return;
+
+	switch (atomic_read(&aw->state)) {
+	case PANTHOR_AW_STATE_GPU_GRANTED:
+	case PANTHOR_AW_STATE_SUSPEND_PENDING:
+	case PANTHOR_AW_STATE_INIT:
+		break;
+	case PANTHOR_AW_STATE_GPU_REQUEST:
+		panthor_aw_state_set(aw, PANTHOR_AW_STATE_GPU_GRANTED);
+		break;
+	default:
+		panthor_aw_state_set(aw, PANTHOR_AW_STATE_YIELDING);
+		panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED);
+	}
+}
+
+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->lock) {
+		if (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);
+
+	if (status & WINDOW_IRQ_WINDOW_CLOSED)
+		panthor_aw_handle_window_closed(aw);
+
+	if (status & WINDOW_IRQ_WINDOW_OPENED)
+		panthor_aw_handle_window_opened(aw);
+
+	gpu_write(pirq->iomem, INT_CLEAR, status);
+
+	return IRQ_HANDLED;
+}
+
+static void panthor_aw_irq_suspend(struct panthor_irq *pirq)
+{
+	panthor_irq_suspend(pirq);
+}
+
+static void panthor_aw_irq_resume(struct panthor_irq *pirq)
+{
+	guard(spinlock_irqsave)(&pirq->lock);
+
+	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,
+				  void __iomem *iomem)
+{
+	pirq->ptdev = ptdev;
+	pirq->irq = irq;
+	pirq->mask = 0;
+	pirq->iomem = iomem;
+	spin_lock_init(&pirq->lock);
+	pirq->state = PANTHOR_IRQ_STATE_SUSPENDED;
+
+	gpu_write(pirq->iomem, INT_MASK, 0);
+	return devm_request_irq(ptdev->base.dev, irq, panthor_aw_irq_raw_hander,
+				IRQF_SHARED, "panthor-aw", pirq);
+}
+
+static int panthor_aw_request(struct panthor_aw *aw)
+{
+	enum panthor_aw_state state;
+	u32 timeout_ms;
+	int ret;
+
+again:
+	scoped_guard(spinlock_irqsave, &aw->lock) {
+		state = atomic_read(&aw->state);
+
+		switch (state) {
+		case PANTHOR_AW_STATE_GPU_GRANTED:
+			return 0;
+		case PANTHOR_AW_STATE_SUSPEND_PENDING:
+			return -EAGAIN;
+		case PANTHOR_AW_STATE_INIT:
+			panthor_aw_handshake_init(aw);
+			timeout_ms = PANTHOR_AW_HANDSHAKE_TIMEOUT_MS;
+			break;
+		case PANTHOR_AW_STATE_READY:
+			if (!panthor_am_msg_supported(&aw->msg)) {
+				drm_err(&aw->ptdev->base,
+					"unsupported protocol version 0x%x. MIN: 0x%x CUR: 0x%x",
+					aw->msg.version,
+					AM_MSG_MIN_SUPPORTED_VERSION,
+					AM_MSG_CURRENT_VERSION);
+				return -EOPNOTSUPP;
+			}
+
+			panthor_aw_state_set(aw, PANTHOR_AW_STATE_GPU_REQUEST);
+			panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
+			fallthrough;
+		case PANTHOR_AW_STATE_GPU_REQUEST:
+			timeout_ms = PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS;
+			break;
+		case PANTHOR_AW_STATE_YIELDING:
+			timeout_ms = PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS;
+			break;
+		}
+
+		state = atomic_read(&aw->state);
+	}
+
+	ret = panthor_aw_wait_cond(aw, atomic_read(&aw->state) != state, timeout_ms);
+	if (ret) {
+		scoped_guard(spinlock_irqsave, &aw->lock) {
+			switch (atomic_read(&aw->state)) {
+			case PANTHOR_AW_STATE_GPU_GRANTED:
+				return 0;
+			case PANTHOR_AW_STATE_GPU_REQUEST:
+				panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
+				break;
+			default:
+				break;
+			}
+		}
+
+		return ret;
+	}
+
+	goto again;
+}
+
+static int __panthor_aw_suspend(struct panthor_aw *aw)
+{
+	scoped_guard(spinlock_irqsave, &aw->lock) {
+		switch (atomic_read(&aw->state)) {
+		case PANTHOR_AW_STATE_READY:
+			return 0;
+		case PANTHOR_AW_STATE_SUSPEND_PENDING:
+			return 0;
+		case PANTHOR_AW_STATE_GPU_GRANTED:
+			panthor_aw_state_set(aw, PANTHOR_AW_STATE_YIELDING);
+			panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED);
+			break;
+		default:
+			break;
+		}
+	}
+
+	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 (IS_ERR(aw->wq))
+		return PTR_ERR(aw->wq);
+
+	aw->ptdev = ptdev;
+	aw->iomem = ptdev->iomem;
+
+	INIT_WORK(&aw->msg_retry_work, panthor_aw_msg_retry_work);
+
+	init_waitqueue_head(&aw->waitqueue);
+	spin_lock_init(&aw->lock);
+	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, aw->iomem + WINDOW_INT_BASE);
+	if (ret)
+		return ret;
+
+	panthor_irq_enable_events(&aw->irq, WINDOW_IRQ_MASK);
+	panthor_aw_irq_resume(&aw->irq);
+
+	ret = panthor_aw_request(aw);
+	if (ret)
+		panthor_aw_unplug(ptdev);
+
+	return ret;
+}
+
+void panthor_aw_unplug(struct panthor_device *ptdev)
+{
+	struct panthor_aw *aw = ptdev->aw;
+
+	if (!aw)
+		return;
+
+	if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) {
+		__panthor_aw_suspend(aw);
+		panthor_aw_irq_suspend(&aw->irq);
+	}
+
+	disable_work_sync(&aw->msg_retry_work);
+}
+
+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;
+
+	ret = __panthor_aw_suspend(aw);
+
+	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 0000000000000..60d5c8b6c9afc
--- /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 panthor_aw_state - Enumeration of possible Access Window states
+ */
+enum panthor_aw_state {
+	/** @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_YIELDING: Stopped GPU access, pending close. */
+	PANTHOR_AW_STATE_YIELDING,
+
+	/** @PANTHOR_AW_STATE_SUSPEND_PENDING: AW closed, pending host cleanup. */
+	PANTHOR_AW_STATE_SUSPEND_PENDING,
+};
+
+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 9687c59de3505..98c0dd3ce7bcb 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);
@@ -259,10 +261,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;
@@ -322,6 +332,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 f0a96ff0f13da..ede630ddce3e1 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -21,6 +21,7 @@
 
 #include "panthor_device_io.h"
 
+struct panthor_aw;
 struct panthor_csf;
 struct panthor_csf_ctx;
 struct panthor_device;
@@ -202,6 +203,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 e8e899c638128..7d40091c1e95a 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -353,7 +353,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;
 	u32 prod_id, major, minor, status;
@@ -471,9 +471,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 dee641c698661..6ca20701f96e1 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -66,6 +66,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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (21 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:23   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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_device.c | 50 +++++++++++++++++++++---
 drivers/gpu/drm/panthor/panthor_device.h |  9 +++++
 2 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 98c0dd3ce7bcb..2ccd8ad6318d1 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;
@@ -293,6 +297,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;
@@ -484,21 +491,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;
 }
 
@@ -609,10 +650,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 ede630ddce3e1..025efb91c1a38 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -353,6 +353,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. */
@@ -442,6 +448,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 related	[flat|nested] 57+ messages in thread

* [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (22 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:24   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows Karunika Choo
                   ` (3 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

A scheduler tick can run during forced system suspend and access GPU
registers after the clocks have been disabled.

Add system suspend callbacks that disable and drain tick work before
forced suspend. Re-enable and queue a fresh tick after forced resume or
when suspend fails.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
 drivers/gpu/drm/panthor/panthor_device.c | 26 ++++++++++++++++++++++++
 drivers/gpu/drm/panthor/panthor_device.h |  3 +++
 drivers/gpu/drm/panthor/panthor_drv.c    |  8 ++++----
 drivers/gpu/drm/panthor/panthor_sched.c  | 15 ++++++++++++++
 drivers/gpu/drm/panthor/panthor_sched.h  |  2 ++
 5 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 2ccd8ad6318d1..ba5252c27410f 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -662,3 +662,29 @@ int panthor_device_suspend(struct device *dev)
 	atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_SUSPENDED);
 	return 0;
 }
+
+int panthor_device_system_suspend(struct device *dev)
+{
+	struct panthor_device *ptdev = dev_get_drvdata(dev);
+	int ret;
+
+	panthor_sched_pre_suspend(ptdev);
+
+	ret = pm_runtime_force_suspend(dev);
+	if (ret)
+		panthor_sched_post_resume(ptdev);
+
+	return ret;
+}
+
+int panthor_device_system_resume(struct device *dev)
+{
+	struct panthor_device *ptdev = dev_get_drvdata(dev);
+	int ret;
+
+	ret = pm_runtime_force_resume(dev);
+
+	panthor_sched_post_resume(ptdev);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 025efb91c1a38..e002ebbc88579 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -448,6 +448,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_system_resume(struct device *dev);
+int panthor_device_system_suspend(struct device *dev);
+
 int panthor_device_suspend_hw_components(struct panthor_device *ptdev);
 int panthor_device_resume_hw_components(struct panthor_device *ptdev);
 
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index e9b71ff5ee53f..b92d600dc4764 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1888,10 +1888,10 @@ static const struct of_device_id dt_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dt_match);
 
-static DEFINE_RUNTIME_DEV_PM_OPS(panthor_pm_ops,
-				 panthor_device_suspend,
-				 panthor_device_resume,
-				 NULL);
+static const struct dev_pm_ops panthor_pm_ops = {
+	SYSTEM_SLEEP_PM_OPS(panthor_device_system_suspend, panthor_device_system_resume)
+	RUNTIME_PM_OPS(panthor_device_suspend, panthor_device_resume, NULL)
+};
 
 static struct platform_driver panthor_driver = {
 	.probe = panthor_probe,
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index a343c5180e01e..3b2ed4095f80e 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -4047,6 +4047,21 @@ void panthor_job_update_resvs(struct drm_exec *exec, struct drm_sched_job *sched
 				DMA_RESV_USAGE_BOOKKEEP, DMA_RESV_USAGE_BOOKKEEP);
 }
 
+void panthor_sched_pre_suspend(struct panthor_device *ptdev)
+{
+	struct panthor_scheduler *sched = ptdev->scheduler;
+
+	disable_delayed_work_sync(&sched->tick_work);
+}
+
+void panthor_sched_post_resume(struct panthor_device *ptdev)
+{
+	struct panthor_scheduler *sched = ptdev->scheduler;
+
+	enable_delayed_work(&sched->tick_work);
+	panthor_sched_resume(ptdev);
+}
+
 void panthor_sched_unplug(struct panthor_device *ptdev)
 {
 	struct panthor_scheduler *sched = ptdev->scheduler;
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index d87cbdba4d40b..af7ca62125ae8 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -49,6 +49,8 @@ void panthor_sched_pre_reset(struct panthor_device *ptdev);
 void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed);
 void panthor_sched_suspend(struct panthor_device *ptdev);
 void panthor_sched_resume(struct panthor_device *ptdev);
+void panthor_sched_pre_suspend(struct panthor_device *ptdev);
+void panthor_sched_post_resume(struct panthor_device *ptdev);
 
 void panthor_sched_report_mmu_fault(struct panthor_device *ptdev);
 void panthor_sched_prepare_for_vm_destruction(struct panthor_device *ptdev);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (23 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:50   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
                   ` (2 subsequent siblings)
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

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.

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>
---
v2:
- Align with state lock changes.
- Fix case where autosuspend would not be triggered if tick_work drops
  its PM ref earlier and post_yield_work becomes the last PM ref holder.
- Fix case where post_yield_work could run after FW/MMU was unplugged.
- Wait for AW state transition before retrying runtime resume if -EAGAIN
  was received the first time around.
- Guard panthor_sched_resume against access if scheduler is not
  initialized or already torn down.

 drivers/gpu/drm/panthor/panthor_aw.c     | 146 ++++++++++++++++++++++-
 drivers/gpu/drm/panthor/panthor_aw.h     |   6 +
 drivers/gpu/drm/panthor/panthor_device.c |  10 +-
 drivers/gpu/drm/panthor/panthor_device.h |  28 ++++-
 drivers/gpu/drm/panthor/panthor_sched.c  |   3 +
 5 files changed, 181 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index 5ab1d44f0a22e..3b418c816d824 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -81,6 +81,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)
@@ -114,6 +117,66 @@ static int panthor_aw_state_wait(struct panthor_aw *aw,
 	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_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;
+
+	scoped_guard(spinlock_irqsave, &aw->lock) {
+		/* Something else has progressed the state to READY. */
+		if (atomic_read(&aw->state) != PANTHOR_AW_STATE_SUSPEND_PENDING)
+			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.");
+
+	scoped_guard(spinlock_irqsave, &aw->lock)
+		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_autosuspend(dev);
+}
+
+static bool panthor_aw_schedule_post_yield_work(struct panthor_aw *aw)
+{
+	lockdep_assert_held(&aw->lock);
+
+	if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED,
+				     PANTHOR_AW_STATE_SUSPEND_PENDING)) {
+		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 =
@@ -213,7 +276,7 @@ static void panthor_aw_handle_window_closed(struct panthor_aw *aw)
 
 	switch (atomic_read(&aw->state)) {
 	case PANTHOR_AW_STATE_GPU_GRANTED:
-		/* TODO: schedule cleanup */
+		panthor_aw_schedule_post_yield_work(aw);
 		break;
 	case PANTHOR_AW_STATE_GPU_REQUEST:
 		/*
@@ -416,6 +479,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);
 	spin_lock_init(&aw->lock);
@@ -443,6 +507,16 @@ int panthor_aw_init(struct panthor_device *ptdev)
 	return ret;
 }
 
+void panthor_aw_cancel(struct panthor_device *ptdev)
+{
+	struct panthor_aw *aw = ptdev->aw;
+
+	if (!aw)
+		return;
+
+	disable_work_sync(&aw->post_yield_work);
+}
+
 void panthor_aw_unplug(struct panthor_device *ptdev)
 {
 	struct panthor_aw *aw = ptdev->aw;
@@ -456,6 +530,7 @@ void panthor_aw_unplug(struct panthor_device *ptdev)
 	}
 
 	disable_work_sync(&aw->msg_retry_work);
+	panthor_aw_cancel(ptdev);
 }
 
 int panthor_aw_resume(struct panthor_device *ptdev)
@@ -463,21 +538,41 @@ 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 */
+		__panthor_aw_suspend(aw);
 		goto err_out;
 	}
 
-	return 0;
+	switch (atomic_read(&aw->state)) {
+	case PANTHOR_AW_STATE_SUSPEND_PENDING:
+		/*
+		 * WINDOW_CLOSED won the race. Suspend components before the
+		 * caller can disable clocks. post_yield_work will transition
+		 * state to READY after this PM transition completes.
+		 */
+		panthor_device_suspend_hw_components(ptdev);
+		ret = -EAGAIN;
+		break;
+	case PANTHOR_AW_STATE_GPU_GRANTED:
+		return 0;
+	default:
+		goto again;
+	}
 
 err_out:
-	panthor_aw_irq_suspend(&aw->irq);
 	return ret;
 }
 
@@ -488,11 +583,50 @@ 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);
+
+	ret = panthor_device_suspend_hw_components(ptdev);
+	if (ret)
+		goto out_irq_suspend;
 
 	ret = __panthor_aw_suspend(aw);
 
+out_irq_suspend:
 	panthor_aw_irq_suspend(&aw->irq);
 
 	return ret;
 }
+
+int panthor_aw_wait_transition_complete(struct panthor_device *ptdev)
+{
+	/*
+	 * This is only called from panthor_device_resume_and_get. If the error
+	 * condition with which this is called changes in the future, the return
+	 * value needs reconsidering.
+	 */
+	if (!ptdev->aw)
+		return -EAGAIN;
+
+	return panthor_aw_state_wait_transition(
+		ptdev->aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+}
+
+int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev)
+{
+	int ret;
+
+	if (!ptdev->aw)
+		return 0;
+
+again:
+	ret = panthor_aw_resume(ptdev);
+	if (ret != -EAGAIN)
+		return ret;
+
+	ret = panthor_aw_state_wait_transition(
+		ptdev->aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
+	if (ret)
+		return ret;
+
+	goto again;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
index 60d5c8b6c9afc..d1f43c4ba17e5 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.h
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -35,8 +35,14 @@ int panthor_aw_init(struct panthor_device *ptdev);
 
 void panthor_aw_unplug(struct panthor_device *ptdev);
 
+void panthor_aw_cancel(struct panthor_device *ptdev);
+
 int panthor_aw_resume(struct panthor_device *ptdev);
 
 int panthor_aw_suspend(struct panthor_device *ptdev);
 
+int panthor_aw_wait_transition_complete(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 ba5252c27410f..d1ef460577c99 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -92,6 +92,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
 	 */
 	mutex_unlock(&ptdev->unplug.lock);
 
+	panthor_aw_cancel(ptdev);
 	/* Now, try to cleanly shutdown the GPU before the device resources
 	 * get reclaimed.
 	 */
@@ -325,6 +326,7 @@ int panthor_device_init(struct panthor_device *ptdev)
 	panthor_sched_unplug(ptdev);
 
 err_unplug_fw:
+	panthor_aw_cancel(ptdev);
 	panthor_fw_unplug(ptdev);
 
 err_unplug_mmu:
@@ -577,15 +579,17 @@ 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)
 			panthor_sched_resume(ptdev);
+		else
+			panthor_aw_suspend(ptdev);
 
 		drm_dev_exit(cookie);
 
@@ -650,7 +654,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 e002ebbc88579..af2282240e692 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -20,6 +20,7 @@
 #include <drm/panthor_drm.h>
 
 #include "panthor_device_io.h"
+#include "panthor_aw.h"
 
 struct panthor_aw;
 struct panthor_csf;
@@ -456,7 +457,10 @@ 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);
+	int ret;
+
+again:
+	ret = pm_runtime_resume_and_get(ptdev->base.dev);
 
 	/* If the resume failed, we need to clear the runtime_error, which
 	 * can done by forcing the RPM state to suspended. If multiple
@@ -470,8 +474,26 @@ 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);
+
+		if (ret == -EAGAIN) {
+			ret = panthor_aw_wait_transition_complete(ptdev);
+			if (ret)
+				return ret;
+
+			goto again;
+		}
+
+		return ret;
+	}
+
+	ret = panthor_aw_ensure_gpu_access(ptdev);
+	if (ret) {
+		pm_runtime_put_autosuspend(ptdev->base.dev);
+		return ret;
+	}
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 3b2ed4095f80e..729eeb339fa40 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -2889,6 +2889,9 @@ void panthor_sched_prepare_for_vm_destruction(struct panthor_device *ptdev)
 
 void panthor_sched_resume(struct panthor_device *ptdev)
 {
+	if (!ptdev->scheduler)
+		return;
+
 	/* Force a tick to re-evaluate after a resume. */
 	sched_queue_delayed_work(ptdev->scheduler, tick, 0);
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (24 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:30   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset Karunika Choo
  2026-11-10 15:38 ` [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration Karunika Choo
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

An access window can close while Panthor waits for a cache flush,
power transition or firmware boot. After access loss, these
operations may not report completion.

Add a helper to check whether the access window has GPU access.
Suppress timeout errors in cache-flush and power-control waits
after access loss. Allow firmware boot and MCU halt waits to end
when GPU access is lost.

Wake firmware waiters from the access-window interrupt handler.
Publish the firmware pointer after initializing its wait queue.

Skip scheduler processing of firmware events while GPU access
is unavailable.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Notify FW waitqueue on WINDOW_CLOSED to make waiters re-evaluate
  state instead of waiting until timeout.
- Use smp_store_release() and smp_load_acquire() to ensure waitqueue
  initialization is visible to the AW-close handler before it attempts
  to wake the waitqueue.
- Bail early on MCU halt wait.

 drivers/gpu/drm/panthor/panthor_aw.c    |  9 +++++++++
 drivers/gpu/drm/panthor/panthor_aw.h    |  2 ++
 drivers/gpu/drm/panthor/panthor_fw.c    | 20 +++++++++++++++---
 drivers/gpu/drm/panthor/panthor_fw.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 |  3 +++
 7 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index 3b418c816d824..f344a2aa32979 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -340,6 +340,7 @@ static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data)
 	if (status & WINDOW_IRQ_WINDOW_OPENED)
 		panthor_aw_handle_window_opened(aw);
 
+	panthor_fw_notify_waitqueue(ptdev);
 	gpu_write(pirq->iomem, INT_CLEAR, status);
 
 	return IRQ_HANDLED;
@@ -630,3 +631,11 @@ int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev)
 
 	goto again;
 }
+
+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 d1f43c4ba17e5..29b32f3d810fd 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.h
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -45,4 +45,6 @@ int panthor_aw_wait_transition_complete(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_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 1449d9a66302e..8643e7bbfc108 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1140,9 +1140,10 @@ static int panthor_fw_start(struct panthor_device *ptdev)
 	gpu_write(fw->iomem, MCU_CONTROL, MCU_CONTROL_AUTO);
 
 	if (!wait_event_timeout(ptdev->fw->req_waitqueue,
-				ptdev->fw->booted,
+				(ptdev->fw->booted ||
+				 !panthor_aw_has_gpu_access(ptdev)),
 				msecs_to_jiffies(1000))) {
-		if (!ptdev->fw->booted &&
+		if (!ptdev->fw->booted && panthor_aw_has_gpu_access(ptdev) &&
 		    !(gpu_read(fw->irq.iomem, INT_STAT) & JOB_INT_GLOBAL_IF))
 			timedout = true;
 	}
@@ -1180,6 +1181,9 @@ static bool panthor_fw_mcu_halted(struct panthor_device *ptdev)
 	struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
 	bool halted;
 
+	if (!panthor_aw_has_gpu_access(ptdev))
+		return true;
+
 	halted = gpu_read(ptdev->fw->iomem, MCU_STATUS) == MCU_STATUS_HALT;
 
 	if (panthor_fw_has_glb_state(ptdev))
@@ -1223,6 +1227,16 @@ static void panthor_fw_mcu_set_active(struct panthor_device *ptdev)
 		panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT);
 }
 
+void panthor_fw_notify_waitqueue(struct panthor_device *ptdev)
+{
+	struct panthor_fw *fw = smp_load_acquire(&ptdev->fw);
+
+	if (!fw)
+		return;
+
+	wake_up_all(&fw->req_waitqueue);
+}
+
 /**
  * panthor_fw_pre_reset() - Call before a reset.
  * @ptdev: Device.
@@ -1501,10 +1515,10 @@ int panthor_fw_init(struct panthor_device *ptdev)
 		return -ENOMEM;
 
 	fw->iomem = ptdev->iomem + ptdev->hw->regs.mcu_control.offset;
-	ptdev->fw = fw;
 	init_waitqueue_head(&fw->req_waitqueue);
 	INIT_LIST_HEAD(&fw->sections);
 	INIT_DELAYED_WORK(&fw->watchdog.ping_work, panthor_fw_ping_work);
+	smp_store_release(&ptdev->fw, fw);
 
 	irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "job");
 	if (irq <= 0)
diff --git a/drivers/gpu/drm/panthor/panthor_fw.h b/drivers/gpu/drm/panthor/panthor_fw.h
index a99a9b6f4825c..c1c6fb9b7b7d6 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.h
+++ b/drivers/gpu/drm/panthor/panthor_fw.h
@@ -513,6 +513,8 @@ panthor_fw_alloc_suspend_buf_mem(struct panthor_device *ptdev, size_t size);
 
 struct panthor_vm *panthor_fw_vm(struct panthor_device *ptdev);
 
+void panthor_fw_notify_waitqueue(struct panthor_device *ptdev);
+
 void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang);
 int panthor_fw_post_reset(struct panthor_device *ptdev);
 
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index 2e1b00e86cdea..d5666264c1fe6 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -18,6 +18,7 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_print.h>
 
+#include "panthor_aw.h"
 #include "panthor_device.h"
 #include "panthor_gpu.h"
 #include "panthor_gpu_regs.h"
@@ -377,7 +378,8 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
 				msecs_to_jiffies(100))) {
 		scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
 			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 1cfd4b658c81b..66a2c54b070dc 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_gpu_regs.h"
 #include "panthor_hw.h"
@@ -122,7 +123,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;
 		}
@@ -279,9 +281,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);
@@ -343,9 +350,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;
@@ -400,9 +411,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;
@@ -542,7 +557,7 @@ void panthor_pwr_l2_power_off(struct panthor_device *ptdev)
 	const bool has_neural = panthor_hw_has_neural(ptdev);
 
 	/* 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 729eeb339fa40..addc5986554df 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -1887,6 +1887,9 @@ void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
 
 	guard(spinlock)(&ptdev->scheduler->events_lock);
 
+	if (!panthor_aw_has_gpu_access(ptdev))
+		return;
+
 	if (events & JOB_INT_GLOBAL_IF) {
 		sched_process_global_irq_locked(ptdev);
 		events &= ~JOB_INT_GLOBAL_IF;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (25 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:44   ` sashiko-bot
  2026-11-10 15:38 ` [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration Karunika Choo
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

GPU resets can generate access-window events that trigger normal
post-yield cleanup. Firmware can also yield GPU access during reset.

Add a RESET_ACTIVE access-window state. Enter this state only from
GPU_GRANTED. Ignore window-open and window-close events while reset
is active. Use the hardware window status to check GPU access
during reset.

After reset, schedule post-yield cleanup if the hardware window
is closed.

Abort reset preparation when the access window is not GPU_GRANTED.
Suppress firmware restart errors when GPU access has been lost.

Clear pending PWR_IRQ_RESET_COMPLETED flags on resume to prevent
blocking future legitimate resets from executing.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
v2:
- Return state transition outcome from panthor_aw_pre_reset to allow
  reset worker to bail the reset early.
- Make unplug decision due to reset failure before drm_dev_exit()
- Make panthor_aw_has_gpu-access check HW window status if in
  RESET_ACTIVE state to allow waiters to bail during reset if WINDOW was
  closed.
- Defensively clear PWR_IRQ_RESET_COMPLETED on resume to prevent state
  de-sync.

 drivers/gpu/drm/panthor/panthor_aw.c     | 50 +++++++++++++++++++++++-
 drivers/gpu/drm/panthor/panthor_aw.h     |  7 ++++
 drivers/gpu/drm/panthor/panthor_device.c | 13 ++++++
 drivers/gpu/drm/panthor/panthor_pwr.c    |  7 ++++
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
index f344a2aa32979..f7fcf51c2c632 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.c
+++ b/drivers/gpu/drm/panthor/panthor_aw.c
@@ -275,6 +275,9 @@ static void panthor_aw_handle_window_closed(struct panthor_aw *aw)
 		return;
 
 	switch (atomic_read(&aw->state)) {
+	case PANTHOR_AW_STATE_RESET_ACTIVE:
+		/* Ignore this WINDOW_CLOSED as part of reset operation. */
+		return;
 	case PANTHOR_AW_STATE_GPU_GRANTED:
 		panthor_aw_schedule_post_yield_work(aw);
 		break;
@@ -302,6 +305,9 @@ static void panthor_aw_handle_window_opened(struct panthor_aw *aw)
 		return;
 
 	switch (atomic_read(&aw->state)) {
+	case PANTHOR_AW_STATE_RESET_ACTIVE:
+		/* Ignore this WINDOW_OPENED as part of reset operation. */
+		return;
 	case PANTHOR_AW_STATE_GPU_GRANTED:
 	case PANTHOR_AW_STATE_SUSPEND_PENDING:
 	case PANTHOR_AW_STATE_INIT:
@@ -388,6 +394,7 @@ static int panthor_aw_request(struct panthor_aw *aw)
 		switch (state) {
 		case PANTHOR_AW_STATE_GPU_GRANTED:
 			return 0;
+		case PANTHOR_AW_STATE_RESET_ACTIVE:
 		case PANTHOR_AW_STATE_SUSPEND_PENDING:
 			return -EAGAIN;
 		case PANTHOR_AW_STATE_INIT:
@@ -637,5 +644,46 @@ 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);
+	switch (atomic_read(&ptdev->aw->state)) {
+	case PANTHOR_AW_STATE_GPU_GRANTED:
+		return true;
+	case PANTHOR_AW_STATE_RESET_ACTIVE:
+		return panthor_aw_is_open(ptdev->aw);
+	default:
+		break;
+	}
+
+	return false;
+}
+
+int panthor_aw_pre_reset(struct panthor_device *ptdev)
+{
+	struct panthor_aw *aw = ptdev->aw;
+
+	if (!aw)
+		return 0;
+
+	guard(spinlock_irqsave)(&aw->lock);
+
+	if (!panthor_aw_state_try_set(ptdev->aw, PANTHOR_AW_STATE_GPU_GRANTED,
+				      PANTHOR_AW_STATE_RESET_ACTIVE))
+		return -EAGAIN;
+
+	return 0;
+}
+
+void panthor_aw_post_reset(struct panthor_device *ptdev)
+{
+	struct panthor_aw *aw = ptdev->aw;
+
+	if (!aw)
+		return;
+
+	guard(spinlock_irqsave)(&aw->lock);
+
+	panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_RESET_ACTIVE,
+				 PANTHOR_AW_STATE_GPU_GRANTED);
+
+	if (!panthor_aw_is_open(aw))
+		panthor_aw_schedule_post_yield_work(aw);
 }
diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h
index 29b32f3d810fd..dde9c8e8742ff 100644
--- a/drivers/gpu/drm/panthor/panthor_aw.h
+++ b/drivers/gpu/drm/panthor/panthor_aw.h
@@ -24,6 +24,9 @@ enum panthor_aw_state {
 	/** @PANTHOR_AW_STATE_GPU_GRANTED: AW is granted GPU access. */
 	PANTHOR_AW_STATE_GPU_GRANTED,
 
+	/** @PANTHOR_AW_STATE_RESET_ACTIVE: AW is being reset. */
+	PANTHOR_AW_STATE_RESET_ACTIVE,
+
 	/** @PANTHOR_AW_STATE_YIELDING: Stopped GPU access, pending close. */
 	PANTHOR_AW_STATE_YIELDING,
 
@@ -47,4 +50,8 @@ int panthor_aw_ensure_gpu_access(struct panthor_device *ptdev);
 
 bool panthor_aw_has_gpu_access(struct panthor_device *ptdev);
 
+int panthor_aw_pre_reset(struct panthor_device *ptdev);
+
+void panthor_aw_post_reset(struct panthor_device *ptdev);
+
 #endif
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index d1ef460577c99..d4a67277fe6fa 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -140,14 +140,27 @@ static void panthor_device_reset_work(struct work_struct *work)
 		return;
 
 	panthor_sched_pre_reset(ptdev);
+	if (panthor_aw_pre_reset(ptdev) == -EAGAIN) {
+		atomic_set(&ptdev->reset.pending, 0);
+		panthor_sched_post_reset(ptdev, false);
+		goto out_dev_exit;
+	}
 	panthor_fw_pre_reset(ptdev, true);
 	panthor_mmu_pre_reset(ptdev);
+
 	panthor_hw_soft_reset(ptdev);
+
 	panthor_hw_l2_power_on(ptdev);
 	panthor_mmu_post_reset(ptdev);
 	ret = panthor_fw_post_reset(ptdev);
+	if (ret && !panthor_aw_has_gpu_access(ptdev))
+		ret = 0;
+
+	panthor_aw_post_reset(ptdev);
 	atomic_set(&ptdev->reset.pending, 0);
 	panthor_sched_post_reset(ptdev, ret != 0);
+
+out_dev_exit:
 	drm_dev_exit(cookie);
 
 	if (ret) {
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
index 66a2c54b070dc..eef400c4bc6fb 100644
--- a/drivers/gpu/drm/panthor/panthor_pwr.c
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -113,6 +113,9 @@ static int panthor_pwr_reset(struct panthor_device *ptdev, u32 reset_cmd)
 		if (reset_pending(ptdev)) {
 			drm_WARN(&ptdev->base, 1, "Reset already pending");
 		} else {
+			if (!panthor_aw_has_gpu_access(ptdev))
+				return 0;
+
 			ptdev->pwr->pending_reqs |= PWR_IRQ_RESET_COMPLETED;
 			gpu_write(pwr->irq.iomem, INT_CLEAR, PWR_IRQ_RESET_COMPLETED);
 			panthor_pwr_write_command(ptdev, reset_cmd, 0);
@@ -622,5 +625,9 @@ void panthor_pwr_resume(struct panthor_device *ptdev)
 	if (!ptdev->pwr)
 		return;
 
+	/* clear any pending resets on resume to prevent blocking future resets */
+	scoped_guard(spinlock_irqsave, &ptdev->pwr->reqs_lock)
+		ptdev->pwr->pending_reqs &= ~PWR_IRQ_RESET_COMPLETED;
+
 	panthor_irq_resume(&ptdev->pwr->irq);
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration
  2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
                   ` (26 preceding siblings ...)
  2026-11-10 15:38 ` [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset Karunika Choo
@ 2026-11-10 15:38 ` Karunika Choo
  2026-11-10 16:22   ` sashiko-bot
  27 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-10 15:38 UTC (permalink / raw)
  To: dri-devel
  Cc: nd, Boris Brezillon, Steven Price, Liviu Dudau, linux-kernel,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter

A partition may still have an open access window when the arbiter
starts (typically from restarting the driver), while its software state
assumes no active window. Starting arbitration in this state can leave
software out of sync with the hardware.

Recover each partition to RESET before enabling its interrupts. Allow
open windows to yield, fall back to forced close if necessary, and bound
recovery attempts so failure aborts initialization.

Keep partition interrupts masked during recovery and clean up before
normal operation begins.

Signed-off-by: Karunika Choo <karunika.choo@arm.com>
---
 .../arbitration/panthor_partition_control.c   | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
index 3b472403f49d1..2c0f8dc0b3bfb 100644
--- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
+++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
@@ -43,6 +43,7 @@
 
 #define PART_REG_POLL_SLEEP_US			10
 #define PART_STATE_TRANSITION_TIMEOUT_US	5000000
+#define PART_ENSURE_RESET_RETRY_COUNT		3
 
 /** struct panthor_partition_cmd - Partition command data */
 struct panthor_partition_cmd {
@@ -368,6 +369,54 @@ void panthor_partition_control_term(struct panthor_arbitration *adev)
 	}
 }
 
+static int partition_ensure_reset(struct panthor_partition_control *pc)
+{
+	int ret = 0;
+	int count = 0;
+
+again:
+	if ((count >= PART_ENSURE_RESET_RETRY_COUNT) &&
+	    (partition_state_get(pc) != PART_STATE_RESET))
+		return -ETIMEDOUT;
+
+	count++;
+
+	switch (partition_state_get(pc)) {
+	case PART_STATE_RESET:
+		/* clear any remnants of this operation */
+		gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, PART_CONTROL_IRQ_MASK);
+		pc->closing = false;
+		pc->last_cmd.aw_id = AW_UNASSIGNED;
+		pc->last_cmd.cmd = 0;
+		return 0;
+	case PART_STATE_WINDOW_OPENING:
+		ret = partition_state_wait(pc, PART_STATE_WINDOW_OPEN);
+		fallthrough;
+	case PART_STATE_WINDOW_OPEN:
+		/*
+		 * Window was left open likely due to arbiter restart. Send
+		 * YIELD_NOW to current AW and wait for it to yield GPU access.
+		 * On timeout, forcibly close the window.
+		 */
+		if (!ret)
+			ret = yield_now(pc);
+		else
+			ret = window_close(pc);
+
+		/* state changed, reevaluate state */
+		if (ret == -EAGAIN) {
+			ret = 0;
+			goto again;
+		}
+		fallthrough;
+	case PART_STATE_WINDOW_CLOSED:
+		ret = partition_state_wait(pc, PART_STATE_RESET);
+		goto again;
+	}
+
+	return -EINVAL;
+}
+
 static int partition_control_init(struct panthor_arbitration *adev, int i)
 {
 	struct panthor_partition_control *pc;
@@ -407,6 +456,7 @@ static int partition_control_init(struct panthor_arbitration *adev, int i)
 
 	spin_lock_init(&pc->irq_lock);
 	pc->irq_suspended = true;
+	gpu_write(pc->iomem, AM_PART_IRQ_MASK, 0);
 
 	spin_lock_init(&pc->lock);
 	pc->current_aw = AW_UNASSIGNED;
@@ -429,6 +479,10 @@ static int partition_control_init(struct panthor_arbitration *adev, int i)
 
 	adev->pc[i] = pc;
 
+	ret = partition_ensure_reset(pc);
+	if (ret)
+		return ret;
+
 	partition_irq_resume(pc);
 
 	return 0;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX
  2026-11-10 15:38 ` [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX Karunika Choo
@ 2026-11-10 15:47   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 15:47 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] drm/panthor: commit message does not explain why GPU_FEATURES_NX
  is renamed
--

commit b271181b3f70a8fceddc935ef5d82c1ab9eb1aa8
Author: Karunika Choo <karunika.choo@arm.com>

> drm/panthor: Rename neural accelerator feature macro to NX
>
> Rename GPU_FEATURES_NEURAL_ENGINE to GPU_FEATURES_NX.
> Update the panthor_hw helper to use the new name.
>
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>

[Severity: Low]
This isn't a bug, but the commit message only restates the code change
without explaining why GPU_FEATURES_NEURAL_ENGINE is renamed to
GPU_FEATURES_NX while panthor_hw_has_neural() and the PWR_NEURAL_*
definitions keep the neural naming.  Could the commit message explain the
rationale for the new name (for example, matching the hardware register
specification)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=2

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver
  2026-11-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
@ 2026-11-10 15:57   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 15:57 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] drm/panthor: undocumented gpu-supply in
  panthor_system_regulator_init()
--

commit 7d8058a58397fcb291163cb1ac726e2a1ba1d037
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add AM_SYSTEM platform driver

This commit adds the AM_SYSTEM platform driver for Arm Mali 5th Gen AM GPUs
to manage shared clock and regulator resources and perform the initial GPU
reset sequence during probe.

> diff --git a/drivers/gpu/drm/panthor/system/panthor_system_drv.c b/drivers/gpu/drm/panthor/system/panthor_system_drv.c
> new file mode 100644
> index 0000000000000..e70dba4058f38
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/system/panthor_system_drv.c
> @@ -0,0 +1,344 @@
[ ... ]
> +static int panthor_system_regulator_init(struct panthor_system *sdev)
> +{
> +	struct regulator *regulator = NULL;
> +	int ret;
> +
> +	regulator = devm_regulator_get_optional(sdev->dev, "gpu");
> +	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 gpu-supply");
> +	}

[Severity: Low]
Should gpu-supply be documented in
Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml?  That
schema sets additionalProperties: false without listing gpu-supply, so any
arm,mali-gen5-am-system device tree node providing the regulator expected
here will fail dtbs_check validation.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=12

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 19/28] drm/panthor: Add AW assignment tracking
  2026-11-10 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
@ 2026-11-10 16:03   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:03 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] drm/panthor: race replaying pending AW requests in
  arbitration_resource_init()
- [Low] drm/panthor: remove extraneous semicolon after
  to_assigned_sched_idx()
- [Low] drm/panthor: initialize __free() variable in
  arbitration_dt_parse_assigned_aw()
--

commit e073b116d1f4f953a3ab51f1f483486ff4942f9f
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add AW assignment tracking

This commit tracks the scheduler assigned to each access window, initializes
assignments from device tree partition masks, and replays requests that
arrive before access windows are assigned.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
> index 48016d0d88557..6479a7602868c 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_drv.c

[ ... ]

> @@ -17,6 +19,28 @@
>  
>  #define PANTHOR_PM_AUTOSUSPEND_DELAY_MS 100
>  
> +#define DT_ASSIGNED_AW				"arm,access-window-mask"
> +
> +static int to_assigned_sched_idx(struct panthor_arbitration *adev, u8 aw_id)
> +{
> +	int idx = -1;
> +
> +	lockdep_assert_held(&adev->assignment_lock);
> +
> +	if (aw_id >= AM_ARB_MAX_AW_COUNT) {
> +		dev_err(adev->dev, "Invalid AW ID (%u)", aw_id);
> +		return -EINVAL;
> +	}
> +
> +	idx = adev->aw_sched_map[aw_id];
> +	if (idx < 0 || idx >= AM_ARB_MAX_PC_COUNT || !adev->sched[idx]) {
> +		dev_warn_ratelimited(adev->dev, "AW%u not assigned to any sched", aw_id);
> +		return -ENOENT;
> +	}
> +
> +	return idx;
> +};

[Severity: Low]
This isn't a bug, but there is an extra semicolon after the closing brace of
to_assigned_sched_idx().

> @@ -57,42 +81,226 @@ static int panthor_arbitration_runtime_resume(struct device *dev)
>  	return 0;
>  }
>  
> +static u32 arbitration_get_sched_aw_mask(struct panthor_arbitration *adev,
> +					 u8 sched_id)
> +{
> +	u32 aw_mask = 0;
> +
> +	lockdep_assert_held(&adev->assignment_lock);
> +
> +	for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++) {
> +		if (adev->aw_sched_map[i] == sched_id)
> +			aw_mask |= BIT(i);
> +	}
> +
> +	return aw_mask;
> +}
> +
> +static int arbitration_dt_parse_assigned_aw(struct panthor_arbitration *adev)
> +{
> +	struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
> +	struct device *dev = adev->dev;
> +	struct fwnode_handle *partitions __free(fwnode_handle) = NULL;
> +	u32 seen_mask = 0;
> +	u8 seen_partitions = 0;
> +	int ret;
> +
> +	partitions = device_get_named_child_node(dev, "partitions");

[Severity: Low]
This isn't a bug, but per the Cleanup API conventions, partitions can be
defined and initialized in a single statement rather than initialized to
NULL and assigned separately.

[ ... ]

> +static void arbitration_retry_pending_requests(struct panthor_arbitration *adev, u32 mask)
> +{
> +	unsigned long aw_mask = mask;
> +	u8 aw_id;
> +
> +	for_each_set_bit(aw_id, &aw_mask, AM_ARB_MAX_AW_COUNT) {
> +		int ret;
> +
> +		if (!test_and_clear_bit(aw_id, adev->pending_aw_request_mask))
> +			continue;
> +
> +		ret = panthor_arbitration_on_request(adev, aw_id);
> +		if (ret && ret != -EEXIST)
> +			set_bit(aw_id, adev->pending_aw_request_mask);
> +	}
> +}
> +
> +static int arbitration_resource_init(struct panthor_arbitration *adev)
> +{
> +	struct panthor_arbitration_dt_info *dt_info = &adev->dt_info;
> +
> +	/*
> +	 * If arm,access-window-mask is not defined or set to 0, assign AWs to
> +	 * PC0 by default.
> +	 */
> +	if (!dt_info->default_aw_masks[0])
> +		dt_info->default_aw_masks[0] = AM_ARB_MAX_AW_MASK;
> +
> +	for (int i = 0; i < AM_ARB_MAX_PC_COUNT; i++) {
> +		unsigned long pc_aw_mask = dt_info->default_aw_masks[i];
> +		u8 aw_id;
> +		int ret;
> +
> +		if (!adev->pc[i] || !adev->sched[i])
> +			continue;
> +
> +		scoped_guard(spinlock_irqsave, &adev->assignment_lock) {
> +			for_each_set_bit(aw_id, &pc_aw_mask, AM_ARB_MAX_AW_COUNT)
> +				adev->aw_sched_map[aw_id] = i;
> +		}
> +
> +		ret = arbitration_sched_try_start(adev, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	arbitration_retry_pending_requests(adev, AM_ARB_MAX_AW_MASK);
> +
> +	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 idx;
> +
> +	guard(spinlock_irqsave)(&adev->assignment_lock);
> +
> +	idx = to_assigned_sched_idx(adev, aw_id);
> +
> +	if (idx == -ENOENT) {
> +		set_bit(aw_id, adev->pending_aw_request_mask);
> +
> +		return 0;
> +	} else if (idx < 0) {
> +		return idx;
> +	}
> +
> +	return panthor_arbitration_sched_on_request(adev->sched[idx], aw_id);
>  }

[Severity: Medium]
Can a request interrupt arriving between publishing adev->aw_sched_map[] and
arbitration_retry_pending_requests() reorder or duplicate AW requests?

panthor_resource_group_init() enables Resource Group interrupts and starts
the AW handshake before arbitration_resource_init() runs.  If an AW sends
VM_ARB_GPU_REQUEST before adev->aw_sched_map[] is populated,
panthor_arbitration_on_request() sets its bit in pending_aw_request_mask.
arbitration_resource_init() then populates adev->aw_sched_map[], drops
assignment_lock, and starts the scheduler before draining
pending_aw_request_mask:

CPU0                                  <hardirq>
arbitration_resource_init()
                                      rg_irq_handler()
                                        ...
                                          panthor_arbitration_on_request(aw0)
                                            set_bit(0, pending_aw_request_mask)
  lock(&adev->assignment_lock)
  adev->aw_sched_map[aw_id] = i
  unlock(&adev->assignment_lock)
  arbitration_sched_try_start()
                                      rg_irq_handler()
                                        ...
                                          panthor_arbitration_on_request(aw0)
                                            panthor_arbitration_sched_on_request()
  arbitration_retry_pending_requests()
    test_and_clear_bit(0, ...)
    panthor_arbitration_on_request(aw0)
      panthor_arbitration_sched_on_request()  <-- duplicate replay

Because panthor_arbitration_on_request() does not clear aw_id in
pending_aw_request_mask when idx >= 0, a resent request (such as from
panthor_aw_handshake_handle() on ARB_VM_INIT) is queued once by the
interrupt handler and replayed a second time by
arbitration_retry_pending_requests(), triggering a spurious warning in
panthor_arbitration_sched_on_request() or an extra GPU grant if the AW has
already entered ARB_SCHED_PHASE_STOPPING or ARB_SCHED_PHASE_IDLE.  A request
from a different AW in that window also bypasses the earlier pending request
and is granted the GPU first.

Replaying pending requests under assignment_lock when populating
adev->aw_sched_map[] (before starting the scheduler), or clearing
pending_aw_request_mask in panthor_arbitration_on_request(), would avoid the
race.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=19

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present
  2026-11-10 15:38 ` [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
@ 2026-11-10 16:03   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:03 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/panthor: panthor_devfreq_init() leaves fast_rate at 0 on
  early return
--

commit f3bfa3d1d539b4b1bfaa2d96f490e98746dad160
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Skip devfreq when no OPP table is present

This commit skips devfreq initialization on Mali v15 AM systems when no OPP
table is present, guards the devfreq helpers against a NULL devfreq
instance, and falls back to clk_get_rate() when querying the current
frequency without devfreq.

> diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
> index 2249b41ca4afe..d1814b5197221 100644
> --- a/drivers/gpu/drm/panthor/panthor_devfreq.c
> +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c

[ ... ]

> @@ -164,6 +165,19 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
>  	 */
>  	table = dev_pm_opp_get_opp_table(dev);
>  	if (IS_ERR_OR_NULL(table)) {
> +		/*
> +		 * On v15 GPUs, frequency scaling is handled outside of panthor
> +		 * by the AM_GOVERNOR block, so the GPU DT node may not provide
> +		 * an OPP table. In such cases, do not initialize devfreq for
> +		 * panthor.
> +		 */
> +		if (panthor_hw_is_gen5_am(ptdev->base.dev) &&
> +		    PTR_ERR(table) == -ENODEV) {
> +			drmm_kfree(&ptdev->base, pdevfreq);
> +			ptdev->devfreq = NULL;
> +			return 0;
> +		}

[Severity: Medium]
As reported by Steven Price on RFC [1], does returning early here leave
ptdev->fast_rate at 0?  ptdev->fast_rate is only populated later in
panthor_devfreq_init() via dev_pm_opp_find_freq_floor(), and
panthor_gpu_show_fdinfo() unconditionally prints it:

drivers/gpu/drm/panthor/panthor_drv.c:panthor_gpu_show_fdinfo() {
	...
	drm_printf(p, "drm-maxfreq-panthor:\t%lu Hz\n", ptdev->fast_rate);
	drm_printf(p, "drm-curfreq-panthor:\t%lu Hz\n",
		   panthor_devfreq_get_freq(ptdev));
}

On v15 AM systems without an OPP table, reading fdinfo will report
"drm-maxfreq-panthor: 0 Hz" alongside a non-zero current frequency,
breaking userspace GPU utilization calculations that divide cycle counts
by drm-maxfreq-panthor.

[1] https://lore.kernel.org/r/dc038a81-3c59-49d5-ab8a-29202c7ce935@arm.com

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=10

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 17/28] drm/panthor: Add arbitration scheduler
  2026-11-10 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
@ 2026-11-10 16:05   ` sashiko-bot
  2026-11-11  0:43   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:05 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] drm/panthor: infinite reset loop in arb_sched_reset_work() on
  close failure
- [High] drm/panthor: dropped GPU request in
  panthor_arbitration_sched_on_request()
- [Medium] drm/panthor: unbounded retry loop in arb_sched_retry_work()
  on -EAGAIN
- [Medium] drm/panthor: timer re-armed by flush_workqueue() during
  scheduler stop
--

commit 3de6cc524258ad93c9b3ccfc5bc67b23efebd6b2
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add arbitration scheduler

This commit adds a per-partition arbitration scheduler that queues GPU
requests from access windows, grants access in turn, and uses hrtimers and
workqueues to manage timeslices, yields, force-closes, and error recovery.

> 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 0000000000000..23768739e9ef5
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
[ ... ]
> +static int __arb_sched_close_locked(struct panthor_arbitration_sched *sched)
> +{
> +	int ret;
> +
> +	lockdep_assert_held(&sched->lock);
> +
> +	ret = panthor_arbitration_on_close(to_adev(sched), sched->id);
> +	if (ret == -EAGAIN) {
> +		arb_sched_queue_retry_work(sched, PANTHOR_ARB_WORK_CLOSE);
> +	} else if (ret < 0) {
> +		dev_err(sched->dev, "%s: Failed to close AW%u. %d", sched->name,
> +			sched->active_aw_id, ret);
> +
> +		arb_sched_queue_work(sched, &sched->reset_work);
> +	}
> +
> +	return ret;
> +}
> +
> +static void arb_sched_close_locked(struct panthor_arbitration_sched *sched)
> +{
> +	int ret;
> +
> +	lockdep_assert_held(&sched->lock);
> +
> +	ret = __arb_sched_close_locked(sched);
> +	if (!ret) {
> +		sched->close_timer.epoch = sched->epoch;
> +		hrtimer_start(&sched->close_timer.timer,
> +			      ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
> +	}
> +}
[ ... ]
> +static void arb_sched_reset_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, reset_work);
> +
> +	guard(spinlock_irqsave)(&sched->lock);
> +	if (sched->epoch != w->epoch)
> +		return;
> +
> +	if (sched->active_aw_id != w->aw_id)
> +		return;
> +
> +	dev_warn(sched->dev, "%s: scheduler reset. active_aw=%u",
> +			sched->name, sched->active_aw_id);
> +
> +	arb_sched_force_close_locked(sched);
> +}

[Severity: High]
Can arb_sched_reset_work() enter an infinite reset loop when closing an
access window fails or times out?

__arb_sched_close_locked() and panthor_arbitration_sched_on_close_timeout()
queue sched->reset_work to recover from close errors and close timeouts.
However, arb_sched_reset_work() does not retire the lease or advance
sched->epoch (note that arb_sched_on_stopped_locked() has an unused forward
declaration at the top of the file); it only calls
arb_sched_force_close_locked() -> arb_sched_close_locked().

If panthor_arbitration_on_close() returns a negative error other than
-EAGAIN, __arb_sched_close_locked() immediately re-queues sched->reset_work
in a tight loop on sched->wq, hanging flush_workqueue() during suspend or
teardown.  If panthor_arbitration_on_close() returns 0 when close_timer has
already timed out, arb_sched_close_locked() simply re-arms close_timer,
which expires and queues sched->reset_work again, wedging the scheduler in
ARB_SCHED_PHASE_LOST and starving all queued access windows.

> +static void arb_sched_retry_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, retry_work);
> +	DECLARE_BITMAP(work_tmp, PANTHOR_ARB_WORK_COUNT);
> +
> +	guard(spinlock_irqsave)(&sched->lock);
> +	if (bitmap_empty(sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT))
> +		return;
> +
> +	bitmap_copy(work_tmp, sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT);
> +	bitmap_zero(sched->pending_retry_work, PANTHOR_ARB_WORK_COUNT);
> +
> +	if (sched->epoch != w->epoch || sched->active_aw_id != w->aw_id)
> +		return;
> +
> +	switch (sched->phase) {
> +	case ARB_SCHED_PHASE_STOPPING:
> +		if (test_bit(PANTHOR_ARB_WORK_STOP, work_tmp))
> +			arb_sched_stop_locked(sched);
> +		break;
> +	case ARB_SCHED_PHASE_CLOSING:
> +	case ARB_SCHED_PHASE_LOST:
> +		if (test_bit(PANTHOR_ARB_WORK_CLOSE, work_tmp))
> +			arb_sched_close_locked(sched);
> +		break;
> +	default:
> +		break;
> +	}
> +}

[Severity: Medium]
Can arb_sched_retry_work() spin indefinitely on sched->wq if
panthor_arbitration_on_stop() or panthor_arbitration_on_close() continues to
return -EAGAIN?

When __arb_sched_stop_locked() or __arb_sched_close_locked() gets -EAGAIN,
stop_timer and close_timer are not started, and sched->retry_work is queued
immediately with no delay or retry limit.  If the partition remains in a
state that returns -EAGAIN, arb_sched_retry_work() clears pending_retry_work
and re-queues itself in an unthrottled busy loop with no running timer to
force recovery, which also prevents flush_workqueue(sched->wq) from
completing in panthor_arbitration_sched_stop().

[ ... ]
> +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;
> +	}

[Severity: High]
Does panthor_arbitration_sched_on_request() drop valid GPU requests that
arrive while the active access window is closing?

When the active access window yields via panthor_arbitration_sched_on_idle()
(or is force-closed), sched->phase moves to ARB_SCHED_PHASE_CLOSING (or
ARB_SCHED_PHASE_LOST) while sched->active_aw_id remains set until the
partition reset completes and calls panthor_arbitration_sched_on_stopped().
Because the duplicate check only exempts ARB_SCHED_PHASE_STOPPING, a new
request from that access window during ARB_SCHED_PHASE_CLOSING or
ARB_SCHED_PHASE_LOST is rejected with -EEXIST and never queued, leaving the
access window unscheduled once panthor_arbitration_sched_on_stopped() clears
active_aw_id.

[ ... ]
> +int panthor_arbitration_sched_stop(struct panthor_arbitration_sched *sched)
> +{
> +	int ret;
> +
> +	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,
> +				   panthor_arbitration_stop_timeout(sched));
> +	if (ret) {
> +		/*
> +		 * TODO: handle the timeout here by escalating the error when
> +		 * reset escalation is supported.
> +		 */
> +		ret = arb_sched_force_close(sched);
> +	}
> +
> +	/* Should no longer have any further activity. Cancel timer. */
> +	hrtimer_cancel(&sched->grant_timer.timer);
> +	hrtimer_cancel(&sched->stop_timer.timer);
> +	hrtimer_cancel(&sched->close_timer.timer);
> +	flush_workqueue(sched->wq);

[Severity: Medium]
Can flush_workqueue(sched->wq) re-arm stop_timer or close_timer after the
timers have already been cancelled?

hrtimer_cancel() runs before flush_workqueue(sched->wq).  If
sched->reset_work or sched->retry_work is pending (for example, queued by
panthor_arbitration_sched_on_close_timeout() during arb_sched_force_close()),
flushing sched->wq runs arb_sched_reset_work() or arb_sched_retry_work(),
which calls arb_sched_close_locked() or arb_sched_stop_locked() and starts
close_timer or stop_timer again, leaving an active hrtimer after
panthor_arbitration_sched_stop() returns.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=17

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support
  2026-11-10 15:38 ` [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
@ 2026-11-10 16:07   ` sashiko-bot
  2026-11-11  8:30   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:07 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/panthor: lost AW message interrupt in
  rg_irq_raw_handler()
--

commit 4a3c9d76bb48fcfdcf16aaac9521c7316fca8936
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add AM_RESOURCE_GROUP support

This commit adds resource-group support to the Panthor arbitration driver,
including MMIO and IRQ setup, per-access-window message handling, handshake
negotiation, and runtime PM callbacks.

> 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 0000000000000..e413cfd9e6d25
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c

[ ... ]

> +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;
> +
> +	scoped_guard(spinlock_irqsave, &rg->irq_lock) {
> +		if (rg->irq_suspended)
> +			return IRQ_NONE;
> +	}
> +
> +	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_drain_pending_messages(struct panthor_resource_group *rg)
> +{
> +	unsigned long message_mask;
> +	u8 aw_id;
> +
> +	if (pm_runtime_status_suspended(rg->dev))
> +		return;
> +
> +	message_mask = gpu_read(rg->iomem, AM_RG_IRQ_RAWSTAT) & AM_RG_MESSAGE_MASK;
> +
> +	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, message_mask);
> +}

[Severity: Medium]
Can writing AM_RG_IRQ_CLEAR after the loop in rg_irq_raw_handler() and
rg_drain_pending_messages() clear a follow-up access-window interrupt
before its message is read?

When rg_handle_message() processes an unacknowledged VM_ARB_INIT from an
access window, rg_respond_to_handshake() immediately sends ARB_VM_INIT back
to that access window before the loop finishes and writes AM_RG_IRQ_CLEAR:

CPU0 (resource group)             CPU1 / VM (access window aw_id)
rg_irq_raw_handler()
  status = gpu_read(AM_RG_IRQ_STATUS)
  panthor_am_msg_read()
  rg_handle_message()
    rg_respond_to_handshake()
      rg_send_init_msg(ARB_VM_INIT)
                                  receives ARB_VM_INIT
                                  sends VM_ARB_GPU_REQUEST
                                  (asserts BIT(aw_id) in AM_RG_IRQ_RAWSTAT)
  gpu_write(AM_RG_IRQ_CLEAR, status)
    <-- clears BIT(aw_id) without reading VM_ARB_GPU_REQUEST

If the hardware also keeps the sender's AM_OUTGOING_MESSAGE_STATUS busy
until AM_RG_IRQ_CLEAR is written, delaying the clear until after
rg_handle_message() leaves the access window's outgoing slot busy while it
handles the reply.  In either case, a missed VM_ARB_GPU_REQUEST or
VM_ARB_GPU_STOPPED stalls GPU access acquisition or partition scheduling
until the request timeout expires.

Clearing BIT(aw_id) in AM_RG_IRQ_CLEAR right after panthor_am_msg_read()
and before rg_handle_message() would avoid this race.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=16

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support
  2026-11-10 15:38 ` [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
@ 2026-11-10 16:10   ` sashiko-bot
  2026-11-11  5:43   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:10 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Medium] drm/panthor: TOCTOU race from multiple AM_PART_STATE reads in
  window helpers
- [Medium] drm/panthor: panthor_partition_control_term() fails on
  in-progress state transitions
- [Low] drm/panthor: unused devm allocations in partition_control_init()
  on -ENODEV
- [Low] drm/panthor: unchecked devm_kasprintf() in
  partition_control_init()
--

commit 9f7b5a10199859069acf5b5904f5b9aca326fc89
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add AM_PARTITION_CONTROL support

This commit adds the Panthor arbitration platform driver and partition
control support to manage access windows on Mali v15 AM GPUs.

Link: https://lore.kernel.org/all/20260922205802.1AC3E1F000FF@smtp.kernel.org/

> 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 0000000000000..330d0bf5fd375
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c

[ ... ]

> +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 yield_now(struct panthor_partition_control *pc)
> +{
> +	guard(spinlock_irqsave)(&pc->lock);
> +
> +	if (partition_state_get(pc) == PART_STATE_RESET)
> +		return 0;
> +
> +	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
> +		return -EAGAIN;
> +
> +	gpu_write(pc->iomem, AM_PART_COMMAND,
> +		  AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW));
> +
> +	return 0;
> +}
> +
> +static int yield_idle(struct panthor_partition_control *pc)
> +{
> +	guard(spinlock_irqsave)(&pc->lock);
> +
> +	if (partition_state_get(pc) == PART_STATE_RESET)
> +		return 0;
> +
> +	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
> +		return -EAGAIN;
> +
> +	gpu_write(pc->iomem, AM_PART_COMMAND,
> +		  AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE));
> +
> +	return 0;
> +}
> +
> +static int window_close(struct panthor_partition_control *pc)
> +{
> +	guard(spinlock_irqsave)(&pc->lock);
> +
> +	if (partition_state_get(pc) == PART_STATE_RESET)
> +		return 0;
> +
> +	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
> +		return -EAGAIN;
> +
> +	pc->closing = true;
> +
> +	gpu_write(pc->iomem, AM_PART_COMMAND,
> +		  AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW));
> +
> +	return 0;
> +}
> +
> +static int window_open(struct panthor_partition_control *pc, u8 aw_id)
> +{
> +	if (aw_id >= AM_ARB_MAX_AW_COUNT)
> +		return -EINVAL;
> +
> +	guard(spinlock_irqsave)(&pc->lock);
> +
> +	if (partition_state_get(pc) == PART_STATE_WINDOW_OPEN) {
> +		u8 cur_aw = partition_aw_get(pc);
> +
> +		if (cur_aw == aw_id)
> +			return 0;
> +
> +		dev_warn(pc->dev,
> +			 "%s: OPEN_WINDOW(%u): Already opened for aw=%u, state=%u. Closing...",
> +			 pc->name, aw_id, cur_aw, partition_state_get(pc));
> +
> +		return -EBUSY;
> +	}

[Severity: Medium]
Can an asynchronous hardware state transition between these back-to-back
AM_PART_STATE reads cause yield_now(), yield_idle(), window_close(), and
window_open() to act on inconsistent state?

Each call to partition_state_get() and partition_aw_get() performs a
separate MMIO read of AM_PART_STATE.  If the partition finishes closing or
yielding and transitions into PART_STATE_RESET between the first and
second partition_state_get() in yield_now(), yield_idle(), or
window_close(), the first check is false and the second sees
PART_STATE_RESET != PART_STATE_WINDOW_OPEN, returning -EAGAIN instead of 0
(which in panthor_partition_control_term() triggers WARN_ON() and skips
partition_state_wait()).  In window_open(), if the partition resets
between partition_state_get() and partition_aw_get(), cur_aw reads as 0;
when aw_id is 0, window_open() returns 0 without issuing
PART_CMD_OPEN_WINDOW.

Also, the dev_warn() in window_open() performs a third MMIO read of
AM_PART_STATE and still says "Closing..." even though window_open() no
longer closes the window.  Reading AM_PART_STATE once into a local
variable under pc->lock and extracting both fields from that value would
avoid these races.

[ ... ]

> +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;
> +
> +		partition_irq_suspend(pc);
> +
> +		if (!pm_runtime_status_suspended(pc->dev))
> +			if (!WARN_ON(window_close(pc) < 0))
> +				WARN_ON(partition_state_wait(pc, PART_STATE_RESET));
> +	}
> +}

[Severity: Medium]
What happens in panthor_partition_control_term() if a partition is already
transitioning through PART_STATE_WINDOW_CLOSED or
PART_STATE_WINDOW_OPENING when teardown runs?

window_open(), window_close(), yield_now(), and yield_idle() issue
commands without waiting for the state transition to complete, and
window_close() returns -EAGAIN for any state other than PART_STATE_RESET
and PART_STATE_WINDOW_OPEN.  If a transition is still in flight when
panthor_partition_control_term() runs, window_close() returns -EAGAIN,
triggering WARN_ON() and skipping partition_state_wait() while the
hardware is still active.  Commit "drm/panthor: Reset partitions before
starting arbitration" later in this series adds partition_ensure_reset()
to handle in-progress states during init, so calling it here as well would
ensure teardown waits for the reset to complete.

> +static int partition_control_init(struct panthor_arbitration *adev, int i)
> +{
> +	struct panthor_partition_control *pc;
> +	struct device *dev = adev->dev;
> +	struct irq_data *irqdata;
> +	struct resource *res;
> +	void __iomem *iomem;
> +	u32 irq_flags = 0;
> +	char *name;
> +	int irq;
> +	int ret;
> +
> +	if (adev->pc[i])
> +		return dev_warn_probe(dev, -ENODEV,
> +				      "partition %u already present", i);
> +
> +	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
> +	if (!pc)
> +		return -ENOMEM;
> +
> +	name = devm_kasprintf(dev, GFP_KERNEL, "pc%d", i);
> +	if (!name)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource_byname(to_platform_device(dev),
> +					   IORESOURCE_MEM, name);
> +	if (!res)
> +		return -ENODEV;

[Severity: Low]
When platform_get_resource_byname() finds no resource and returns -ENODEV,
panthor_partition_control_init() ignores -ENODEV and probe succeeds,
leaving pc and name allocated on dev until the driver unbinds.  Could
pc be allocated after looking up res (or the unused devm allocations be
freed before returning -ENODEV)?

[ ... ]

> +	ret = devm_request_irq(
> +		dev, irq, partition_irq_raw_handler, irq_flags | IRQF_SHARED,
> +		devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), pc);
> +	if (ret)
> +		return ret;

[Severity: Low]
Can devm_kasprintf() return NULL here and pass a NULL devname into
devm_request_irq() instead of failing with -ENOMEM?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=14

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle
  2026-11-10 15:38 ` [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle Karunika Choo
@ 2026-11-10 16:15   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:15 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] drm/panthor: dropped YIELD_IDLE command in
  arb_sched_next_locked()
- [Medium] drm/panthor: dropped GPU_REQUEST in
  panthor_arbitration_sched_on_request()
--

commit f99f87c6e1d4cdf3c37b5e1e28569f0fc943d72a
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Request AW to yield GPU access on idle

This commit sends a YIELD_IDLE notification to the active access window
when other access windows are waiting in the arbitration scheduler queue
or when a second access window requests GPU access.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
> index 16bd0fac2514e..f137b73deaa32 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c
[ ... ]
> @@ -382,6 +394,10 @@ static int arb_sched_next_locked(struct panthor_arbitration_sched *sched)
>  
>  	arb_sched_grant_locked(sched);
>  
> +	if (sched->phase == ARB_SCHED_PHASE_GRANTED &&
> +	    !kfifo_is_empty(&sched->queue))
> +		arb_sched_yield_idle_locked(sched);
> +
>  	wake_up_all(&sched->waitqueue);
>  
>  	return 0;

[Severity: Medium]
Can arb_sched_yield_idle_locked() silently drop the YIELD_IDLE command
when called immediately after arb_sched_grant_locked()?

arb_sched_grant_locked() calls window_open(), which writes
PART_CMD_OPEN_WINDOW to AM_PART_COMMAND and returns 0 without waiting for
AM_PART_STATE to leave PART_STATE_RESET.  When arb_sched_yield_idle_locked()
then calls yield_idle(), AM_PART_STATE can still read as PART_STATE_RESET:

drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:yield_idle() {
	guard(spinlock_irqsave)(&pc->lock);

	if (partition_state_get(pc) == PART_STATE_RESET)
		return 0;

	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
		return -EAGAIN;
    ...
}

Because yield_idle() returns 0 instead of -EAGAIN in PART_STATE_RESET,
arb_sched_yield_idle_locked() does not queue PANTHOR_ARB_WORK_YIELD_IDLE,
and the active access window holds the GPU even while idle until
grant_timer expires.

[ ... ]

> @@ -551,8 +573,18 @@ int panthor_arbitration_sched_on_request(struct panthor_arbitration_sched *sched
>  	 * Grant timer for the current AW has previously expired, request it to
>  	 * yield the GPU.
>  	 */
> -	if (sched->grant_previously_expired)
> +	if (sched->grant_previously_expired) {
>  		arb_sched_request_stop_locked(sched);
> +		return 0;
> +	}
> +
> +	/*
> +	 * GPU was previously granted to a lone requesting AW, YIELD_IDLE was
> +	 * not sent to it. Send YIELD_IDLE to it now that multiple AWs are
> +	 * requesting for the GPU.
> +	 */
> +	if (sched->phase == ARB_SCHED_PHASE_GRANTED && kfifo_len(&sched->queue) == 1)
> +		arb_sched_yield_idle_locked(sched);
>  
>  	return 0;
>  }

[Severity: Medium]
Will panthor_arbitration_sched_on_request() drop a new GPU_REQUEST from an
access window that just yielded due to YIELD_IDLE?

Unlike arb_sched_request_stop_locked(), which sets sched->phase to
ARB_SCHED_PHASE_STOPPING before sending PART_CMD_YIELD_NOW, sending
PART_CMD_YIELD_IDLE leaves sched->phase as ARB_SCHED_PHASE_GRANTED.  When
the active access window yields on idle, it receives
WINDOW_IRQ_WINDOW_CLOSED and can issue a new VM_ARB_GPU_REQUEST before the
partition control IRQ processes PART_RESET_DONE and calls
panthor_arbitration_on_stopped():

drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:panthor_arbitration_sched_on_request() {
    ...
	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;
	}
    ...
}

Because sched->active_aw_id is still aw_id and sched->phase is still
ARB_SCHED_PHASE_GRANTED, panthor_arbitration_sched_on_request() rejects
the request with -EEXIST, leaving the access window to time out in
panthor_aw_request().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=21

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt
  2026-11-10 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
@ 2026-11-10 16:16   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:16 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] drm/panthor: unassigned current_aw in window_open() breaks
  recovery
- [Medium] drm/panthor: stale last_cmd on PART_STATE_RESET in
  yield/close helpers
--

commit 94f33b60c028012f4fc4c4edbb429977e08e2557
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Handle partition control INVALID_COMMAND interrupt

This commit tracks the last sent partition command and handles
PART_INVALID_COMMAND interrupts by retrying rejected commands or
requesting scheduler recovery when the partition is open for a different
access window.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> index d8fdafbf31864..3b472403f49d1 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
[ ... ]
> @@ -128,8 +158,8 @@ static int yield_now(struct panthor_partition_control *pc)
>  	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
>  		return -EAGAIN;
>  
> -	gpu_write(pc->iomem, AM_PART_COMMAND,
> -		  AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW));
> +	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_YIELD_NOW),
> +				pc->current_aw);
>  
>  	return 0;
>  }
> @@ -144,8 +174,8 @@ static int yield_idle(struct panthor_partition_control *pc)
>  	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
>  		return -EAGAIN;
>  
> -	gpu_write(pc->iomem, AM_PART_COMMAND,
> -		  AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE));
> +	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_YIELD_IDLE),
> +				pc->current_aw);
>  
>  	return 0;
>  }
> @@ -162,8 +192,8 @@ static int window_close(struct panthor_partition_control *pc)
>  
>  	pc->closing = true;
>  
> -	gpu_write(pc->iomem, AM_PART_COMMAND,
> -		  AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW));
> +	partition_command_write(pc, AM_PART_SET_COMMAND(PART_CMD_CLOSE_WINDOW),
> +				pc->current_aw);
>  
>  	return 0;
>  }

[Severity: Medium]
Can a stale PART_CMD_OPEN_WINDOW in pc->last_cmd reopen the partition
after yield_now(), yield_idle(), or window_close() has already returned 0?

window_open() writes PART_CMD_OPEN_WINDOW, stores it in pc->last_cmd, and
returns 0 without waiting for AM_PART_STATE to leave PART_STATE_RESET.  If
PART_CMD_OPEN_WINDOW is rejected while a previous reset is still finishing
(or before the hardware leaves PART_STATE_RESET) and the scheduler then
calls yield_now(), yield_idle(), or window_close(), those helpers see
partition_state_get(pc) == PART_STATE_RESET and return 0 without clearing
pc->last_cmd or scheduling retry_work:

drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:yield_now() {
	guard(spinlock_irqsave)(&pc->lock);

	if (partition_state_get(pc) == PART_STATE_RESET)
		return 0;

When partition_handle_invalid_cmd() later runs for the rejected open
command, pc->last_cmd.cmd is still PART_CMD_OPEN_WINDOW and the partition
is not in PART_STATE_WINDOW_OPEN, so it re-issues PART_CMD_OPEN_WINDOW and
opens the access window after the yield or close already succeeded.

> @@ -188,8 +218,8 @@ static int window_open(struct panthor_partition_control *pc, u8 aw_id)
>  		return -EBUSY;
>  	}
>  
> -	gpu_write(pc->iomem, AM_PART_COMMAND,
> -		  AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) | AM_PART_SET_WINDOW(aw_id));
> +	partition_command_write(pc, (AM_PART_SET_COMMAND(PART_CMD_OPEN_WINDOW) |
> +				     AM_PART_SET_WINDOW(aw_id)), aw_id);
>  
>  	pc->current_aw = aw_id;
>  
> @@ -201,10 +231,16 @@ static void partition_handle_reset_done(struct panthor_partition_control *pc)
>  	bool notify_stopped = false;
>  	int aw_id;
>  
> +	gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, PART_RESET_DONE);
> +
>  	scoped_guard(spinlock_irqsave, &pc->lock) {
>  		aw_id = pc->current_aw;
>  		pc->current_aw = AW_UNASSIGNED;
>  
> +		/* invalidate last command */
> +		pc->last_cmd.aw_id = AW_UNASSIGNED;
> +		pc->last_cmd.cmd = 0;
> +
>  		/* RESET_DONE from CLOSE_WINDOW */
>  		if (pc->closing)
>  			pc->closing = false;
> @@ -216,6 +252,55 @@ static void partition_handle_reset_done(struct panthor_partition_control *pc)
>  		panthor_arbitration_on_stopped(dev_get_drvdata(pc->dev), aw_id);
>  }
>  
> +static void partition_handle_invalid_cmd(struct panthor_partition_control *pc)
> +{
> +	bool on_err = false;
> +	int aw_id;
> +
> +	gpu_write(pc->iomem, AM_PART_IRQ_CLEAR, PART_INVALID_COMMAND);
> +
> +	scoped_guard(spinlock_irqsave, &pc->lock) {
> +		if (!pc->last_cmd.cmd)
> +			return;
> +
> +		if (pc->last_cmd.aw_id < 0)
> +			return;

[Severity: Medium]
Does leaving pc->current_aw as AW_UNASSIGNED (-1) when window_open() finds
the partition in PART_STATE_WINDOW_OPEN prevent command retries and
scheduler recovery?

When window_open() sees PART_STATE_WINDOW_OPEN, it returns 0 (if
cur_aw == aw_id) or -EBUSY (if cur_aw != aw_id) before setting
pc->current_aw = aw_id:

drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:window_open() {
	if (partition_state_get(pc) == PART_STATE_WINDOW_OPEN) {
		u8 cur_aw = partition_aw_get(pc);

		if (cur_aw == aw_id)
			return 0;

		dev_warn(pc->dev,
			 "%s: OPEN_WINDOW(%u): Already opened for aw=%u, state=%u. Closing...",
			 pc->name, aw_id, cur_aw, partition_state_get(pc));

		return -EBUSY;
	}

On -EBUSY, arb_sched_grant_locked() queues sched->reset_work, which calls
arb_sched_force_close_locked() -> arb_sched_close_locked() ->
window_close().  Because pc->current_aw is still -1, window_close() (as
well as yield_now() and yield_idle() when cur_aw == aw_id) records
pc->last_cmd.aw_id = -1.  If that command raises PART_INVALID_COMMAND,
partition_handle_invalid_cmd() aborts at pc->last_cmd.aw_id < 0 without
retrying it.  Furthermore, when PART_RESET_DONE later fires,
partition_handle_reset_done() sees aw_id < 0 and skips
panthor_arbitration_on_stopped(), leaving the scheduler stuck in
ARB_SCHED_PHASE_LOST.

In addition, panthor_partition_control_term() calls
partition_irq_suspend() before window_close() and partition_state_wait(),
which masks PART_INVALID_COMMAND so a rejected close command cannot be
retried during teardown.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=20

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 18/28] drm/panthor: Route arbitration events
  2026-11-10 15:38 ` [PATCH v2 18/28] drm/panthor: Route arbitration events Karunika Choo
@ 2026-11-10 16:19   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:19 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] drm/panthor: unhandled RESET and current_aw states wedge
  arbitration
- [Medium] drm/panthor: dropped GPU_REQUEST in
  panthor_arbitration_sched_on_request()
--

commit 48a85078af75aa6fff27b7d739d44d8c4f545271
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Route arbitration events

This commit wires arbitration events between resource-group message handling,
the arbitration scheduler, and partition control.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> index 330d0bf5fd375..d8fdafbf31864 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> @@ -198,13 +198,22 @@ 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 = AW_UNASSIGNED;
>  
>  		/* RESET_DONE from CLOSE_WINDOW */
>  		if (pc->closing)
>  			pc->closing = false;
> +		if (aw_id >= 0)
> +			notify_stopped = true;
>  	}
> +
> +	if (notify_stopped)
> +		panthor_arbitration_on_stopped(dev_get_drvdata(pc->dev), aw_id);
>  }

[Severity: High]
Can relying solely on partition_handle_reset_done() with pc->current_aw >= 0
drop partition commands or wedge the arbitration scheduler in an infinite
close loop?

First, window_open() performs a posted write of PART_CMD_OPEN_WINDOW, sets
pc->current_aw = aw_id, and returns 0 before AM_PART_STATE leaves
PART_STATE_RESET. If the scheduler immediately follows up with a stop,
yield, or close command (for example when sched->disabled is set in
arb_sched_grant_locked(), or when another request is queued), yield_now()
and window_close() see partition_state_get(pc) == PART_STATE_RESET and
return 0 without issuing a command or returning -EAGAIN:

drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:window_close() {
	...
	if (partition_state_get(pc) == PART_STATE_RESET)
		return 0;

	if (partition_state_get(pc) != PART_STATE_WINDOW_OPEN)
		return -EAGAIN;
	...
}

Because 0 is returned while pc->current_aw >= 0, the command is silently
dropped instead of being retried once the window finishes opening.

Second, if partition_state_get(pc) is already PART_STATE_WINDOW_OPEN on
entry to window_open(), it returns 0 (when cur_aw == aw_id) or -EBUSY (which
triggers reset_work -> arb_sched_force_close_locked()) before setting
pc->current_aw = aw_id. When PART_RESET_DONE later fires, pc->current_aw is
still AW_UNASSIGNED, so partition_handle_reset_done() skips
panthor_arbitration_on_stopped().

Third, whenever the hardware partition is already in PART_STATE_RESET while
sched->active_aw_id >= 0 (such as after a skipped
panthor_arbitration_on_stopped() notification), arb_sched_close_locked()
calls window_close(), gets 0 back without any PART_CMD_CLOSE_WINDOW or
stopped notification, and starts close_timer. When close_timer expires,
reset_work calls arb_sched_force_close_locked() -> arb_sched_close_locked(),
repeating indefinitely and wedging GPU scheduling across all access windows.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
> index e413cfd9e6d25..26b22461f6d39 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c
> @@ -163,10 +163,10 @@ static void rg_handle_message(struct panthor_resource_group *rg, u8 aw_id,
>  
>  	switch (msg_id) {
>  	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;

[Severity: Medium]
Can a VM_ARB_GPU_REQUEST sent right after VM_ARB_GPU_STOPPED be rejected
with -EEXIST and lost while the partition is still closing?

Receiving VM_ARB_GPU_STOPPED calls panthor_arbitration_sched_on_idle(),
which sets sched->phase = ARB_SCHED_PHASE_CLOSING and starts an asynchronous
PART_CMD_CLOSE_WINDOW while keeping sched->active_aw_id == aw_id until
PART_RESET_DONE fires on the partition control IRQ. Because the access
window receives WINDOW_IRQ_WINDOW_CLOSED as soon as the window closes (prior
to PART_RESET_DONE) and the resource group and partition control use
separate interrupts, the access window can send a new VM_ARB_GPU_REQUEST
while sched->phase is still ARB_SCHED_PHASE_CLOSING.

In panthor_arbitration_sched_on_request(), only ARB_SCHED_PHASE_STOPPING is
exempted when checking whether the active access window may re-queue itself:

drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:panthor_arbitration_sched_on_request() {
	...
	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;
	}
	...
}

The new request is dropped, and once PART_RESET_DONE completes, the
scheduler becomes idle without granting the GPU back to the access window,
stalling it until its request timeout expires. Allowing re-queuing when
sched->phase == ARB_SCHED_PHASE_CLOSING as well would avoid dropping the
request.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=18

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 22/28] drm/panthor: Add access-window support
  2026-11-10 15:38 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
@ 2026-11-10 16:20   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:20 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [High] drm/panthor: uncanceled msg_retry_work in panthor_aw_suspend()
  and resume
- [Medium] drm/panthor: zero-delay workqueue busy loop in
  panthor_aw_msg_retry_work()
- [Medium] drm/panthor: unhandled INIT and GPU_REQUEST states in
  __panthor_aw_suspend()
- [Medium] drm/panthor: unhandled YIELDING timeout and INIT wait in
  panthor_aw
- [Low] drm/panthor: unchecked version in panthor_aw_handshake_handle()
  resend path
--

commit 2a64ca6e72bd954e55857187b10a1459415f36df
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Add access-window support

This commit adds the VM-side access-window component for Mali v15 GPUs to
negotiate the AM message protocol, request and yield GPU access from the
arbiter, and defer reading hardware information until GPU access is
granted.

> diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
> new file mode 100644
> index 0000000000000..5ab1d44f0a22e
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_aw.c
> @@ -0,0 +1,498 @@

[ ... ]

> +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);
> +}

[Severity: Medium]
Does panthor_aw_msg_retry_work() spin in a zero-delay workqueue loop when
panthor_am_msg_retry() returns -EBUSY?

When a second message is sent before the arbiter consumes the first from
AM_OUTGOING_MESSAGE, panthor_am_msg_send() queues it in msg->send_fifo and
schedules aw->msg_retry_work.  As long as AM_OUTGOING_MESSAGE_STATUS stays
non-zero, panthor_am_msg_retry() returns -EBUSY and the worker immediately
re-queues itself onto aw->wq without any backoff or bound, pegging a
kworker at 100% CPU polling MMIO registers (unlike rg_retry_messages() in
panthor_resource_group.c, which uses delayed_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 == -ENOSPC)
> +		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);
> +	u8 reply_version = AM_MSG_CURRENT_VERSION;
> +	int ret;
> +
> +	ret = panthor_am_msg_version_validate(&aw->msg, version, acked,
> +					      &reply_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, reply_version);
> +
> +		panthor_aw_send_msg(aw, reply);
> +
> +		guard(spinlock_irqsave)(&aw->lock);
> +
> +		/*
> +		 * If state is GPU_REQUEST, arbiter previously completed
> +		 * handshake, New INIT could mean that the arbiter was restarted
> +		 * and we need to resend GPU_REQUEST.
> +		 */
> +		if (atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_REQUEST) {
> +			panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST);
> +			return;
> +		}
> +	}

[Severity: Low]
Can this resend VM_ARB_GPU_REQUEST to an incompatible arbiter if the
arbiter restarts with an unsupported protocol version while aw->state is
PANTHOR_AW_STATE_GPU_REQUEST?

Even when panthor_am_msg_version_validate() returns -EOPNOTSUPP, the
!acked branch still sends VM_ARB_GPU_REQUEST and returns early, leaving
panthor_aw_request() waiting for the 5-second timeout instead of
transitioning to PANTHOR_AW_STATE_READY so it can fail immediately with
-EOPNOTSUPP.  Also, panthor_am_msg_version_validate() returns -EOPNOTSUPP
when acked is true and version > AM_MSG_CURRENT_VERSION, so the warning
above can log that version is less than AM_MSG_MIN_SUPPORTED_VERSION when
it is actually greater than AM_MSG_CURRENT_VERSION.

[ ... ]

> +	ret = panthor_aw_wait_cond(aw, atomic_read(&aw->state) != state, timeout_ms);
> +	if (ret) {
> +		scoped_guard(spinlock_irqsave, &aw->lock) {
> +			switch (atomic_read(&aw->state)) {
> +			case PANTHOR_AW_STATE_GPU_GRANTED:
> +				return 0;
> +			case PANTHOR_AW_STATE_GPU_REQUEST:
> +				panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY);
> +				break;
> +			default:
> +				break;
> +			}
> +		}
> +
> +		return ret;
> +	}
> +
> +	goto again;
> +}

[Severity: Medium]
Can aw->state become permanently wedged in PANTHOR_AW_STATE_YIELDING if
waiting for the window to close times out?

If __panthor_aw_suspend() times out waiting for PANTHOR_AW_STATE_READY (or
if WINDOW_IRQ_WINDOW_OPENED arrives just after panthor_aw_request() times
out in panthor_aw_resume() and transitions READY -> YIELDING), the caller
suspends the AW IRQ while aw->state is PANTHOR_AW_STATE_YIELDING.  If the
hardware window finishes closing while the IRQ is masked, no
WINDOW_IRQ_WINDOW_CLOSED interrupt fires on the next resume, and because
neither __panthor_aw_suspend() nor panthor_aw_request() checks
!panthor_aw_is_open(aw) or resets PANTHOR_AW_STATE_YIELDING to
PANTHOR_AW_STATE_READY on timeout, every subsequent panthor_aw_request()
call times out waiting on PANTHOR_AW_STATE_YIELDING.

> +
> +static int __panthor_aw_suspend(struct panthor_aw *aw)
> +{
> +	scoped_guard(spinlock_irqsave, &aw->lock) {
> +		switch (atomic_read(&aw->state)) {
> +		case PANTHOR_AW_STATE_READY:
> +			return 0;
> +		case PANTHOR_AW_STATE_SUSPEND_PENDING:
> +			return 0;
> +		case PANTHOR_AW_STATE_GPU_GRANTED:
> +			panthor_aw_state_set(aw, PANTHOR_AW_STATE_YIELDING);
> +			panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED);
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	return panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY,
> +				     PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS);
> +}

[Severity: Medium]
What happens in __panthor_aw_suspend() when aw->state is
PANTHOR_AW_STATE_INIT or PANTHOR_AW_STATE_GPU_REQUEST?

If the initial handshake in panthor_aw_init() -> panthor_aw_request()
times out, aw->state is still PANTHOR_AW_STATE_INIT when
panthor_aw_unplug() calls __panthor_aw_suspend(), which hits default and
blocks for another 5 seconds waiting for PANTHOR_AW_STATE_READY.  If
__panthor_aw_suspend() is called while in PANTHOR_AW_STATE_GPU_REQUEST, it
leaves the state as PANTHOR_AW_STATE_GPU_REQUEST while waiting for
PANTHOR_AW_STATE_READY, so a concurrent WINDOW_IRQ_WINDOW_OPENED will
transition it to PANTHOR_AW_STATE_GPU_GRANTED instead of yielding and
cause suspend to time out with the window left open.

[ ... ]

> +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;
> +
> +	ret = __panthor_aw_suspend(aw);
> +
> +	panthor_aw_irq_suspend(&aw->irq);
> +
> +	return ret;
> +}

[Severity: High]
Can aw->msg_retry_work run after panthor_aw_suspend() or the error path of
panthor_aw_resume() returns and the caller disables the GPU clocks?

CPU0                                  kworker
panthor_device_suspend()
  ...
  panthor_aw_suspend()
    __panthor_aw_suspend()
      panthor_aw_send_msg()
        queue_work(..., &msg_retry_work)
    panthor_aw_irq_suspend()
  clk_disable_unprepare(ptdev->clks.core)
                                      panthor_aw_msg_retry_work()
                                        panthor_am_msg_retry()
                                          gpu_read(msg->iomem, ...)
                                            <-- unclocked MMIO access

Unlike panthor_aw_unplug(), neither panthor_aw_suspend() nor the err_out
path of panthor_aw_resume() cancels aw->msg_retry_work, and
panthor_aw_msg_retry_work() does not hold a runtime PM reference, so it
can read and write msg->iomem after the clocks are turned off and trigger
a synchronous external abort.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=22

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration
  2026-11-10 15:38 ` [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration Karunika Choo
@ 2026-11-10 16:22   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:22 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/panthor: adev->pc[i] left set on partition_ensure_reset()
  failure
--

commit 872e2f2e631e5b4428bb12ac09aefb750b611c43
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Reset partitions before starting arbitration

This commit ensures each partition is brought into the RESET state with
interrupts masked during partition control initialization before starting
arbitration.

> diff --git a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> index 3b472403f49d1..2c0f8dc0b3bfb 100644
> --- a/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
> +++ b/drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c
[ ... ]
> @@ -429,6 +479,10 @@ static int partition_control_init(struct panthor_arbitration *adev, int i)
>  
>  	adev->pc[i] = pc;
>  
> +	ret = partition_ensure_reset(pc);
> +	if (ret)
> +		return ret;

[Severity: Medium]
Should adev->pc[i] be assigned only after partition_ensure_reset()
succeeds?

When partition_ensure_reset() fails, panthor_arbitration_probe() unwinds
via panthor_partition_control_term(), which iterates over non-NULL
adev->pc[i] entries and runs window_close() and partition_state_wait() on
the partition that just failed reset recovery, triggering WARN_ON() and
an extra 5-second timeout.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=28

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions
  2026-11-10 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
@ 2026-11-10 16:23   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:23 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/panthor: missing slow-reset fallback in
  panthor_device_resume_hw_components()
--

commit b392fdf733ca625b6c9ab7dbb3e11caa5a1d1b14
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Synchronize HW component PM transitions

This commit splits hardware component suspend and resume into
panthor_device_suspend_hw_components() and
panthor_device_resume_hw_components(), serializing transitions with
ptdev->pm.hw_component_lock and ptdev->pm.hw_component_state.

> diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
> index 98c0dd3ce7bcb..2ccd8ad6318d1 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.c
> +++ b/drivers/gpu/drm/panthor/panthor_device.c
[ ... ]
> @@ -484,21 +491,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;
>  }

[Severity: Medium]
Should the ptdev->reset.pending check and the fast-to-slow reset retry live
inside panthor_device_resume_hw_components() under
ptdev->pm.hw_component_lock rather than only in panthor_device_resume()?

drivers/gpu/drm/panthor/panthor_device.c:panthor_device_resume() {
    ...
		if (atomic_read(&ptdev->reset.pending)) {
			ptdev->reset.fast = false;
			atomic_set(&ptdev->reset.pending, 0);
		}

		ret = panthor_device_resume_hw_components(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);
		}
    ...
}

When access-window arbitration later calls
panthor_device_resume_hw_components() while runtime PM remains active,
panthor_device_resume() is not run.  A pending reset in ptdev->reset.pending
is then ignored, and a failed fast reset does not fall back to a slow reset
by reloading firmware sections, causing component resume to fail.  In
panthor_device_resume(), ptdev->reset.fast is also modified outside
ptdev->pm.hw_component_lock and the lock is dropped between the fast-reset
attempt and the slow-reset retry.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=23

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend
  2026-11-10 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
@ 2026-11-10 16:24   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:24 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] drm/panthor: panthor_device_system_resume() wakes idle GPU
  via tick_work
--

commit 4eba454e973401bba31950f161b35fd4637e2ab7
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Quiesce scheduler tick during system suspend

This commit adds system suspend and resume callbacks that disable the
scheduler tick work before forced runtime suspend and re-enable it on
resume or suspend failure.

> diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
> index 2ccd8ad6318d1..ba5252c27410f 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.c
> +++ b/drivers/gpu/drm/panthor/panthor_device.c
> @@ -662,3 +662,29 @@ int panthor_device_suspend(struct device *dev)
>  	atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_SUSPENDED);
>  	return 0;
>  }
> +
> +int panthor_device_system_suspend(struct device *dev)
> +{
> +	struct panthor_device *ptdev = dev_get_drvdata(dev);
> +	int ret;
> +
> +	panthor_sched_pre_suspend(ptdev);
> +
> +	ret = pm_runtime_force_suspend(dev);
> +	if (ret)
> +		panthor_sched_post_resume(ptdev);
> +
> +	return ret;
> +}
> +
> +int panthor_device_system_resume(struct device *dev)
> +{
> +	struct panthor_device *ptdev = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = pm_runtime_force_resume(dev);
> +
> +	panthor_sched_post_resume(ptdev);
> +
> +	return ret;
> +}

[ ... ]

> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index a343c5180e01e..3b2ed4095f80e 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -4047,6 +4047,21 @@ void panthor_job_update_resvs(struct drm_exec *exec, struct drm_sched_job *sched
>  				DMA_RESV_USAGE_BOOKKEEP, DMA_RESV_USAGE_BOOKKEEP);
>  }
>  
> +void panthor_sched_pre_suspend(struct panthor_device *ptdev)
> +{
> +	struct panthor_scheduler *sched = ptdev->scheduler;
> +
> +	disable_delayed_work_sync(&sched->tick_work);
> +}
> +
> +void panthor_sched_post_resume(struct panthor_device *ptdev)
> +{
> +	struct panthor_scheduler *sched = ptdev->scheduler;
> +
> +	enable_delayed_work(&sched->tick_work);
> +	panthor_sched_resume(ptdev);
> +}

[Severity: Medium]
Does calling panthor_sched_resume() unconditionally in
panthor_sched_post_resume() force a runtime wakeup of an idle GPU on every
system resume?

When the GPU is already runtime-suspended before system suspend,
pm_runtime_force_resume() leaves the device in RPM_SUSPENDED without
calling panthor_device_resume().  Calling panthor_sched_resume() queues
sched->tick_work immediately, and tick_work() then calls
panthor_device_resume_and_get(), powering up the GPU and queuing a second
tick from panthor_device_resume().

In addition, if pm_runtime_force_resume() fails, queuing sched->tick_work
causes tick_work() to immediately retry panthor_device_resume_and_get() and
hit drm_WARN_ON(&ptdev->base, ret).  Since panthor_device_resume() already
calls panthor_sched_resume() when the device actually resumes, only the
pm_runtime_force_suspend() error path needs to kick the tick if the device
remained active.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=24

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits
  2026-11-10 15:38 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
@ 2026-11-10 16:30   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:30 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 5 potential
issues to consider:
- [High] drm/panthor: false success in panthor_fw_start() and
  panthor_fw_mcu_halted()
- [High] drm/panthor: panthor_sched_report_fw_events() drops FW events
  on access loss
- [Medium] drm/panthor: unguarded PWR_STATUS checks fail on access loss
  in panthor_pwr.c
- [Medium] drm/panthor: unawakened waits stall on access loss in flush,
  reset, and FW
- [Low] drm/panthor: missing early return on access loss in
  panthor_pwr_l2_power_off()
--

commit d06b3bf7778829600d32bed288afa3f04bcf72dd
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Tolerate access-window loss during HW waits

This commit adds panthor_aw_has_gpu_access() and updates firmware, GPU cache
flush, power control, and scheduler paths to stop waiting or suppress timeout
errors when the access window closes.

> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 1449d9a66302e..8643e7bbfc108 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
> @@ -1140,9 +1140,10 @@ static int panthor_fw_start(struct panthor_device *ptdev)
>  	gpu_write(fw->iomem, MCU_CONTROL, MCU_CONTROL_AUTO);
>  
>  	if (!wait_event_timeout(ptdev->fw->req_waitqueue,
> -				ptdev->fw->booted,
> +				(ptdev->fw->booted ||
> +				 !panthor_aw_has_gpu_access(ptdev)),
>  				msecs_to_jiffies(1000))) {
> -		if (!ptdev->fw->booted &&
> +		if (!ptdev->fw->booted && panthor_aw_has_gpu_access(ptdev) &&
>  		    !(gpu_read(fw->irq.iomem, INT_STAT) & JOB_INT_GLOBAL_IF))
>  			timedout = true;
>  	}
> @@ -1180,6 +1181,9 @@ static bool panthor_fw_mcu_halted(struct panthor_device *ptdev)
>  	struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
>  	bool halted;
>  
> +	if (!panthor_aw_has_gpu_access(ptdev))
> +		return true;
> +
>  	halted = gpu_read(ptdev->fw->iomem, MCU_STATUS) == MCU_STATUS_HALT;

[Severity: High]
Can returning 0 from panthor_fw_start() and true from panthor_fw_mcu_halted()
on access loss cause callers to treat an unbooted MCU as booted and corrupt
ptdev->reset.fast?

If the access window closes in panthor_fw_start() before JOB_INT_GLOBAL_IF
sets ptdev->fw->booted, panthor_fw_start() returns 0 while ptdev->fw->booted
is still false.  In panthor_fw_init(), execution continues into
panthor_fw_init_ifaces() before the MCU has populated the shared control
interface.  In panthor_aw_resume() -> panthor_device_resume_hw_components() ->
panthor_fw_post_reset(), an interrupted slow reset (where ptdev->reset.fast
was false and firmware sections were freshly reloaded) succeeds and is
immediately followed by panthor_device_suspend_hw_components() ->
panthor_fw_pre_reset().  Because panthor_fw_mcu_halted() unconditionally
returns true when !panthor_aw_has_gpu_access(ptdev), panthor_fw_pre_reset()
sets ptdev->reset.fast = true even though the MCU never finished booting, so
the next resume skips panthor_reload_fw_sections() and attempts a fast reset
on unbooted firmware memory.

> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index 2e1b00e86cdea..d5666264c1fe6 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
> @@ -377,7 +378,8 @@ int panthor_gpu_flush_caches(struct panthor_device *ptdev,
>  				msecs_to_jiffies(100))) {
>  		scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
>  			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;

[Severity: Medium]
Does panthor_gpu_flush_caches() still block for the full 100ms timeout per
active VM whenever the access window closes?

When panthor_aw_handle_window_closed() sets aw->state to
PANTHOR_AW_STATE_SUSPEND_PENDING and schedules panthor_aw_post_yield_work(),
panthor_device_suspend_hw_components() -> panthor_mmu_suspend() ->
panthor_mmu_as_disable() calls panthor_gpu_flush_caches() for each bound MMU
address space slot while holding ptdev->mmu->as.slots_lock and
ptdev->pm.hw_component_lock.  Because panthor_gpu_flush_caches() only checks
panthor_aw_has_gpu_access(ptdev) after wait_event_timeout() expires (and
ptdev->gpu->reqs_acked is not woken on access-window close), every yield
sleeps for 100ms per active VM.  Similarly, panthor_pwr_reset() and
panthor_fw_wait_acks() do not include !panthor_aw_has_gpu_access(ptdev) in
their wait_event_timeout() conditions, so in-flight waits there also sleep
until their full timeout expires when access is lost.

> diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
> index 1cfd4b658c81b..66a2c54b070dc 100644
> --- a/drivers/gpu/drm/panthor/panthor_pwr.c
> +++ b/drivers/gpu/drm/panthor/panthor_pwr.c

[ ... ]

> @@ -400,9 +411,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;

[Severity: Medium]
What happens in panthor_pwr_l2_power_on() when panthor_pwr_domain_transition()
or delegate_domain() returns 0 after losing GPU access?

panthor_pwr_l2_power_on() powers on the L2 domain and then calls
panthor_pwr_delegate_domains(), which runs delegate_domain() for the neural,
shader, and tiler domains in sequence.  Once the access window closes, MMIO
reads from PWR_STATUS return 0, so the next delegate_domain() call hits the
unguarded !(pwr_status & allow_mask) check at the start of the function, logs
a warning, and fails with -EPERM.  The initial PWR_STATUS checks in
panthor_pwr_l2_power_on() and panthor_pwr_reset_soft() have the same issue and
fail with -EPERM and -EOPNOTSUPP when entered after access loss.

> @@ -542,7 +557,7 @@ void panthor_pwr_l2_power_off(struct panthor_device *ptdev)
>  	const bool has_neural = panthor_hw_has_neural(ptdev);
>  
>  	/* 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;
>  	}

[Severity: Low]
Should panthor_pwr_l2_power_off() still return early when GPU access is lost,
while only gating drm_warn() on panthor_aw_has_gpu_access(ptdev)?

With the condition written this way, calling panthor_pwr_l2_power_off() when
!panthor_aw_has_gpu_access(ptdev) (such as during panthor_aw_post_yield_work()
-> panthor_device_suspend_hw_components() -> panthor_gpu_suspend()) bypasses
the return and falls through to panthor_pwr_domain_force_off() and
panthor_pwr_domain_power_off() across all domains on a closed access window.

> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 729eeb339fa40..addc5986554df 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -1887,6 +1887,9 @@ void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
>  
>  	guard(spinlock)(&ptdev->scheduler->events_lock);
>  
> +	if (!panthor_aw_has_gpu_access(ptdev))
> +		return;
> +
>  	if (events & JOB_INT_GLOBAL_IF) {
>  		sched_process_global_irq_locked(ptdev);
>  		events &= ~JOB_INT_GLOBAL_IF;

[Severity: High]
Can returning early here drop job completions and CSG events when the MCU
raises a job IRQ just before yielding the access window?

kthread (job irq)                 <hardirq> (aw irq)
panthor_job_irq_handler()
  status = gpu_read(INT_RAWSTAT)
  gpu_write(INT_CLEAR, status)
                                  panthor_aw_irq_raw_hander()
                                    panthor_aw_handle_window_closed()
                                      aw->state =
                                        PANTHOR_AW_STATE_SUSPEND_PENDING
  panthor_sched_report_fw_events()
    if (!panthor_aw_has_gpu_access(ptdev))
      return;  <-- drops status events

With YIELD_IDLE enabled, the MCU updates group sync objects and CSG output
structures in host DRAM, raises the job interrupt, and immediately yields the
access window.  Because panthor_job_irq_handler() has already cleared the
interrupt before calling panthor_sched_report_fw_events(), bailing out when
!panthor_aw_has_gpu_access(ptdev) discards those events (such as
CSG_SYNC_UPDATE and CS_FAULT) even though the global and CSG interfaces live
in shared DRAM, leaving completed jobs' dma_fences unsignaled across fast
resets.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=26

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset
  2026-11-10 15:38 ` [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset Karunika Choo
@ 2026-11-10 16:44   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:44 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 7 potential
issues to consider:
- [High] drm/panthor: panthor_sched_pre_reset() runs before
  panthor_aw_pre_reset()
- [High] drm/panthor: lost slow reset in panthor_device_reset_work() on
  AW loss
- [High] drm/panthor: race between panthor_device_reset_work() and AW HW
  component PM
- [Medium] drm/panthor: panthor_aw_post_reset() sets GPU_GRANTED before
  checking window
- [Medium] drm/panthor: panthor_pwr_reset_soft() fails before GPU access
  check
- [Medium] drm/panthor: panthor_pwr_reset() leaves
  PWR_IRQ_RESET_COMPLETED set on timeout
- [Medium] drm/panthor: unhandled AW reset events in
  panthor_gpu_suspend()
--

commit 04a925f0f5b623c17e694b1b19489978a653cb56
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Prevent missed post-yield cleanup due to reset

This commit adds a PANTHOR_AW_STATE_RESET_ACTIVE state to ignore
reset-induced access-window events during GPU resets and schedule
post-yield cleanup afterward if the hardware window closed.

> diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
> index f344a2aa32979..f7fcf51c2c632 100644
> --- a/drivers/gpu/drm/panthor/panthor_aw.c
> +++ b/drivers/gpu/drm/panthor/panthor_aw.c
[ ... ]
> @@ -637,5 +644,46 @@ bool panthor_aw_has_gpu_access(struct panthor_device *ptdev)
[ ... ]
> +int panthor_aw_pre_reset(struct panthor_device *ptdev)
> +{
> +	struct panthor_aw *aw = ptdev->aw;
> +
> +	if (!aw)
> +		return 0;
> +
> +	guard(spinlock_irqsave)(&aw->lock);
> +
> +	if (!panthor_aw_state_try_set(ptdev->aw, PANTHOR_AW_STATE_GPU_GRANTED,
> +				      PANTHOR_AW_STATE_RESET_ACTIVE))
> +		return -EAGAIN;
> +
> +	return 0;
> +}

[Severity: High]
Can panthor_device_reset_work() race with panthor_device_resume_hw_components()
when an access window is re-granted while runtime PM is active?

When an access window yields while ptdev->pm.state is
PANTHOR_DEVICE_PM_STATE_ACTIVE, panthor_aw_post_yield_work() suspends the
hardware components under ptdev->pm.hw_component_lock while ptdev->pm.state
stays active.  On the next panthor_aw_resume(), panthor_aw_request() sets
aw->state to PANTHOR_AW_STATE_GPU_GRANTED before
panthor_device_resume_hw_components() acquires ptdev->pm.hw_component_lock:

kworker (reset.work)              caller (panthor_aw_resume)
                                  panthor_aw_request()
                                    <hardirq>
                                      panthor_aw_handle_window_opened()
                                        aw->state = GPU_GRANTED
panthor_device_reset_work()
  panthor_aw_pre_reset()
    aw->state = RESET_ACTIVE
  panthor_mmu_pre_reset()
                                  panthor_device_resume_hw_components()
                                    panthor_gpu_resume()
                                    panthor_mmu_resume()
  panthor_hw_soft_reset()
  panthor_mmu_post_reset()          panthor_fw_resume()  <-- concurrent reset/resume

Because neither panthor_device_reset_work() nor panthor_aw_pre_reset()
synchronizes with ptdev->pm.hw_component_lock or checks
ptdev->pm.hw_component_state, the reset worker can reset the GPU, MMU, and
firmware while panthor_device_resume_hw_components() is concurrently
resuming them, corrupting ptdev->mmu->as.alloc_mask and causing firmware
boot failure that triggers panthor_device_unplug().

> +
> +void panthor_aw_post_reset(struct panthor_device *ptdev)
> +{
> +	struct panthor_aw *aw = ptdev->aw;
> +
> +	if (!aw)
> +		return;
> +
> +	guard(spinlock_irqsave)(&aw->lock);
> +
> +	panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_RESET_ACTIVE,
> +				 PANTHOR_AW_STATE_GPU_GRANTED);
> +
> +	if (!panthor_aw_is_open(aw))
> +		panthor_aw_schedule_post_yield_work(aw);
> +}

[Severity: Medium]
If the hardware access window closed during the reset, does calling
panthor_aw_state_try_set() before panthor_aw_is_open() briefly expose a
false PANTHOR_AW_STATE_GPU_GRANTED state and wake aw->waitqueue?

panthor_aw_state_try_set() sets aw->state to PANTHOR_AW_STATE_GPU_GRANTED
and calls wake_up_all(&aw->waitqueue) before panthor_aw_is_open() checks
WINDOW_STATUS and transitions aw->state to PANTHOR_AW_STATE_SUSPEND_PENDING.
Lockless readers such as panthor_aw_has_gpu_access(), panthor_aw_resume(),
and panthor_aw_state_wait_transition() do not take aw->lock, so they can
observe PANTHOR_AW_STATE_GPU_GRANTED and proceed as if GPU access were held
even though the window is closed.

In addition, when panthor_aw_post_reset() transitions aw->state to
PANTHOR_AW_STATE_SUSPEND_PENDING, any pending WINDOW_CLOSED interrupt that
fires afterward will hit the default branch in
panthor_aw_handle_window_closed() and warn "Unexpected WINDOW_CLOSED
received" because PANTHOR_AW_STATE_SUSPEND_PENDING is not handled there
(unlike in panthor_aw_handle_window_opened()).

> diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
> index d1ef460577c99..d4a67277fe6fa 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.c
> +++ b/drivers/gpu/drm/panthor/panthor_device.c
> @@ -140,14 +140,27 @@ static void panthor_device_reset_work(struct work_struct *work)
>  		return;
>  
>  	panthor_sched_pre_reset(ptdev);
> +	if (panthor_aw_pre_reset(ptdev) == -EAGAIN) {
> +		atomic_set(&ptdev->reset.pending, 0);
> +		panthor_sched_post_reset(ptdev, false);
> +		goto out_dev_exit;
> +	}

[Severity: High]
Should panthor_aw_pre_reset() run before panthor_sched_pre_reset()?

panthor_aw_post_yield_work() suspends hardware components without calling
panthor_sched_suspend(), so user groups remain bound to CSG slots while the
access window is closed.  When panthor_sched_pre_reset() runs before
panthor_aw_pre_reset() has verified PANTHOR_AW_STATE_GPU_GRANTED,
panthor_sched_suspend() rings CSF_GLB_DOORBELL_ID over the closed window and
waits in panthor_fw_csg_wait_acks() -> panthor_fw_wait_acks(), which does
not check panthor_aw_has_gpu_access().  Every bound CSG times out on
CSG_STATE_SUSPEND, escalates to CSG_STATE_TERMINATE, and marks innocent user
groups asPANTHOR_CS_GROUP_TERMINATED.  Furthermore, if
panthor_aw_post_yield_work() already ran panthor_mmu_suspend() (which resets
vm->as.active_cnt to 0 via panthor_vm_release_as_locked()), unbinding those
groups in panthor_sched_suspend() calls panthor_vm_idle() and underflows
vm->as.active_cnt.

Even when aw->state starts in PANTHOR_AW_STATE_GPU_GRANTED, running
panthor_sched_pre_reset() before transitioning to
PANTHOR_AW_STATE_RESET_ACTIVE means a WINDOW_CLOSED event arriving while
CSGs are suspending will schedule post_yield_work concurrently with the
reset.

>  	panthor_fw_pre_reset(ptdev, true);
>  	panthor_mmu_pre_reset(ptdev);
> +
>  	panthor_hw_soft_reset(ptdev);

[Severity: Medium]
Does panthor_gpu_suspend() also need PANTHOR_AW_STATE_RESET_ACTIVE
protection when it calls panthor_hw_soft_reset()?

drivers/gpu/drm/panthor/panthor_gpu.c:panthor_gpu_suspend() {
    ...
	if (!ptdev->reset.fast)
		panthor_hw_soft_reset(ptdev);
	else
		panthor_hw_l2_power_off(ptdev);

	panthor_gpu_irq_suspend(ptdev);
}

When panthor_aw_suspend() (or the error path of panthor_aw_resume()) calls
panthor_device_suspend_hw_components() after a failed MCU halt or slow-reset
boot failure, panthor_gpu_suspend() executes panthor_hw_soft_reset() while
aw->state is still PANTHOR_AW_STATE_GPU_GRANTED.  The reset-induced
WINDOW_CLOSED interrupt transitions aw->state to
PANTHOR_AW_STATE_SUSPEND_PENDING and queues post_yield_work, so the
subsequent __panthor_aw_suspend() sees PANTHOR_AW_STATE_SUSPEND_PENDING and
returns 0 without sending VM_ARB_GPU_STOPPED, leaving the hardware window
open and the arbiter out of sync with aw->state once post_yield_work sets
PANTHOR_AW_STATE_READY.

> +
>  	panthor_hw_l2_power_on(ptdev);
>  	panthor_mmu_post_reset(ptdev);
>  	ret = panthor_fw_post_reset(ptdev);
> +	if (ret && !panthor_aw_has_gpu_access(ptdev))
> +		ret = 0;
> +
> +	panthor_aw_post_reset(ptdev);
>  	atomic_set(&ptdev->reset.pending, 0);

[Severity: High]
When a reset is aborted at panthor_aw_pre_reset() or loses GPU access before
panthor_fw_post_reset() completes, does clearing ptdev->reset.pending cause
the next resume to skip a required slow reset?

When the access window closes, panthor_aw_post_yield_work() runs
panthor_fw_suspend() -> panthor_fw_pre_reset(ptdev, false):

drivers/gpu/drm/panthor/panthor_fw.c:panthor_fw_pre_reset() {
    ...
	ptdev->reset.fast = false;

	if (!on_hang) {
		panthor_fw_halt_mcu(ptdev);
		if (!panthor_fw_wait_mcu_halted(ptdev))
			drm_warn(&ptdev->base, "Failed to cleanly suspend MCU");
		else
			ptdev->reset.fast = true;
	}
    ...
}

Because panthor_fw_mcu_halted() returns true whenever
!panthor_aw_has_gpu_access(ptdev), panthor_fw_pre_reset(ptdev, false) sets
ptdev->reset.fast = true even though the MCU was never cleanly halted (or
failed to boot during panthor_fw_post_reset()).  With ptdev->reset.pending
also cleared to 0 here and in the -EAGAIN branch above, the subsequent
panthor_aw_resume() or panthor_device_resume() performs a fast reset instead
of reloading the firmware sections.

> diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
> index 66a2c54b070dc..eef400c4bc6fb 100644
> --- a/drivers/gpu/drm/panthor/panthor_pwr.c
> +++ b/drivers/gpu/drm/panthor/panthor_pwr.c
> @@ -113,6 +113,9 @@ static int panthor_pwr_reset(struct panthor_device *ptdev, u32 reset_cmd)
>  		if (reset_pending(ptdev)) {
>  			drm_WARN(&ptdev->base, 1, "Reset already pending");
>  		} else {
> +			if (!panthor_aw_has_gpu_access(ptdev))
> +				return 0;
> +
>  			ptdev->pwr->pending_reqs |= PWR_IRQ_RESET_COMPLETED;
>  			gpu_write(pwr->irq.iomem, INT_CLEAR, PWR_IRQ_RESET_COMPLETED);
>  			panthor_pwr_write_command(ptdev, reset_cmd, 0);

[Severity: Medium]
Can panthor_pwr_reset() reach this panthor_aw_has_gpu_access() check when
the access window is already closed?

Its caller panthor_pwr_reset_soft() reads PWR_STATUS first:

drivers/gpu/drm/panthor/panthor_pwr.c:panthor_pwr_reset_soft() {
    ...
	if (!(gpu_read64(pwr->iomem, PWR_STATUS) & PWR_STATUS_ALLOW_SOFT_RESET)) {
		drm_err(&ptdev->base, "RESET_SOFT not allowed");
		return -EOPNOTSUPP;
	}

	return panthor_pwr_reset(ptdev, PWR_COMMAND_RESET_SOFT);
}

When GPU access is lost, gpu_read64(pwr->iomem, PWR_STATUS) reads 0 over the
closed window, so panthor_pwr_reset_soft() logs a false "RESET_SOFT not
allowed" error and returns -EOPNOTSUPP before calling panthor_pwr_reset().
panthor_pwr_l2_power_on(), called right after panthor_hw_soft_reset() in
panthor_device_reset_work(), similarly reads PWR_STATUS as 0 without
checking panthor_aw_has_gpu_access() and logs a false "Power on L2 domain
not allowed" warning.

> @@ -622,5 +625,9 @@ void panthor_pwr_resume(struct panthor_device *ptdev)
>  	if (!ptdev->pwr)
>  		return;
>  
> +	/* clear any pending resets on resume to prevent blocking future resets */
> +	scoped_guard(spinlock_irqsave, &ptdev->pwr->reqs_lock)
> +		ptdev->pwr->pending_reqs &= ~PWR_IRQ_RESET_COMPLETED;
> +
>  	panthor_irq_resume(&ptdev->pwr->irq);
>  }

[Severity: Medium]
Should panthor_pwr_reset() also clear PWR_IRQ_RESET_COMPLETED before
returning -ETIMEDOUT?

drivers/gpu/drm/panthor/panthor_pwr.c:panthor_pwr_reset() {
    ...
	if (!wait_event_timeout(ptdev->pwr->reqs_acked, !reset_pending(ptdev),
				msecs_to_jiffies(PWR_RESET_TIMEOUT_MS))) {
		guard(spinlock_irqsave)(&ptdev->pwr->reqs_lock);

		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;
		}

		ptdev->pwr->pending_reqs &= ~PWR_IRQ_RESET_COMPLETED;
	}
}

If the access window closes during panthor_pwr_reset() (aborting the
hardware reset) and re-opens before the 500ms wait_event_timeout() expires,
panthor_aw_has_gpu_access() is true both in panthor_pwr_reset() and in
panthor_aw_post_reset().  panthor_pwr_reset() returns -ETIMEDOUT without
clearing PWR_IRQ_RESET_COMPLETED, and panthor_aw_post_reset() sees the open
window and does not schedule post_yield_work, so panthor_pwr_resume() never
runs and future panthor_pwr_reset() calls hit drm_WARN("Reset already
pending") without issuing the reset command.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=27

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows
  2026-11-10 15:38 ` [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows Karunika Choo
@ 2026-11-10 16:50   ` sashiko-bot
  0 siblings, 0 replies; 57+ messages in thread
From: sashiko-bot @ 2026-11-10 16:50 UTC (permalink / raw)
  To: Karunika Choo, dri-devel

Thank you for your contribution! Sashiko AI review found 6 potential
issues to consider:
- [High] drm/panthor: missing panthor_sched_suspend() in
  panthor_aw_post_yield_work()
- [High] drm/panthor: race between panthor_aw_ensure_gpu_access() and
  post_yield_work
- [High] drm/panthor: endless AW yield-request ping-pong from
  panthor_aw_post_yield_work()
- [Medium] drm/panthor: panthor_device_resume() treats -EAGAIN as
  fast-reset failure
- [Medium] drm/panthor: __panthor_aw_suspend() leaves state as
  SUSPEND_PENDING
- [Medium] drm/panthor: unhandled -EAGAIN from panthor_aw_resume() on PM
  paths
--

commit 4fae2615b4ec401e9748b231f8ea063e488e1f9a
Author: Karunika Choo <karunika.choo@arm.com>

drm/panthor: Route HW component PM through access windows

This commit routes hardware component suspend and resume through the access
window layer when AW support is present, defers post-yield cleanup to a
workqueue, and updates panthor_device_resume_and_get() to ensure GPU access is
granted before returning.

> diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c
> index 5ab1d44f0a22e..3b418c816d824 100644
> --- a/drivers/gpu/drm/panthor/panthor_aw.c
> +++ b/drivers/gpu/drm/panthor/panthor_aw.c
[ ... ]
> @@ -114,6 +117,66 @@ static int panthor_aw_state_wait(struct panthor_aw *aw,
>  	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_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;
> +
> +	scoped_guard(spinlock_irqsave, &aw->lock) {
> +		/* Something else has progressed the state to READY. */
> +		if (atomic_read(&aw->state) != PANTHOR_AW_STATE_SUSPEND_PENDING)
> +			return;
> +	}

[Severity: Medium]
Does any other path ever transition aw->state from
PANTHOR_AW_STATE_SUSPEND_PENDING to PANTHOR_AW_STATE_READY?

When panthor_aw_suspend() runs after a WINDOW_CLOSED interrupt sets
PANTHOR_AW_STATE_SUSPEND_PENDING, it suspends the hardware components and
calls __panthor_aw_suspend(), which returns 0 on
PANTHOR_AW_STATE_SUSPEND_PENDING without updating aw->state:

drivers/gpu/drm/panthor/panthor_aw.c:__panthor_aw_suspend() {
	scoped_guard(spinlock_irqsave, &aw->lock) {
		switch (atomic_read(&aw->state)) {
		case PANTHOR_AW_STATE_READY:
			return 0;
		case PANTHOR_AW_STATE_SUSPEND_PENDING:
			return 0;
    ...
}

Because aw->state remains PANTHOR_AW_STATE_SUSPEND_PENDING after runtime
suspend finishes, panthor_aw_post_yield_work() does not exit early here and
calls panthor_sched_resume(), which schedules tick_work and wakes the
suspended GPU back up.  If system resume (pm_runtime_force_resume()) runs
before post_yield_work instead, panthor_aw_resume() sees
PANTHOR_AW_STATE_SUSPEND_PENDING and fails system resume with -EAGAIN.

> +
> +	/*
> +	 * 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.");

[Severity: High]
Doesn't panthor_aw_post_yield_work() need to suspend or unbind scheduler
groups before calling panthor_device_suspend_hw_components()?

Unlike panthor_device_suspend(), which calls panthor_sched_suspend() before
panthor_aw_suspend(), panthor_aw_post_yield_work() leaves groups bound in
sched->csg_slots[] with group->csg_id >= 0 while
panthor_device_suspend_hw_components() stops the MCU and calls
panthor_mmu_suspend().  panthor_mmu_suspend() disables all active address
spaces and calls panthor_vm_release_as_locked(), resetting vm->as.id = -1 and
vm->as.active_cnt = 0 on those still-bound groups.

On the next scheduler tick after hardware components resume, tick_ctx_apply()
sees group->csg_id >= 0 and skips group_bind_locked(), panthor_vm_active(),
and CSG programming, leaving the bound groups unmapped in the MMU and
unconfigured on the rebooted MCU.  When those groups are later unbound in
group_unbind_locked(), panthor_vm_idle() decrements vm->as.active_cnt from 0
(underflowing refcount_t) and hits drm_WARN_ON(&ptdev->base, vm->as.id == -1).
In addition, if runtime suspend runs before the hardware components are
resumed again, panthor_device_suspend() will call panthor_sched_suspend()
while the MCU is stopped and the access window is closed.

> +
> +	scoped_guard(spinlock_irqsave, &aw->lock)
> +		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_autosuspend(dev);
> +}

[Severity: High]
Can calling panthor_sched_resume() unconditionally here cause an endless
request/yield livelock between two idle access windows sharing a partition?

When AW0 is idle inside its 50ms autosuspend window and AW1 requests the GPU,
the arbiter issues PART_CMD_YIELD_IDLE and FW v2 closes AW0's window,
scheduling panthor_aw_post_yield_work() on AW0.  panthor_sched_resume() then
queues tick_work with delay 0, and tick_work() calls
panthor_device_resume_and_get() -> panthor_aw_ensure_gpu_access() ->
panthor_aw_request() before checking whether AW0 has any runnable groups.

Sending VM_ARB_GPU_REQUEST from idle AW0 causes the arbiter to issue
PART_CMD_YIELD_IDLE to AW1.  Once AW1 finishes its work and yields to AW0,
AW1's panthor_aw_post_yield_work() likewise requests the GPU back from AW0,
while AW0's tick_work() finds no runnable work and resets its 50ms
autosuspend timer via pm_runtime_put_autosuspend().  Both access windows can
then ping-pong GPU ownership and hardware component suspend/resume
indefinitely without ever reaching runtime autosuspend.

[ ... ]

> @@ -463,21 +538,41 @@ 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 */
> +		__panthor_aw_suspend(aw);
>  		goto err_out;
>  	}
>  
> -	return 0;
> +	switch (atomic_read(&aw->state)) {
> +	case PANTHOR_AW_STATE_SUSPEND_PENDING:
> +		/*
> +		 * WINDOW_CLOSED won the race. Suspend components before the
> +		 * caller can disable clocks. post_yield_work will transition
> +		 * state to READY after this PM transition completes.
> +		 */
> +		panthor_device_suspend_hw_components(ptdev);
> +		ret = -EAGAIN;
> +		break;
> +	case PANTHOR_AW_STATE_GPU_GRANTED:
> +		return 0;
> +	default:
> +		goto again;
> +	}
>  
>  err_out:
> -	panthor_aw_irq_suspend(&aw->irq);
>  	return ret;
>  }

[Severity: High]
Can panthor_aw_ensure_gpu_access() -> panthor_aw_resume() race with
panthor_aw_post_yield_work() when the device is already RPM_ACTIVE?

panthor_aw_post_yield_work() uses pm_runtime_barrier(dev) to wait for an
in-progress runtime resume, but panthor_device_resume_and_get() calls
panthor_aw_ensure_gpu_access() after pm_runtime_resume_and_get() has already
set the device to RPM_ACTIVE.  In that state pm_runtime_barrier(dev) returns
immediately, and nothing serializes panthor_aw_resume() against
panthor_aw_post_yield_work() or a second caller of
panthor_aw_ensure_gpu_access():

CPU0                              CPU1 (kworker / second caller)
panthor_aw_ensure_gpu_access()
  panthor_aw_resume()
    panthor_device_resume_hw_components()
    /* WINDOW_CLOSED sets SUSPEND_PENDING */
    switch (atomic_read(&aw->state))
    case PANTHOR_AW_STATE_SUSPEND_PENDING:
                                  panthor_aw_post_yield_work()
                                    pm_runtime_barrier() /* no-op */
                                    panthor_device_suspend_hw_components()
                                    panthor_aw_state_set(READY)
                                  panthor_aw_ensure_gpu_access()
                                    panthor_aw_resume()
                                      panthor_aw_request() /* GRANTED */
                                      panthor_device_resume_hw_components()
                                      return 0
      panthor_device_suspend_hw_components()
      <-- suspends FW/MMU/GPU/PWR while CPU1 is using the GPU

Alternatively, if WINDOW_CLOSED fires after panthor_aw_request() succeeds on
CPU0 but before CPU0 takes hw_component_lock in
panthor_device_resume_hw_components(), panthor_aw_post_yield_work() can finish
and set aw->state to PANTHOR_AW_STATE_READY first; CPU0 then resumes hardware
components while the window is closed, sees PANTHOR_AW_STATE_READY, and takes
default: goto again without suspending them.

> diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
> index ba5252c27410f..d1ef460577c99 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.c
> +++ b/drivers/gpu/drm/panthor/panthor_device.c
[ ... ]
> @@ -577,15 +579,17 @@ 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);
>  		}

[Severity: Medium]
Does panthor_device_resume() treat -EAGAIN from panthor_aw_resume() as a
fast-reset failure?

When panthor_aw_resume() returns -EAGAIN because aw->state is
PANTHOR_AW_STATE_SUSPEND_PENDING, panthor_device_resume() logs a false fast
reset error, clears ptdev->reset.fast = false (forcing a full firmware reload
on the next resume), and immediately retries panthor_aw_resume() while the
device is still RPM_RESUMING and panthor_aw_post_yield_work() is still blocked
in pm_runtime_barrier(dev), so the retry is guaranteed to fail with -EAGAIN
as well.  Conversely, when panthor_device_resume_hw_components() is called
from panthor_aw_ensure_gpu_access() -> panthor_aw_resume() while the device
is already RPM_ACTIVE, there is no fallback to retry with
ptdev->reset.fast = false if a fast reset fails.

>  
>  		if (!ret)
>  			panthor_sched_resume(ptdev);
> +		else
> +			panthor_aw_suspend(ptdev);
>  
>  		drm_dev_exit(cookie);

[Severity: Medium]
What happens when panthor_device_resume() returns -EAGAIN to callers other
than panthor_device_resume_and_get()?

panthor_device_resume_and_get() catches -EAGAIN, waits with
panthor_aw_wait_transition_complete(), and retries, but
panthor_device_unplug() calls pm_runtime_get_sync() directly (triggering
drm_WARN_ON() and proceeding with unplug while suspended) and
panthor_device_system_resume() calls pm_runtime_force_resume() directly
(failing system resume and leaving the device suspended).

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110153907.3241978-1-karunika.choo@arm.com?part=25

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 17/28] drm/panthor: Add arbitration scheduler
  2026-11-10 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
  2026-11-10 16:05   ` sashiko-bot
@ 2026-11-11  0:43   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: kernel test robot @ 2026-11-11  0:43 UTC (permalink / raw)
  To: Karunika Choo, dri-devel
  Cc: llvm, oe-kbuild-all, nd, Boris Brezillon, Steven Price,
	Liviu Dudau, linux-kernel, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter

Hi Karunika,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on drm/drm-next drm-tip/drm-tip]
[cannot apply to linux-review/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20260922-214455 daeinki-drm-exynos/exynos-drm-next drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v7.4-rc2 next-20261109]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20261110-153822
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20261110153907.3241978-18-karunika.choo%40arm.com
patch subject: [PATCH v2 17/28] drm/panthor: Add arbitration scheduler
config: um-randconfig-1001-20261111 (https://download.01.org/0day-ci/archive/20261111/202611110840.2W7v6LAR-lkp@intel.com/config)
compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261111/202611110840.2W7v6LAR-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611110840.2W7v6LAR-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:223:3: error: call to undeclared function 'hrtimer_start'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     223 |                 hrtimer_start(&sched->stop_timer.timer,
         |                 ^
>> drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:224:38: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     224 |                               ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
         |                                                           ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:249:4: error: call to undeclared function 'hrtimer_start'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     249 |                         hrtimer_start(&sched->grant_timer.timer,
         |                         ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:251:11: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     251 |                                       HRTIMER_MODE_REL);
         |                                       ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:291:3: error: call to undeclared function 'hrtimer_start'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     291 |                 hrtimer_start(&sched->close_timer.timer,
         |                 ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:292:38: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     292 |                               ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
         |                                                           ^
>> drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:458:6: error: call to undeclared function 'hrtimer_is_queued'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     458 |         if (hrtimer_is_queued(timer))
         |             ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:483:6: error: call to undeclared function 'hrtimer_is_queued'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     483 |         if (hrtimer_is_queued(timer))
         |             ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:504:6: error: call to undeclared function 'hrtimer_is_queued'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     504 |         if (hrtimer_is_queued(timer))
         |             ^
>> drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:621:2: error: call to undeclared function 'hrtimer_cancel'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     621 |         hrtimer_cancel(&sched->grant_timer.timer);
         |         ^
>> drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:666:2: error: call to undeclared function 'hrtimer_setup'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     666 |         hrtimer_setup(&sched->grant_timer.timer,
         |         ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:668:26: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     668 |                       CLOCK_MONOTONIC, HRTIMER_MODE_REL);
         |                                        ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:671:26: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     671 |                       CLOCK_MONOTONIC, HRTIMER_MODE_REL);
         |                                        ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:674:26: error: use of undeclared identifier 'HRTIMER_MODE_REL'
     674 |                       CLOCK_MONOTONIC, HRTIMER_MODE_REL);
         |                                        ^
   drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c:807:2: error: call to undeclared function 'hrtimer_cancel'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     807 |         hrtimer_cancel(&sched->grant_timer.timer);
         |         ^
   15 errors generated.


vim +/hrtimer_start +223 drivers/gpu/drm/panthor/arbitration/panthor_arbitration_sched.c

   207	
   208	static void arb_sched_stop_locked(struct panthor_arbitration_sched *sched)
   209	{
   210		int ret;
   211	
   212		lockdep_assert_held(&sched->lock);
   213	
   214		ret = __arb_sched_stop_locked(sched);
   215		if (!ret) {
   216			if (sched->phase != ARB_SCHED_PHASE_STOPPING) {
   217				dev_warn(sched->dev, "%s: Stop ACKED when not yielding",
   218					 sched->name);
   219				return;
   220			}
   221	
   222			sched->stop_timer.epoch = sched->epoch;
 > 223			hrtimer_start(&sched->stop_timer.timer,
 > 224				      ms_to_ktime(yield_timeout), HRTIMER_MODE_REL);
   225		}
   226	}
   227	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support
  2026-11-10 15:38 ` [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
  2026-11-10 16:10   ` sashiko-bot
@ 2026-11-11  5:43   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: kernel test robot @ 2026-11-11  5:43 UTC (permalink / raw)
  To: Karunika Choo, dri-devel
  Cc: llvm, oe-kbuild-all, nd, Boris Brezillon, Steven Price,
	Liviu Dudau, linux-kernel, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter

Hi Karunika,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on drm/drm-next drm-tip/drm-tip next-20261110]
[cannot apply to linux-review/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20260922-214455 daeinki-drm-exynos/exynos-drm-next drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v7.4-rc2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20261110-153822
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20261110153907.3241978-15-karunika.choo%40arm.com
patch subject: [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20261111/202611111301.KUTVMgUg-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project e73e336290028f26553b46c9fba844c13da38c9c)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261111/202611111301.KUTVMgUg-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611111301.KUTVMgUg-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:328:12: error: call to undeclared function 'irq_get_irq_data'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     328 |         irqdata = irq_get_irq_data(irq);
         |                   ^
   drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:328:12: note: did you mean 'irq_set_irq_wake'?
   include/linux/interrupt.h:486:12: note: 'irq_set_irq_wake' declared here
     486 | extern int irq_set_irq_wake(unsigned int irq, unsigned int on);
         |            ^
>> drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:328:10: error: incompatible integer to pointer conversion assigning to 'struct irq_data *' from 'int' [-Wint-conversion]
     328 |         irqdata = irq_get_irq_data(irq);
         |                 ^ ~~~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c:330:15: error: call to undeclared function 'irqd_get_trigger_type'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     330 |                 irq_flags = irqd_get_trigger_type(irqdata);
         |                             ^
   3 errors generated.


vim +/irq_get_irq_data +328 drivers/gpu/drm/panthor/arbitration/panthor_partition_control.c

   278	
   279	static int partition_control_init(struct panthor_arbitration *adev, int i)
   280	{
   281		struct panthor_partition_control *pc;
   282		struct device *dev = adev->dev;
   283		struct irq_data *irqdata;
   284		struct resource *res;
   285		void __iomem *iomem;
   286		u32 irq_flags = 0;
   287		char *name;
   288		int irq;
   289		int ret;
   290	
   291		if (adev->pc[i])
   292			return dev_warn_probe(dev, -ENODEV,
   293					      "partition %u already present", i);
   294	
   295		pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
   296		if (!pc)
   297			return -ENOMEM;
   298	
   299		name = devm_kasprintf(dev, GFP_KERNEL, "pc%d", i);
   300		if (!name)
   301			return -ENOMEM;
   302	
   303		res = platform_get_resource_byname(to_platform_device(dev),
   304						   IORESOURCE_MEM, name);
   305		if (!res)
   306			return -ENODEV;
   307	
   308		iomem = devm_ioremap_resource(dev, res);
   309		if (IS_ERR(iomem))
   310			return dev_err_probe(dev, PTR_ERR(iomem), "%s: Failed to ioremap", name);
   311	
   312		pc->name = name;
   313		pc->dev = dev;
   314		pc->iomem = iomem;
   315	
   316		spin_lock_init(&pc->irq_lock);
   317		pc->irq_suspended = true;
   318	
   319		spin_lock_init(&pc->lock);
   320		pc->current_aw = AW_UNASSIGNED;
   321	
   322		irq = platform_get_irq_byname(to_platform_device(dev), name);
   323		if (irq < 0)
   324			return irq;
   325	
   326		pc->irq = irq;
   327	
 > 328		irqdata = irq_get_irq_data(irq);
   329		if (irqdata)
 > 330			irq_flags = irqd_get_trigger_type(irqdata);
   331	
   332		ret = devm_request_irq(
   333			dev, irq, partition_irq_raw_handler, irq_flags | IRQF_SHARED,
   334			devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), pc);
   335		if (ret)
   336			return ret;
   337	
   338		adev->pc[i] = pc;
   339	
   340		partition_irq_resume(pc);
   341	
   342		return 0;
   343	}
   344	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings
  2026-11-10 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
@ 2026-11-11  8:29   ` Krzysztof Kozlowski
  2026-11-11 10:43     ` Karunika Choo
  0 siblings, 1 reply; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-11  8:29 UTC (permalink / raw)
  To: Karunika Choo
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On Tue, Nov 10, 2026 at 03:38:32PM +0000, Karunika Choo wrote:
> Mali 5th-Gen AM GPUs expose an AM_SYSTEM block for GPU-wide control and
> the AM_ASSIGN block for HW resource assignment outside the GPU
> job submission register window.
> 
> Document the system node compatible, register and interrupt resources,
> GPU clock, and assignment properties for HW resource assignment.
> 
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> v2:
> - Add an optional AM_ASSIGN register range. Require this range when the
>   assignments node is present.
> - Describe resource-group assignments for AXI buses, partitions, slices
>   and access windows on partition-capable GPUs.
> - Allow gpu-arbitration child nodes and the #address-cells, #size-cells
>   and ranges properties.
> - Remove the panthor-system node-name restriction.
> - Remove gpu-supply from the schema and example.
> - Add an example with assignments for partition-capable GPU with four
>   resource groups.
> - Move the MAINTAINERS update to the GPU schema patch.
> 
>  .../bindings/gpu/arm,mali-gen5-am-system.yaml | 196 ++++++++++++++++++
>  1 file changed, 196 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
> 
> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
> new file mode 100644
> index 0000000000000..9e0477b8b723a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
> @@ -0,0 +1,196 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +# Copyright 2026 ARM Limited. All rights reserved.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am-system.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM Mali 5th-Gen GPU System Device
> +
> +maintainers:
> +  - Liviu Dudau <liviu.dudau@arm.com>
> +  - Boris Brezillon <boris.brezillon@collabora.com>
> +
> +properties:
> +  compatible:
> +    items:
> +      - const: arm,mali-gen5-am-system
> +
> +  reg:
> +    minItems: 1
> +    maxItems: 2

Why is this flexible? There is no description in the binding which would
explain oddities in choices of hardware representation.

> +
> +  reg-names:
> +    minItems: 1
> +    items:
> +      - const: system
> +      - const: assign
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  dma-coherent: true
> +
> +  "#address-cells": true
> +
> +  "#size-cells": true

Both need to be const or enum.

> +
> +  ranges: true
> +
> +  assignments:

Do not mix nodes with and without bus addressing. That's mess.

> +    type: object
> +    additionalProperties: false
> +    description:
> +      Resource group HW resource assignment. Only valid for partition-capable
> +      GPUs.
> +
> +    properties:
> +      "#address-cells":
> +        const: 1
> +      "#size-cells":
> +        const: 0
> +
> +    required:
> +      - "#address-cells"
> +      - "#size-cells"
> +
> +    patternProperties:
> +      "^resource-group@[0-3]$":
> +        type: object
> +        additionalProperties: false
> +
> +        properties:
> +          reg:
> +            items:
> +              - maximum: 3
> +            description: Resource group number.
> +
> +          arm,axi-bus:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            maximum: 1
> +            default: 0
> +            description:
> +              Static resource-group to AXI bus assignment. Valid field values
> +              are 0 for AXI_A and 1 for AXI_B.
> +
> +          arm,partition-mask:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            maximum: 0xf
> +            default: 0
> +            description:
> +              Static partition-control to resource-group assignment. Bit N
> +              selects partition control ID N. Mask must be disjoint across
> +              resource groups. Unassigned partitions will be assigned to
> +              resource group 0 by default.
> +
> +          arm,slice-mask:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            maximum: 0xff
> +            default: 0
> +            description:
> +              Static slice to resource-group assignment. Bit N selects slice ID
> +              N. Mask must be disjoint across resource groups. Unassigned slices
> +              will be assigned to resource group 0 by default.
> +
> +          arm,access-window-mask:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            maximum: 0xffff
> +            default: 0
> +            description:
> +              Static access-window to resource-group assignment.  Bit N selects
> +              access window ID N. Masks must be disjoint across resource groups.
> +              Unassigned access-windows will be assigned to resource group 0 by
> +              default.
> +
> +        required:
> +          - reg
> +
> +patternProperties:
> +  '^gpu-arbitration@[a-f0-9]+$':
> +    $ref: arm,mali-gen5-am-arbitration.yaml#

There is no such file. You did not really test it.

> +
> +required:
> +  - compatible
> +  - reg
> +  - reg-names
> +  - interrupts
> +  - clocks
> +
> +additionalProperties: false

This goes after allOf: block.

> +
> +allOf:
> +  - if:
> +      required:
> +        - assignments
> +    then:
> +      properties:
> +        reg:
> +          minItems: 2
> +        reg-names:
> +          minItems: 2
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    gpu-system@68000000 {
> +      compatible = "arm,mali-gen5-am-system";
> +      reg = <0x68000000 0x10000>;
> +      reg-names = "system";
> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
> +      clocks = <&mali_gpu_clk>;
> +    };
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    gpu-system@68000000 {
> +      compatible = "arm,mali-gen5-am-system";
> +      reg = <0x68000000 0x10000>, <0x68010000 0x10000>;
> +      reg-names = "system", "assign";
> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
> +      clocks = <&mali_gpu_clk>;
> +
> +      assignments {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        resource-group@0 {
> +          reg = <0>;
> +          arm,axi-bus = <0>;
> +          arm,partition-mask = <0x1>;
> +          arm,slice-mask = <0x3>;
> +          arm,access-window-mask = <0xf>;
> +        };
> +
> +        resource-group@1 {
> +          reg = <1>;
> +          arm,axi-bus = <0>;
> +          arm,partition-mask = <0x2>;
> +          arm,slice-mask = <0xc>;
> +          arm,access-window-mask = <0xf0>;
> +        };
> +
> +        resource-group@2 {
> +          reg = <2>;
> +          arm,axi-bus = <1>;
> +          arm,partition-mask = <0x4>;
> +          arm,slice-mask = <0x30>;
> +          arm,access-window-mask = <0xf00>;
> +        };
> +
> +        resource-group@3 {
> +          reg = <3>;
> +          arm,axi-bus = <1>;
> +          arm,partition-mask = <0x8>;
> +          arm,slice-mask = <0xc0>;
> +          arm,access-window-mask = <0xf000>;
> +        };

Your example is incomplete.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support
  2026-11-10 15:38 ` [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
  2026-11-10 16:07   ` sashiko-bot
@ 2026-11-11  8:30   ` kernel test robot
  1 sibling, 0 replies; 57+ messages in thread
From: kernel test robot @ 2026-11-11  8:30 UTC (permalink / raw)
  To: Karunika Choo, dri-devel
  Cc: llvm, oe-kbuild-all, nd, Boris Brezillon, Steven Price,
	Liviu Dudau, linux-kernel, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter

Hi Karunika,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-misc/drm-misc-next]
[also build test ERROR on drm/drm-next drm-tip/drm-tip next-20261110]
[cannot apply to linux-review/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20260922-214455 daeinki-drm-exynos/exynos-drm-next drm-i915/for-linux-next drm-i915/for-linux-next-fixes linus/master v7.4-rc2]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Karunika-Choo/drm-panthor-Ignore-EOPNOTSUPP-for-shader-present-nvmem-lookup/20261110-153822
base:   https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link:    https://lore.kernel.org/r/20261110153907.3241978-17-karunika.choo%40arm.com
patch subject: [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20261111/202611111629.kkrg6syk-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project e73e336290028f26553b46c9fba844c13da38c9c)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261111/202611111629.kkrg6syk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611111629.kkrg6syk-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c:341:12: error: call to undeclared function 'irq_get_irq_data'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     341 |         irqdata = irq_get_irq_data(irq);
         |                   ^
   drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c:341:12: note: did you mean 'irq_set_irq_wake'?
   include/linux/interrupt.h:486:12: note: 'irq_set_irq_wake' declared here
     486 | extern int irq_set_irq_wake(unsigned int irq, unsigned int on);
         |            ^
>> drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c:341:10: error: incompatible integer to pointer conversion assigning to 'struct irq_data *' from 'int' [-Wint-conversion]
     341 |         irqdata = irq_get_irq_data(irq);
         |                 ^ ~~~~~~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c:343:15: error: call to undeclared function 'irqd_get_trigger_type'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     343 |                 irq_flags = irqd_get_trigger_type(irqdata);
         |                             ^
   3 errors generated.


vim +/irq_get_irq_data +341 drivers/gpu/drm/panthor/arbitration/panthor_resource_group.c

   284	
   285	static int resource_group_init(struct panthor_arbitration *adev, int i)
   286	{
   287		struct panthor_resource_group *rg;
   288		struct device *dev = adev->dev;
   289		struct irq_data *irqdata;
   290		struct resource *res;
   291		void __iomem *iomem;
   292		u32 irq_flags = 0;
   293		char *name;
   294		int irq;
   295		int ret;
   296	
   297		if (adev->rg[i])
   298			return dev_warn_probe(dev, -ENODEV,
   299					      "resource-group %u already present", i);
   300	
   301		rg = devm_kzalloc(dev, sizeof(*rg), GFP_KERNEL);
   302		if (!rg)
   303			return -ENOMEM;
   304	
   305		name = devm_kasprintf(dev, GFP_KERNEL, "rg%d", i);
   306		if (!name)
   307			return -ENOMEM;
   308	
   309		res = platform_get_resource_byname(to_platform_device(dev),
   310						   IORESOURCE_MEM, name);
   311		if (!res)
   312			return -ENODEV;
   313	
   314		iomem = devm_ioremap_resource(dev, res);
   315		if (IS_ERR(iomem))
   316			return dev_err_probe(dev, PTR_ERR(iomem), "%s: Failed to ioremap", name);
   317	
   318		rg->name = name;
   319		rg->dev = dev;
   320		rg->iomem = iomem;
   321	
   322		spin_lock_init(&rg->irq_lock);
   323		rg->irq_suspended = true;
   324	
   325		rg->wq = devm_alloc_ordered_workqueue(dev, "panthor-%s-wq", WQ_FREEZABLE, name);
   326		if (!rg->wq)
   327			return -ENOMEM;
   328	
   329		INIT_DELAYED_WORK(&rg->msg_retry_work, rg_retry_messages);
   330	
   331		for (int i = 0; i < AM_ARB_MAX_AW_COUNT; i++)
   332			panthor_am_msg_init(&rg->aw_data[i].msg,
   333					    rg->iomem + AM_RG_AW_MESSAGE(i));
   334	
   335		irq = platform_get_irq_byname(to_platform_device(dev), name);
   336		if (irq < 0)
   337			return irq;
   338	
   339		rg->irq = irq;
   340	
 > 341		irqdata = irq_get_irq_data(irq);
   342		if (irqdata)
 > 343			irq_flags = irqd_get_trigger_type(irqdata);
   344	
   345		ret = devm_request_irq(
   346			dev, irq, rg_irq_raw_handler, irq_flags | IRQF_SHARED,
   347			devm_kasprintf(dev, GFP_KERNEL, "panthor-%s-irq", name), rg);
   348		if (ret)
   349			return ret;
   350	
   351		adev->rg[i] = rg;
   352	
   353		rg_irq_resume(rg);
   354	
   355		rg_handshake_init(rg);
   356	
   357		return 0;
   358	}
   359	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-10 15:38 ` [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema Karunika Choo
@ 2026-11-11  8:33   ` Krzysztof Kozlowski
  2026-11-11 10:32     ` Karunika Choo
  0 siblings, 1 reply; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-11  8:33 UTC (permalink / raw)
  To: Karunika Choo
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On Tue, Nov 10, 2026 at 03:38:29PM +0000, Karunika Choo wrote:
> Add a separate device-tree schema for Mali 5th-Gen AM GPUs.
> 
> AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
> cooling or GPU supply properties.
> 
> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
> ---
> v2:
> - Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
>   separate arm,mali-gen5-am.yaml schema.
> - Move the AM GPU example to the new schema and add the interrupt
>   binding headers.
> - Add the new schema to the Panthor MAINTAINERS entry.
> 
>  .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
>  MAINTAINERS                                   |  1 +
>  2 files changed, 91 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
> 
> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
> new file mode 100644
> index 0000000000000..6349888e1ae2a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
> @@ -0,0 +1,90 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM Mali 5th Gen AM GPU
> +
> +maintainers:
> +  - Liviu Dudau <liviu.dudau@arm.com>
> +  - Boris Brezillon <boris.brezillon@collabora.com>
> +
> +properties:
> +  $nodename:
> +    pattern: '^gpu@[a-f0-9]+$'

Same comments as last time.

> +
> +  compatible:
> +    const: arm,mali-gen5-am

Same comments as last time. Seems like you just ignore review, so I will
finish here.

Respond to review. Dropping from DT patchwork.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-11  8:33   ` Krzysztof Kozlowski
@ 2026-11-11 10:32     ` Karunika Choo
  2026-11-11 10:41       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-11 10:32 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 08:33, Krzysztof Kozlowski wrote:
> On Tue, Nov 10, 2026 at 03:38:29PM +0000, Karunika Choo wrote:
>> Add a separate device-tree schema for Mali 5th-Gen AM GPUs.
>>
>> AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
>> cooling or GPU supply properties.
>>
>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>> ---
>> v2:
>> - Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
>>   separate arm,mali-gen5-am.yaml schema.
>> - Move the AM GPU example to the new schema and add the interrupt
>>   binding headers.
>> - Add the new schema to the Panthor MAINTAINERS entry.
>>
>>  .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
>>  MAINTAINERS                                   |  1 +
>>  2 files changed, 91 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>> new file mode 100644
>> index 0000000000000..6349888e1ae2a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>> @@ -0,0 +1,90 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: ARM Mali 5th Gen AM GPU
>> +
>> +maintainers:
>> +  - Liviu Dudau <liviu.dudau@arm.com>
>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>> +
>> +properties:
>> +  $nodename:
>> +    pattern: '^gpu@[a-f0-9]+$'
> 
> Same comments as last time.

Sorry, do you mean that 'gpu' not generic enough?

>> +
>> +  compatible:
>> +    const: arm,mali-gen5-am
> 
> Same comments as last time. Seems like you just ignore review, so I will
> finish here.
> 

Hi, we are trying to provide initial support for v15. The vendor 
identifiers will be added once there is an available device.

> Respond to review. Dropping from DT patchwork.
> 
> Best regards,
> Krzysztof
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-11 10:32     ` Karunika Choo
@ 2026-11-11 10:41       ` Krzysztof Kozlowski
  2026-11-11 11:04         ` Karunika Choo
  0 siblings, 1 reply; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-11 10:41 UTC (permalink / raw)
  To: Karunika Choo
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 11:32, Karunika Choo wrote:
> On 11/11/2026 08:33, Krzysztof Kozlowski wrote:
>> On Tue, Nov 10, 2026 at 03:38:29PM +0000, Karunika Choo wrote:
>>> Add a separate device-tree schema for Mali 5th-Gen AM GPUs.
>>>
>>> AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
>>> cooling or GPU supply properties.
>>>
>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>> ---
>>> v2:
>>> - Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
>>>   separate arm,mali-gen5-am.yaml schema.
>>> - Move the AM GPU example to the new schema and add the interrupt
>>>   binding headers.
>>> - Add the new schema to the Panthor MAINTAINERS entry.
>>>
>>>  .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
>>>  MAINTAINERS                                   |  1 +
>>>  2 files changed, 91 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>> new file mode 100644
>>> index 0000000000000..6349888e1ae2a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>> @@ -0,0 +1,90 @@
>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: ARM Mali 5th Gen AM GPU
>>> +
>>> +maintainers:
>>> +  - Liviu Dudau <liviu.dudau@arm.com>
>>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>>> +
>>> +properties:
>>> +  $nodename:
>>> +    pattern: '^gpu@[a-f0-9]+$'
>>
>> Same comments as last time.
> 
> Sorry, do you mean that 'gpu' not generic enough?

I asked to drop the node name completely.

> 
>>> +
>>> +  compatible:
>>> +    const: arm,mali-gen5-am
>>
>> Same comments as last time. Seems like you just ignore review, so I will
>> finish here.
>>
> 
> Hi, we are trying to provide initial support for v15. The vendor 
> identifiers will be added once there is an available device.
> 

So how can this initial support be run? Which board has it? I received
only three patches of entire thread, but if there was DTS I would be
cc-ed. I can't find such patch.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings
  2026-11-11  8:29   ` Krzysztof Kozlowski
@ 2026-11-11 10:43     ` Karunika Choo
  2026-11-12  8:32       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-11 10:43 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 08:29, Krzysztof Kozlowski wrote:
> On Tue, Nov 10, 2026 at 03:38:32PM +0000, Karunika Choo wrote:
>> Mali 5th-Gen AM GPUs expose an AM_SYSTEM block for GPU-wide control and
>> the AM_ASSIGN block for HW resource assignment outside the GPU
>> job submission register window.
>>
>> Document the system node compatible, register and interrupt resources,
>> GPU clock, and assignment properties for HW resource assignment.
>>
>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>> ---
>> v2:
>> - Add an optional AM_ASSIGN register range. Require this range when the
>>   assignments node is present.
>> - Describe resource-group assignments for AXI buses, partitions, slices
>>   and access windows on partition-capable GPUs.
>> - Allow gpu-arbitration child nodes and the #address-cells, #size-cells
>>   and ranges properties.
>> - Remove the panthor-system node-name restriction.
>> - Remove gpu-supply from the schema and example.
>> - Add an example with assignments for partition-capable GPU with four
>>   resource groups.
>> - Move the MAINTAINERS update to the GPU schema patch.
>>
>>  .../bindings/gpu/arm,mali-gen5-am-system.yaml | 196 ++++++++++++++++++
>>  1 file changed, 196 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>> new file mode 100644
>> index 0000000000000..9e0477b8b723a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>> @@ -0,0 +1,196 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +# Copyright 2026 ARM Limited. All rights reserved.
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am-system.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: ARM Mali 5th-Gen GPU System Device
>> +
>> +maintainers:
>> +  - Liviu Dudau <liviu.dudau@arm.com>
>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>> +
>> +properties:
>> +  compatible:
>> +    items:
>> +      - const: arm,mali-gen5-am-system
>> +
>> +  reg:
>> +    minItems: 1
>> +    maxItems: 2
> 
> Why is this flexible? There is no description in the binding which would
> explain oddities in choices of hardware representation.

I will restore the description that was previously asked to be dropped.

>> +
>> +  reg-names:
>> +    minItems: 1
>> +    items:
>> +      - const: system
>> +      - const: assign
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    maxItems: 1
>> +
>> +  dma-coherent: true
>> +
>> +  "#address-cells": true
>> +
>> +  "#size-cells": true
> 
> Both need to be const or enum.
> 
>> +
>> +  ranges: true
>> +
>> +  assignments:
> 
> Do not mix nodes with and without bus addressing. That's mess.

Hi, I was asked to use a more generic DT such that should the
hardware registers change in the future we can still retain
some compatibility. Is it necessarily a problem?

> 
>> +    type: object
>> +    additionalProperties: false
>> +    description:
>> +      Resource group HW resource assignment. Only valid for partition-capable
>> +      GPUs.
>> +
>> +    properties:
>> +      "#address-cells":
>> +        const: 1
>> +      "#size-cells":
>> +        const: 0
>> +
>> +    required:
>> +      - "#address-cells"
>> +      - "#size-cells"
>> +
>> +    patternProperties:
>> +      "^resource-group@[0-3]$":
>> +        type: object
>> +        additionalProperties: false
>> +
>> +        properties:
>> +          reg:
>> +            items:
>> +              - maximum: 3
>> +            description: Resource group number.
>> +
>> +          arm,axi-bus:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            maximum: 1
>> +            default: 0
>> +            description:
>> +              Static resource-group to AXI bus assignment. Valid field values
>> +              are 0 for AXI_A and 1 for AXI_B.
>> +
>> +          arm,partition-mask:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            maximum: 0xf
>> +            default: 0
>> +            description:
>> +              Static partition-control to resource-group assignment. Bit N
>> +              selects partition control ID N. Mask must be disjoint across
>> +              resource groups. Unassigned partitions will be assigned to
>> +              resource group 0 by default.
>> +
>> +          arm,slice-mask:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            maximum: 0xff
>> +            default: 0
>> +            description:
>> +              Static slice to resource-group assignment. Bit N selects slice ID
>> +              N. Mask must be disjoint across resource groups. Unassigned slices
>> +              will be assigned to resource group 0 by default.
>> +
>> +          arm,access-window-mask:
>> +            $ref: /schemas/types.yaml#/definitions/uint32
>> +            maximum: 0xffff
>> +            default: 0
>> +            description:
>> +              Static access-window to resource-group assignment.  Bit N selects
>> +              access window ID N. Masks must be disjoint across resource groups.
>> +              Unassigned access-windows will be assigned to resource group 0 by
>> +              default.
>> +
>> +        required:
>> +          - reg
>> +
>> +patternProperties:
>> +  '^gpu-arbitration@[a-f0-9]+$':
>> +    $ref: arm,mali-gen5-am-arbitration.yaml#
> 
> There is no such file. You did not really test it.

Sorry, this was included from a later patch that
introduces this file. I will move it there in v3.

> 
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - reg-names
>> +  - interrupts
>> +  - clocks
>> +
>> +additionalProperties: false
> 
> This goes after allOf: block.
> 
>> +
>> +allOf:
>> +  - if:
>> +      required:
>> +        - assignments
>> +    then:
>> +      properties:
>> +        reg:
>> +          minItems: 2
>> +        reg-names:
>> +          minItems: 2
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/irq.h>
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> +    gpu-system@68000000 {
>> +      compatible = "arm,mali-gen5-am-system";
>> +      reg = <0x68000000 0x10000>;
>> +      reg-names = "system";
>> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
>> +      clocks = <&mali_gpu_clk>;
>> +    };
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/irq.h>
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> +    gpu-system@68000000 {
>> +      compatible = "arm,mali-gen5-am-system";
>> +      reg = <0x68000000 0x10000>, <0x68010000 0x10000>;
>> +      reg-names = "system", "assign";
>> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
>> +      clocks = <&mali_gpu_clk>;
>> +
>> +      assignments {
>> +        #address-cells = <1>;
>> +        #size-cells = <0>;
>> +
>> +        resource-group@0 {
>> +          reg = <0>;
>> +          arm,axi-bus = <0>;
>> +          arm,partition-mask = <0x1>;
>> +          arm,slice-mask = <0x3>;
>> +          arm,access-window-mask = <0xf>;
>> +        };
>> +
>> +        resource-group@1 {
>> +          reg = <1>;
>> +          arm,axi-bus = <0>;
>> +          arm,partition-mask = <0x2>;
>> +          arm,slice-mask = <0xc>;
>> +          arm,access-window-mask = <0xf0>;
>> +        };
>> +
>> +        resource-group@2 {
>> +          reg = <2>;
>> +          arm,axi-bus = <1>;
>> +          arm,partition-mask = <0x4>;
>> +          arm,slice-mask = <0x30>;
>> +          arm,access-window-mask = <0xf00>;
>> +        };
>> +
>> +        resource-group@3 {
>> +          reg = <3>;
>> +          arm,axi-bus = <1>;
>> +          arm,partition-mask = <0x8>;
>> +          arm,slice-mask = <0xc0>;
>> +          arm,access-window-mask = <0xf000>;
>> +        };
> 
> Your example is incomplete.

Could you clarify what specifically is missing?

> 
> Best regards,
> Krzysztof
> 


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-11 10:41       ` Krzysztof Kozlowski
@ 2026-11-11 11:04         ` Karunika Choo
  2026-11-12  8:36           ` Krzysztof Kozlowski
  0 siblings, 1 reply; 57+ messages in thread
From: Karunika Choo @ 2026-11-11 11:04 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 10:41, Krzysztof Kozlowski wrote:
> On 11/11/2026 11:32, Karunika Choo wrote:
>> On 11/11/2026 08:33, Krzysztof Kozlowski wrote:
>>> On Tue, Nov 10, 2026 at 03:38:29PM +0000, Karunika Choo wrote:
>>>> Add a separate device-tree schema for Mali 5th-Gen AM GPUs.
>>>>
>>>> AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
>>>> cooling or GPU supply properties.
>>>>
>>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>>> ---
>>>> v2:
>>>> - Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
>>>>   separate arm,mali-gen5-am.yaml schema.
>>>> - Move the AM GPU example to the new schema and add the interrupt
>>>>   binding headers.
>>>> - Add the new schema to the Panthor MAINTAINERS entry.
>>>>
>>>>  .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
>>>>  MAINTAINERS                                   |  1 +
>>>>  2 files changed, 91 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>> new file mode 100644
>>>> index 0000000000000..6349888e1ae2a
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>> @@ -0,0 +1,90 @@
>>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>>> +%YAML 1.2
>>>> +---
>>>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>> +
>>>> +title: ARM Mali 5th Gen AM GPU
>>>> +
>>>> +maintainers:
>>>> +  - Liviu Dudau <liviu.dudau@arm.com>
>>>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>>>> +
>>>> +properties:
>>>> +  $nodename:
>>>> +    pattern: '^gpu@[a-f0-9]+$'
>>>
>>> Same comments as last time.
>>
>> Sorry, do you mean that 'gpu' not generic enough?
> 
> I asked to drop the node name completely.
> 

ah! Understood. Will do that in v3.

>>
>>>> +
>>>> +  compatible:
>>>> +    const: arm,mali-gen5-am
>>>
>>> Same comments as last time. Seems like you just ignore review, so I will
>>> finish here.
>>>
>>
>> Hi, we are trying to provide initial support for v15. The vendor 
>> identifiers will be added once there is an available device.
>>
> 
> So how can this initial support be run? Which board has it? I received
> only three patches of entire thread, but if there was DTS I would be
> cc-ed. I can't find such patch.

Apologies for the trouble. This was tested on internal FPGAs, but let
me try to see if I can add a representative DTS for one of our publicly
available FVP platforms in v3.

> 
> Best regards,
> Krzysztof


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings
  2026-11-11 10:43     ` Karunika Choo
@ 2026-11-12  8:32       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-12  8:32 UTC (permalink / raw)
  To: Karunika Choo
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 11:43, Karunika Choo wrote:
> On 11/11/2026 08:29, Krzysztof Kozlowski wrote:
>> On Tue, Nov 10, 2026 at 03:38:32PM +0000, Karunika Choo wrote:
>>> Mali 5th-Gen AM GPUs expose an AM_SYSTEM block for GPU-wide control and
>>> the AM_ASSIGN block for HW resource assignment outside the GPU
>>> job submission register window.
>>>
>>> Document the system node compatible, register and interrupt resources,
>>> GPU clock, and assignment properties for HW resource assignment.
>>>
>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>> ---
>>> v2:
>>> - Add an optional AM_ASSIGN register range. Require this range when the
>>>   assignments node is present.
>>> - Describe resource-group assignments for AXI buses, partitions, slices
>>>   and access windows on partition-capable GPUs.
>>> - Allow gpu-arbitration child nodes and the #address-cells, #size-cells
>>>   and ranges properties.
>>> - Remove the panthor-system node-name restriction.
>>> - Remove gpu-supply from the schema and example.
>>> - Add an example with assignments for partition-capable GPU with four
>>>   resource groups.
>>> - Move the MAINTAINERS update to the GPU schema patch.
>>>
>>>  .../bindings/gpu/arm,mali-gen5-am-system.yaml | 196 ++++++++++++++++++
>>>  1 file changed, 196 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>>> new file mode 100644
>>> index 0000000000000..9e0477b8b723a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am-system.yaml
>>> @@ -0,0 +1,196 @@
>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>> +# Copyright 2026 ARM Limited. All rights reserved.
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am-system.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: ARM Mali 5th-Gen GPU System Device
>>> +
>>> +maintainers:
>>> +  - Liviu Dudau <liviu.dudau@arm.com>
>>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>>> +
>>> +properties:
>>> +  compatible:
>>> +    items:
>>> +      - const: arm,mali-gen5-am-system
>>> +
>>> +  reg:
>>> +    minItems: 1
>>> +    maxItems: 2
>>
>> Why is this flexible? There is no description in the binding which would
>> explain oddities in choices of hardware representation.
> 
> I will restore the description that was previously asked to be dropped.

Hm, let me open the cover letter to find lore link to previous
discussion and check... ah wait, no cover letter!

> 
>>> +
>>> +  reg-names:
>>> +    minItems: 1
>>> +    items:
>>> +      - const: system
>>> +      - const: assign
>>> +
>>> +  interrupts:
>>> +    maxItems: 1
>>> +
>>> +  clocks:
>>> +    maxItems: 1
>>> +
>>> +  dma-coherent: true
>>> +
>>> +  "#address-cells": true
>>> +
>>> +  "#size-cells": true
>>
>> Both need to be const or enum.
>>
>>> +
>>> +  ranges: true
>>> +
>>> +  assignments:
>>
>> Do not mix nodes with and without bus addressing. That's mess.
> 
> Hi, I was asked to use a more generic DT such that should the

By whom?

> hardware registers change in the future we can still retain
> some compatibility. Is it necessarily a problem?

Bindings are not generic, but specific.

...

>>> +patternProperties:
>>> +  '^gpu-arbitration@[a-f0-9]+$':
>>> +    $ref: arm,mali-gen5-am-arbitration.yaml#
>>
>> There is no such file. You did not really test it.
> 
> Sorry, this was included from a later patch that
> introduces this file. I will move it there in v3.
> 
>>
>>> +
>>> +required:
>>> +  - compatible
>>> +  - reg
>>> +  - reg-names
>>> +  - interrupts
>>> +  - clocks
>>> +
>>> +additionalProperties: false
>>
>> This goes after allOf: block.
>>
>>> +
>>> +allOf:
>>> +  - if:
>>> +      required:
>>> +        - assignments
>>> +    then:
>>> +      properties:
>>> +        reg:
>>> +          minItems: 2
>>> +        reg-names:
>>> +          minItems: 2
>>> +
>>> +examples:
>>> +  - |
>>> +    #include <dt-bindings/interrupt-controller/irq.h>
>>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>>> +
>>> +    gpu-system@68000000 {
>>> +      compatible = "arm,mali-gen5-am-system";
>>> +      reg = <0x68000000 0x10000>;
>>> +      reg-names = "system";
>>> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
>>> +      clocks = <&mali_gpu_clk>;
>>> +    };
>>> +  - |
>>> +    #include <dt-bindings/interrupt-controller/irq.h>
>>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>>> +
>>> +    gpu-system@68000000 {
>>> +      compatible = "arm,mali-gen5-am-system";
>>> +      reg = <0x68000000 0x10000>, <0x68010000 0x10000>;
>>> +      reg-names = "system", "assign";
>>> +      interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
>>> +      clocks = <&mali_gpu_clk>;
>>> +
>>> +      assignments {
>>> +        #address-cells = <1>;
>>> +        #size-cells = <0>;
>>> +
>>> +        resource-group@0 {
>>> +          reg = <0>;
>>> +          arm,axi-bus = <0>;
>>> +          arm,partition-mask = <0x1>;
>>> +          arm,slice-mask = <0x3>;
>>> +          arm,access-window-mask = <0xf>;
>>> +        };
>>> +
>>> +        resource-group@1 {
>>> +          reg = <1>;
>>> +          arm,axi-bus = <0>;
>>> +          arm,partition-mask = <0x2>;
>>> +          arm,slice-mask = <0xc>;
>>> +          arm,access-window-mask = <0xf0>;
>>> +        };
>>> +
>>> +        resource-group@2 {
>>> +          reg = <2>;
>>> +          arm,axi-bus = <1>;
>>> +          arm,partition-mask = <0x4>;
>>> +          arm,slice-mask = <0x30>;
>>> +          arm,access-window-mask = <0xf00>;
>>> +        };
>>> +
>>> +        resource-group@3 {
>>> +          reg = <3>;
>>> +          arm,axi-bus = <1>;
>>> +          arm,partition-mask = <0x8>;
>>> +          arm,slice-mask = <0xc0>;
>>> +          arm,access-window-mask = <0xf000>;
>>> +        };
>>
>> Your example is incomplete.
> 
> Could you clarify what specifically is missing?

ll the properties you added in the binding.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema
  2026-11-11 11:04         ` Karunika Choo
@ 2026-11-12  8:36           ` Krzysztof Kozlowski
  0 siblings, 0 replies; 57+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-12  8:36 UTC (permalink / raw)
  To: Karunika Choo
  Cc: dri-devel, nd, Boris Brezillon, Steven Price, Liviu Dudau,
	linux-kernel, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
	David Airlie, Simona Vetter, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, devicetree

On 11/11/2026 12:04, Karunika Choo wrote:
> On 11/11/2026 10:41, Krzysztof Kozlowski wrote:
>> On 11/11/2026 11:32, Karunika Choo wrote:
>>> On 11/11/2026 08:33, Krzysztof Kozlowski wrote:
>>>> On Tue, Nov 10, 2026 at 03:38:29PM +0000, Karunika Choo wrote:
>>>>> Add a separate device-tree schema for Mali 5th-Gen AM GPUs.
>>>>>
>>>>> AM_GOVERNOR controls GPU frequency. The GPU node does not accept OPP,
>>>>> cooling or GPU supply properties.
>>>>>
>>>>> Signed-off-by: Karunika Choo <karunika.choo@arm.com>
>>>>> ---
>>>>> v2:
>>>>> - Move arm,mali-gen5-am support from arm,mali-valhall-csf.yaml to a
>>>>>   separate arm,mali-gen5-am.yaml schema.
>>>>> - Move the AM GPU example to the new schema and add the interrupt
>>>>>   binding headers.
>>>>> - Add the new schema to the Panthor MAINTAINERS entry.
>>>>>
>>>>>  .../bindings/gpu/arm,mali-gen5-am.yaml        | 90 +++++++++++++++++++
>>>>>  MAINTAINERS                                   |  1 +
>>>>>  2 files changed, 91 insertions(+)
>>>>>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>>> new file mode 100644
>>>>> index 0000000000000..6349888e1ae2a
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-gen5-am.yaml
>>>>> @@ -0,0 +1,90 @@
>>>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>>>> +%YAML 1.2
>>>>> +---
>>>>> +$id: http://devicetree.org/schemas/gpu/arm,mali-gen5-am.yaml#
>>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>>> +
>>>>> +title: ARM Mali 5th Gen AM GPU
>>>>> +
>>>>> +maintainers:
>>>>> +  - Liviu Dudau <liviu.dudau@arm.com>
>>>>> +  - Boris Brezillon <boris.brezillon@collabora.com>
>>>>> +
>>>>> +properties:
>>>>> +  $nodename:
>>>>> +    pattern: '^gpu@[a-f0-9]+$'
>>>>
>>>> Same comments as last time.
>>>
>>> Sorry, do you mean that 'gpu' not generic enough?
>>
>> I asked to drop the node name completely.
>>
> 
> ah! Understood. Will do that in v3.
> 
>>>
>>>>> +
>>>>> +  compatible:
>>>>> +    const: arm,mali-gen5-am
>>>>
>>>> Same comments as last time. Seems like you just ignore review, so I will
>>>> finish here.
>>>>
>>>
>>> Hi, we are trying to provide initial support for v15. The vendor 
>>> identifiers will be added once there is an available device.
>>>
>>
>> So how can this initial support be run? Which board has it? I received
>> only three patches of entire thread, but if there was DTS I would be
>> cc-ed. I can't find such patch.
> 
> Apologies for the trouble. This was tested on internal FPGAs, but let
> me try to see if I can add a representative DTS for one of our publicly
> available FVP platforms in v3.

So it cannot run on its own, because internal FPGA is a not a complete
and final system. I assume you need some SoC integration, so you need a
front compatible. If it is unknown, then it has to be {} (see other
cases how we represented unknown integration).

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2026-11-12  8:36 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 15:38 [PATCH v2 00/28] drm/panthor: Add Mali v15 virtualization support Karunika Choo
2026-11-10 15:38 ` [PATCH v2 01/28] drm/panthor: Ignore -EOPNOTSUPP for shader-present nvmem lookup Karunika Choo
2026-11-10 15:38 ` [PATCH v2 02/28] drm/panthor: Rename neural accelerator feature macro to NX Karunika Choo
2026-11-10 15:47   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 03/28] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
2026-11-10 15:38 ` [PATCH v2 04/28] drm/panthor: Parse and store GPU_ID fields Karunika Choo
2026-11-10 15:38 ` [PATCH v2 05/28] drm/panthor: Add 64-bit GPU_ID decoding for v15 GPUs Karunika Choo
2026-11-10 15:38 ` [PATCH v2 06/28] drm/panthor: Move register base offsets to the HW description Karunika Choo
2026-11-10 15:38 ` [PATCH v2 07/28] drm/panthor: Derive MMU AS register addresses from base and stride Karunika Choo
2026-11-10 15:38 ` [PATCH v2 08/28] dt-bindings: gpu: mali-gen5-am: Add Mali 5th-Gen AM schema Karunika Choo
2026-11-11  8:33   ` Krzysztof Kozlowski
2026-11-11 10:32     ` Karunika Choo
2026-11-11 10:41       ` Krzysztof Kozlowski
2026-11-11 11:04         ` Karunika Choo
2026-11-12  8:36           ` Krzysztof Kozlowski
2026-11-10 15:38 ` [PATCH v2 09/28] drm/panthor: Add Mali v15 hardware support Karunika Choo
2026-11-10 15:38 ` [PATCH v2 10/28] drm/panthor: Skip devfreq when no OPP table is present Karunika Choo
2026-11-10 16:03   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
2026-11-11  8:29   ` Krzysztof Kozlowski
2026-11-11 10:43     ` Karunika Choo
2026-11-12  8:32       ` Krzysztof Kozlowski
2026-11-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
2026-11-10 15:57   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 13/28] dt-bindings: gpu: panthor: Document panthor-arbitration bindings Karunika Choo
2026-11-10 15:38 ` [PATCH v2 14/28] drm/panthor: Add AM_PARTITION_CONTROL support Karunika Choo
2026-11-10 16:10   ` sashiko-bot
2026-11-11  5:43   ` kernel test robot
2026-11-10 15:38 ` [PATCH v2 15/28] drm/panthor: Add AM message helpers Karunika Choo
2026-11-10 15:38 ` [PATCH v2 16/28] drm/panthor: Add AM_RESOURCE_GROUP support Karunika Choo
2026-11-10 16:07   ` sashiko-bot
2026-11-11  8:30   ` kernel test robot
2026-11-10 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
2026-11-10 16:05   ` sashiko-bot
2026-11-11  0:43   ` kernel test robot
2026-11-10 15:38 ` [PATCH v2 18/28] drm/panthor: Route arbitration events Karunika Choo
2026-11-10 16:19   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
2026-11-10 16:03   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
2026-11-10 16:16   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle Karunika Choo
2026-11-10 16:15   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
2026-11-10 16:20   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
2026-11-10 16:23   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
2026-11-10 16:24   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows Karunika Choo
2026-11-10 16:50   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
2026-11-10 16:30   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset Karunika Choo
2026-11-10 16:44   ` sashiko-bot
2026-11-10 15:38 ` [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration Karunika Choo
2026-11-10 16:22   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.