Add the VM-side access-window component for v15 GPUs. The new code handles AM message handshakes, requests GPU access from the arbiter, waits for the window-open interrupt, and yields access on suspend.
Split panthor_hw_init into bind device and initialize gpu_info after panthor_aw has ensured GPU access. Signed-off-by: Karunika Choo <[email protected]> --- drivers/gpu/drm/panthor/Makefile | 1 + drivers/gpu/drm/panthor/panthor_aw.c | 421 +++++++++++++++++++++++ drivers/gpu/drm/panthor/panthor_aw.h | 42 +++ drivers/gpu/drm/panthor/panthor_device.c | 15 +- drivers/gpu/drm/panthor/panthor_device.h | 4 + drivers/gpu/drm/panthor/panthor_hw.c | 8 +- drivers/gpu/drm/panthor/panthor_hw.h | 2 + 7 files changed, 486 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/panthor/panthor_aw.c create mode 100644 drivers/gpu/drm/panthor/panthor_aw.h diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile index 5d4d0ae64952..aa48212653dc 100644 --- a/drivers/gpu/drm/panthor/Makefile +++ b/drivers/gpu/drm/panthor/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 or MIT panthor-y := \ + panthor_aw.o \ panthor_devfreq.o \ panthor_device.o \ panthor_drv.o \ diff --git a/drivers/gpu/drm/panthor/panthor_aw.c b/drivers/gpu/drm/panthor/panthor_aw.c new file mode 100644 index 000000000000..00a52175f3a3 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_aw.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#include <linux/platform_device.h> + +#include <drm/drm_managed.h> +#include <drm/drm_print.h> +#include <drm/drm_drv.h> + +#include "panthor_am_msg.h" +#include "panthor_aw.h" +#include "panthor_device.h" +#include "panthor_fw.h" +#include "panthor_hw.h" +#include "panthor_sched.h" + +#include "panthor_trace.h" + +#define WINDOW_CONTROL_BASE 0x0 +#define WINDOW_CONTROL_SIZE 0x1000 + +#define WINDOW_STATUS 0x43c +#define WINDOW_STATUS_IRQ_WINDOW_CONTROL BIT(0) +#define WINDOW_STATUS_IRQ_GPU_CONTROL BIT(1) +#define WINDOW_STATUS_IRQ_GPU_POWER BIT(2) +#define WINDOW_STATUS_IRQ_JOB_CONTROL BIT(3) +#define WINDOW_STATUS_IRQ_MMU_CONTROL BIT(4) +#define WINDOW_STATUS_WINDOW_OPEN BIT(31) + +#define WINDOW_INT_BASE 0x440 +#define WINDOW_IRQ_MESSAGE BIT(0) +#define WINDOW_IRQ_INVALID_ACCESS BIT(1) +#define WINDOW_IRQ_WINDOW_OPENING BIT(2) +#define WINDOW_IRQ_WINDOW_OPENED BIT(3) +#define WINDOW_IRQ_WINDOW_CLOSED BIT(4) + +#define WINDOW_MESSAGE_BASE 0x460 + +#define WINDOW_IRQ_MASK \ + (WINDOW_IRQ_MESSAGE | WINDOW_IRQ_WINDOW_OPENED | WINDOW_IRQ_WINDOW_CLOSED) + +#define PANTHOR_AW_HANDSHAKE_TIMEOUT_MS (5 * MSEC_PER_SEC) +#define PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS (5 * MSEC_PER_SEC) +#define PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS (5 * MSEC_PER_SEC) + +#define panthor_aw_wait_cond(_aw, _cond, _timeout_ms) \ +({ \ + int __ret = 0; \ + if (!wait_event_timeout((_aw)->waitqueue, _cond, \ + msecs_to_jiffies(_timeout_ms))) \ + if (!(_cond)) \ + __ret = -ETIMEDOUT; \ + __ret; \ +}) + +struct panthor_aw { + /** @ptdev: Pointer to Panthor device */ + struct panthor_device *ptdev; + + /** @iomem: CPU mapping of WINDOW_CONTROL iomem region */ + void __iomem *iomem; + + /** @irq: IRQ data */ + struct panthor_irq irq; + + /** @msg: Messaging data */ + struct panthor_am_msg msg; + + /** @waitqueue: Event wait queue. Not IRQ safe */ + wait_queue_head_t waitqueue; + + /** @state: Current state of the AW */ + atomic_t state; + + /** @wq: Scheduler workqueue to dispatch events */ + struct workqueue_struct *wq; + + /** @msg_retry_work: Work to resend messages */ + struct work_struct msg_retry_work; +}; + +static bool panthor_aw_is_open(struct panthor_aw *aw) +{ + return gpu_read(aw->iomem, WINDOW_STATUS) & WINDOW_STATUS_WINDOW_OPEN; +} + +static void panthor_aw_state_set(struct panthor_aw *aw, enum aw_states new) +{ + atomic_set(&aw->state, new); + + wake_up_all(&aw->waitqueue); +} + +static bool panthor_aw_state_try_set(struct panthor_aw *aw, + enum aw_states expected, + enum aw_states new) +{ + int old_state = atomic_cmpxchg(&aw->state, expected, new); + bool res = (old_state == expected); + + if (res) + wake_up_all(&aw->waitqueue); + + return res; +} + +static int panthor_aw_state_wait(struct panthor_aw *aw, enum aw_states target, u32 timeout_ms) +{ + return panthor_aw_wait_cond(aw, atomic_read(&aw->state) == target, timeout_ms); +} + +static int panthor_aw_state_wait_transition(struct panthor_aw *aw, u32 timeout_ms) +{ + return panthor_aw_wait_cond( + aw, + (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY || + atomic_read(&aw->state) == PANTHOR_AW_STATE_GPU_GRANTED), + timeout_ms); +} + +static void panthor_aw_msg_retry_work(struct work_struct *work) +{ + struct panthor_aw *aw = + container_of(work, struct panthor_aw, msg_retry_work); + struct panthor_am_msg *msg = &aw->msg; + int ret; + + ret = panthor_am_msg_retry(msg); + if (ret == -EINVAL) + drm_warn(&aw->ptdev->base, "Send FIFO unexpectedly empty"); + + if (ret == -EBUSY || ret == -EAGAIN) + queue_work(aw->wq, &aw->msg_retry_work); +} + +static void panthor_aw_send_msg(struct panthor_aw *aw, u64 message) +{ + struct panthor_device *ptdev = aw->ptdev; + int ret; + + ret = panthor_am_msg_send(&aw->msg, message); + if (ret == -ENOMEM) + drm_err(&ptdev->base, "Send FIFO is full"); + + if (ret == -EBUSY) { + drm_dbg(&ptdev->base, "Pending messages, scheduling retry work"); + queue_work(aw->wq, &aw->msg_retry_work); + } +} + +static void panthor_aw_handshake_handle(struct panthor_aw *aw, u64 message) +{ + struct panthor_device *ptdev = aw->ptdev; + bool acked = AM_MSG_ACK_GET(message); + u8 version = AM_MSG_VERSION_GET(message); + int ret; + + ret = panthor_am_msg_version_validate(&aw->msg, version); + if (ret == -EOPNOTSUPP) + drm_warn(&ptdev->base, + "Msg protocol version less than minimum supported (%u < %u)", + version, AM_MSG_MIN_SUPPORTED_VERSION); + + if (!acked) { + u64 reply = VM_ARB_INIT_MAKE(1, aw->msg.version); + + panthor_aw_send_msg(aw, reply); + + /* TODO: Handle AW restart */ + } + + panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_INIT, PANTHOR_AW_STATE_READY); +} + +static void panthor_aw_handshake_init(struct panthor_aw *aw) +{ + u64 message = VM_ARB_INIT_MAKE(0, AM_MSG_CURRENT_VERSION); + + panthor_aw_send_msg(aw, message); +} + +static void panthor_aw_handle_message(struct panthor_aw *aw) +{ + struct panthor_device *ptdev = aw->ptdev; + const u64 message = panthor_am_msg_read(&aw->msg); + const u8 msg_id = AM_MSG_ID_GET(message); + + /* currently only support ARB_VM_INIT */ + if (msg_id == ARB_VM_INIT) + panthor_aw_handshake_handle(aw, message); + else + drm_warn(&ptdev->base, "Unsupported msg id (0x%x)", msg_id); +} + + +static irqreturn_t panthor_aw_irq_raw_hander(int irq, void *data) +{ + struct panthor_irq *pirq = data; + struct panthor_device *ptdev = pirq->ptdev; + struct panthor_aw *aw = ptdev->aw; + u32 status; + + scoped_guard(spinlock_irqsave, &pirq->mask_lock) { + if (atomic_read(&pirq->state) != PANTHOR_IRQ_STATE_ACTIVE) + return IRQ_NONE; + } + + status = gpu_read(pirq->iomem, INT_STAT); + if (!status) + return IRQ_NONE; + + if (status & WINDOW_IRQ_MESSAGE) + panthor_aw_handle_message(aw); + + /* TODO: handle WINDOW_CLOSED*/ + + if ((status & WINDOW_IRQ_WINDOW_OPENED) && panthor_aw_is_open(aw)) + panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_REQUEST, + PANTHOR_AW_STATE_GPU_GRANTED); + + gpu_write(pirq->iomem, INT_CLEAR, status); + + return IRQ_HANDLED; +} + +static void panthor_aw_irq_suspend(struct panthor_irq *pirq) +{ + scoped_guard(spinlock_irqsave, &pirq->mask_lock) { + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDING); + gpu_write(pirq->iomem, INT_MASK, 0); + } + synchronize_irq(pirq->irq); + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); +} + +static void panthor_aw_irq_resume(struct panthor_irq *pirq) +{ + guard(spinlock_irqsave)(&pirq->mask_lock); + + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_ACTIVE); + gpu_write(pirq->iomem, INT_MASK, pirq->mask); +} + +static int panthor_request_aw_irq(struct panthor_device *ptdev, + struct panthor_irq *pirq, int irq, u32 mask, + void __iomem *iomem) +{ + int ret; + + pirq->ptdev = ptdev; + pirq->irq = irq; + pirq->mask = mask; + pirq->iomem = iomem; + spin_lock_init(&pirq->mask_lock); + + ret = devm_request_irq(ptdev->base.dev, irq, panthor_aw_irq_raw_hander, + IRQF_SHARED, "panthor-aw", pirq); + if (ret) + return ret; + + panthor_aw_irq_resume(pirq); + + return 0; +} + +static int panthor_aw_request(struct panthor_aw *aw) +{ + int ret; + +again: + switch(atomic_read(&aw->state)) { + case PANTHOR_AW_STATE_GPU_GRANTED: + return 0; + + case PANTHOR_AW_STATE_READY: + break; + + case PANTHOR_AW_STATE_INIT: + panthor_aw_handshake_init(aw); + ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY, + PANTHOR_AW_HANDSHAKE_TIMEOUT_MS); + if (ret) + return ret; + goto again; + default: + ret = panthor_aw_state_wait_transition( + aw, PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS); + if (ret) + return ret; + goto again; + } + + if (!panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_READY, + PANTHOR_AW_STATE_GPU_REQUEST)) + goto again; + + panthor_aw_send_msg(aw, VM_ARB_GPU_REQUEST); + + /* Wait for GPU to be granted */ + ret = panthor_aw_state_wait(aw, PANTHOR_AW_STATE_GPU_GRANTED, + PANTHOR_AW_GPU_REQUEST_TIMEOUT_MS); + if (ret) { + panthor_aw_state_set(aw, PANTHOR_AW_STATE_READY); + return ret; + } + + return 0; +} + +static int panthor_aw_yield(struct panthor_aw *aw) +{ + panthor_aw_send_msg(aw, VM_ARB_GPU_STOPPED); + + return panthor_aw_state_wait(aw, PANTHOR_AW_STATE_READY, + PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS); +} + +int panthor_aw_init(struct panthor_device *ptdev) +{ + struct panthor_aw *aw; + int irq; + int ret; + + if (!panthor_hw_has_gpu_discover(ptdev)) + return 0; + + aw = drmm_kzalloc(&ptdev->base, sizeof(*aw), GFP_KERNEL); + if (!aw) + return -ENOMEM; + + aw->wq = drmm_alloc_ordered_workqueue(&ptdev->base, "panthor-aw-wq", 0); + if (!aw->wq) + return -ENOMEM; + + aw->ptdev = ptdev; + aw->iomem = ptdev->iomem; + + INIT_WORK(&aw->msg_retry_work, panthor_aw_msg_retry_work); + + init_waitqueue_head(&aw->waitqueue); + atomic_set(&aw->state, PANTHOR_AW_STATE_INIT); + + panthor_am_msg_init(&aw->msg, aw->iomem + WINDOW_MESSAGE_BASE); + + ptdev->aw = aw; + + irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu"); + if (irq < 0) + return irq; + + ret = panthor_request_aw_irq(ptdev, &aw->irq, irq, WINDOW_IRQ_MASK, + aw->iomem + WINDOW_INT_BASE); + if (ret) + return ret; + + ret = panthor_aw_request(aw); + if (ret) + return ret; + + return 0; +} + +void panthor_aw_unplug(struct panthor_device *ptdev) +{ + struct panthor_aw *aw = ptdev->aw; + + if (!aw) + return; + + disable_work_sync(&aw->msg_retry_work); + + panthor_aw_irq_suspend(&aw->irq); +} + +int panthor_aw_resume(struct panthor_device *ptdev) +{ + struct panthor_aw *aw = ptdev->aw; + int ret; + + if (!aw) + return 0; + + panthor_aw_irq_resume(&aw->irq); + + ret = panthor_aw_request(aw); + if (ret) { + drm_warn(&ptdev->base, "Timedout waiting for GPU to be granted"); + goto err_out; + } + + return 0; + +err_out: + panthor_aw_irq_suspend(&aw->irq); + return ret; +} + +int panthor_aw_suspend(struct panthor_device *ptdev) +{ + struct panthor_aw *aw = ptdev->aw; + int ret = 0; + + /* suspend hw components directly if AW is not supported */ + if (!aw) + return 0; + + if (atomic_read(&aw->state) == PANTHOR_AW_STATE_READY) + goto out_irq_suspend; + + if (panthor_aw_state_try_set(aw, PANTHOR_AW_STATE_GPU_GRANTED, + PANTHOR_AW_STATE_GPU_STOPPED)) + ret = panthor_aw_yield(aw); + else + ret = panthor_aw_state_wait( + aw, PANTHOR_AW_STATE_READY, + PANTHOR_AW_STATE_TRANSITION_TIMEOUT_MS); + +out_irq_suspend: + panthor_aw_irq_suspend(&aw->irq); + return ret; +} diff --git a/drivers/gpu/drm/panthor/panthor_aw.h b/drivers/gpu/drm/panthor/panthor_aw.h new file mode 100644 index 000000000000..c2b89caa87c4 --- /dev/null +++ b/drivers/gpu/drm/panthor/panthor_aw.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ +/* Copyright 2026 ARM Limited. All rights reserved. */ + +#ifndef __PANTHOR_AW_H__ +#define __PANTHOR_AW_H__ + +#include <linux/types.h> + +struct panthor_device; + +/** + * enum aw_states - Enumeration of possible Access Window states + */ +enum aw_states { + /** @PANTHOR_AW_STATE_INIT: Initial state, prior to handshake. */ + PANTHOR_AW_STATE_INIT = 0, + + /** @PANTHOR_AW_STATE_READY: Handshake with Resource Group completed. */ + PANTHOR_AW_STATE_READY, + + /** @PANTHOR_AW_STATE_GPU_REQUEST: AW requested for GPU access. */ + PANTHOR_AW_STATE_GPU_REQUEST, + + /** @PANTHOR_AW_STATE_GPU_GRANTED: AW is granted GPU access. */ + PANTHOR_AW_STATE_GPU_GRANTED, + + /** @PANTHOR_AW_STATE_GPU_STOP: AW is requested to stop GPU access. */ + PANTHOR_AW_STATE_GPU_STOP, + + /** @PANTHOR_AW_STATE_GPU_STOPPED: AW has stopped GPU access. */ + PANTHOR_AW_STATE_GPU_STOPPED, +}; + +int panthor_aw_init(struct panthor_device *ptdev); + +void panthor_aw_unplug(struct panthor_device *ptdev); + +int panthor_aw_resume(struct panthor_device *ptdev); + +int panthor_aw_suspend(struct panthor_device *ptdev); + +#endif diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index bd417d6ae8c0..7248e2aa9da2 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -16,6 +16,7 @@ #include <drm/drm_managed.h> #include <drm/drm_print.h> +#include "panthor_aw.h" #include "panthor_devfreq.h" #include "panthor_device.h" #include "panthor_fw.h" @@ -100,6 +101,7 @@ void panthor_device_unplug(struct panthor_device *ptdev) panthor_gem_shrinker_unplug(ptdev); panthor_gpu_unplug(ptdev); panthor_pwr_unplug(ptdev); + panthor_aw_unplug(ptdev); pm_runtime_dont_use_autosuspend(ptdev->base.dev); pm_runtime_put_sync_suspend(ptdev->base.dev); @@ -255,10 +257,18 @@ int panthor_device_init(struct panthor_device *ptdev) if (ret) goto err_rpm_put; - ret = panthor_pwr_init(ptdev); + ret = panthor_aw_init(ptdev); if (ret) goto err_rpm_put; + ret = panthor_hw_info_init(ptdev); + if (ret) + goto err_unplug_aw; + + ret = panthor_pwr_init(ptdev); + if (ret) + goto err_unplug_aw; + ret = panthor_gpu_init(ptdev); if (ret) goto err_unplug_pwr; @@ -315,6 +325,9 @@ int panthor_device_init(struct panthor_device *ptdev) err_unplug_pwr: panthor_pwr_unplug(ptdev); +err_unplug_aw: + panthor_aw_unplug(ptdev); + err_rpm_put: pm_runtime_put_sync_suspend(ptdev->base.dev); return ret; diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 8b2a9bb426fc..a1092d02a1fe 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -18,6 +18,7 @@ #include <drm/gpu_scheduler.h> #include <drm/panthor_drm.h> +struct panthor_aw; struct panthor_csf; struct panthor_csf_ctx; struct panthor_device; @@ -196,6 +197,9 @@ struct panthor_device { /** @hw: GPU-specific data. */ struct panthor_hw *hw; + /** @aw: AW-specific data */ + struct panthor_aw *aw; + /** @pwr: Power control management data. */ struct panthor_pwr *pwr; diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c index 52271fb9db52..aa77d1b7f21c 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.c +++ b/drivers/gpu/drm/panthor/panthor_hw.c @@ -326,7 +326,7 @@ static int panthor_gpu_info_init(struct panthor_device *ptdev) return overload_shader_present(ptdev); } -static int panthor_hw_info_init(struct panthor_device *ptdev) +int panthor_hw_info_init(struct panthor_device *ptdev) { u64 l2_features = ptdev->gpu_info.l2_features; u32 major, minor, status; @@ -433,9 +433,5 @@ int panthor_hw_init(struct panthor_device *ptdev) if (ret) return ret; - ret = panthor_hw_bind_device(ptdev); - if (ret) - return ret; - - return panthor_hw_info_init(ptdev); + return panthor_hw_bind_device(ptdev); } diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h index 1b2678ea00db..d60860102b95 100644 --- a/drivers/gpu/drm/panthor/panthor_hw.h +++ b/drivers/gpu/drm/panthor/panthor_hw.h @@ -60,6 +60,8 @@ struct panthor_hw { }; int panthor_hw_init(struct panthor_device *ptdev); +int panthor_hw_info_init(struct panthor_device *ptdev); + int panthor_hw_power_status_register(void); void panthor_hw_power_status_unregister(void); -- 2.43.0
