[PATCH v2 27/28] drm/panthor: Prevent missed post-yield cleanup due to reset
From: Karunika Choo
Date: Tue Nov 10 2026 - 10:55:28 EST
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@xxxxxxx>
---
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