* [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; 31+ 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] 31+ 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; 31+ 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 [flat|nested] 31+ 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:38 ` [PATCH v2 03/28] drm/panthor: Move register access helpers out of panthor_device.h Karunika Choo
` (25 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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-10 15:38 ` [PATCH v2 09/28] drm/panthor: Add Mali v15 hardware support Karunika Choo
` (19 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
` (17 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
` (16 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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:38 ` [PATCH v2 13/28] dt-bindings: gpu: panthor: Document panthor-arbitration bindings Karunika Choo
` (15 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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-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, 1 reply; 31+ 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 [flat|nested] 31+ 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; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
` (11 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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-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, 1 reply; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
` (9 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
` (8 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 21/28] drm/panthor: Request AW to yield GPU access on idle Karunika Choo
` (7 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
` (6 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
` (5 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
` (4 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 25/28] drm/panthor: Route HW component PM through access windows Karunika Choo
` (3 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
` (2 subsequent siblings)
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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 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, 0 replies; 31+ 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 [flat|nested] 31+ 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 15:38 ` [PATCH v2 28/28] drm/panthor: Reset partitions before starting arbitration Karunika Choo
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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
27 siblings, 0 replies; 31+ 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 [flat|nested] 31+ 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-11 0:43 ` kernel test robot
0 siblings, 0 replies; 31+ 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] 31+ 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-11 5:43 ` kernel test robot
0 siblings, 0 replies; 31+ 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] 31+ messages in thread
end of thread, other threads:[~2026-11-11 5:44 UTC | newest]
Thread overview: 31+ 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: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-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 15:38 ` [PATCH v2 11/28] dt-bindings: gpu: panthor: Document AM system bindings Karunika Choo
2026-11-10 15:38 ` [PATCH v2 12/28] drm/panthor: Add AM_SYSTEM platform driver Karunika Choo
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-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 15:38 ` [PATCH v2 17/28] drm/panthor: Add arbitration scheduler Karunika Choo
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 15:38 ` [PATCH v2 19/28] drm/panthor: Add AW assignment tracking Karunika Choo
2026-11-10 15:38 ` [PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt Karunika Choo
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 ` [PATCH v2 22/28] drm/panthor: Add access-window support Karunika Choo
2026-11-10 15:38 ` [PATCH v2 23/28] drm/panthor: Synchronize HW component PM transitions Karunika Choo
2026-11-10 15:38 ` [PATCH v2 24/28] drm/panthor: Quiesce scheduler tick during system suspend Karunika Choo
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 ` [PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits Karunika Choo
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®