[PATCH v2 26/28] drm/panthor: Tolerate access-window loss during HW waits
From: Karunika Choo
Date: Tue Nov 10 2026 - 14:06:18 EST
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@xxxxxxx>
---
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