[PATCH v2 20/28] drm/panthor: Handle partition control INVALID_COMMAND interrupt
From: Karunika Choo
Date: Tue Nov 10 2026 - 11:24:29 EST
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@xxxxxxx>
---
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