[PATCH v1 11/27] drm/panthor: Add AM_SYSTEM platform driver
From: Karunika Choo
Date: Tue Sep 22 2026 - 16:53:27 EST
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@xxxxxxx>
---
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 | 304 ++++++++++++++++++
4 files changed, 328 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..1f25c7bad49b5
--- /dev/null
+++ b/drivers/gpu/drm/panthor/system/panthor_system_drv.c
@@ -0,0 +1,304 @@
+// 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);
+
+ panthor_system_clk_suspend(sdev);
+ return panthor_system_regulator_suspend(sdev);
+}
+
+static int panthor_system_resume(struct device *dev)
+{
+ struct panthor_system *sdev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = panthor_system_regulator_resume(sdev);
+ if (ret)
+ return ret;
+
+ ret = panthor_system_clk_resume(sdev);
+ if (ret)
+ goto err_regulator_suspend;
+
+ return 0;
+
+err_regulator_suspend:
+ panthor_system_regulator_suspend(sdev);
+
+ return ret;
+}
+
+static int panthor_system_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct panthor_system *sdev;
+ void __iomem *iomem;
+ int ret;
+
+ sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return -ENOMEM;
+
+ iomem = devm_platform_ioremap_resource_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);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM");
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to resume device");
+
+ ret = panthor_system_reset(sdev);
+ if (ret) {
+ dev_err(dev, "Failed to reset GPU");
+ goto err_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:
+ pm_runtime_put_sync_suspend(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);
+
+ if (ret >= 0)
+ pm_runtime_put_sync_suspend(dev);
+ else
+ pm_runtime_set_suspended(dev);
+}
+
+static const struct dev_pm_ops panthor_system_pm_ops = {
+ .suspend = pm_runtime_force_suspend,
+ .resume = pm_runtime_force_resume,
+ .runtime_suspend = panthor_system_suspend,
+ .runtime_resume = panthor_system_resume,
+};
+
+static const struct of_device_id panthor_system_dt_match[] = {
+ { .compatible = "arm,mali-gen5-am-system" },
+ {}
+};
+
+static struct platform_driver panthor_system_driver = {
+ .probe = panthor_system_probe,
+ .remove = panthor_system_remove,
+ .driver = {
+ .name = "panthor_system",
+ .pm = pm_ptr(&panthor_system_pm_ops),
+ .of_match_table = panthor_system_dt_match,
+ },
+};
+
+module_platform_driver(panthor_system_driver);
+
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_DESCRIPTION("GPU system driver for AM GPUs");
+MODULE_LICENSE("Dual MIT/GPL");
--
2.43.0