[PATCH v2 3/3] media: qcom: iris: Fix IRQ disable depth imbalance on watchdog interrupt

From: Vishnu Reddy

Date: Wed Nov 11 2026 - 04:13:40 EST


iris_hfi_isr() disables the IRQ for every interrupt and the threaded
handler enables it again, except for a watchdog interrupt, which is left
masked until the firmware is reset. iris_vpu_power_off() decides whether
to add its own disable from core->intr_status, but that is only updated
by the threaded handler. If a watchdog interrupt is still pending when
power-off checks it, power-off adds a disable and the handler then skips
its enable, leaving the depth one higher than the single enable_irq() in
iris_vpu_power_on() can undo. The IRQ then stays disabled after the
reset and the firmware can no longer be reached.

Make the depth independent of when intr_status is read. Power-off now
always takes one disable, paired with the enable in power-on. The
threaded handler evaluates the watchdog status once under core->lock and
sets core->wdt_irq_enable_pending when it skips its enable. Power-on
drops that extra disable together with its own.

Fixes: bb8a95aa038e ("media: iris: implement power management")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Vishnu Reddy <busanna.reddy@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/iris/iris_core.h | 2 ++
drivers/media/platform/qcom/iris/iris_hfi_common.c | 6 +++++-
drivers/media/platform/qcom/iris/iris_vpu_common.c | 7 +++++--
3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
index 0b1b9721e6f9..1909b91deab7 100644
--- a/drivers/media/platform/qcom/iris/iris_core.h
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -73,6 +73,7 @@ struct qcom_ubwc_cfg_data;
* @lock: a lock for this strucure
* @irq_disabling: flag to indicate an IRQ-disable is in-progress
* @irq_disable_waitqueue: waitqueue to wait for the IRQ-disable to complete
+ * @wdt_irq_enable_pending: enable_irq skipped on wdt in isr, done on power-on
* @response_packet: a pointer to response packet from fw to driver
* @header_id: id of packet header
* @packet_id: id of packet
@@ -121,6 +122,7 @@ struct iris_core {
struct mutex lock; /* lock for core related operations */
bool irq_disabling;
wait_queue_head_t irq_disable_waitqueue;
+ bool wdt_irq_enable_pending;
u8 *response_packet;
u32 header_id;
u32 packet_id;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c
index dad906d03fdc..7a2969579697 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c
@@ -100,6 +100,7 @@ irqreturn_t iris_hfi_isr(int irq, void *data)
irqreturn_t iris_hfi_isr_handler(int irq, void *data)
{
struct iris_core *core = data;
+ int wdt_irq;

if (!core)
return IRQ_NONE;
@@ -107,12 +108,15 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data)
mutex_lock(&core->lock);
pm_runtime_mark_last_busy(core->dev);
iris_vpu_clear_interrupt(core);
+ wdt_irq = iris_vpu_watchdog(core, core->intr_status);
+ if (wdt_irq)
+ core->wdt_irq_enable_pending = true;
mutex_unlock(&core->lock);

if (core->hfi_sys_ops)
core->hfi_sys_ops->sys_hfi_response_handler(core);

- if (!iris_vpu_watchdog(core, core->intr_status))
+ if (!wdt_irq)
enable_irq(irq);

return IRQ_HANDLED;
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index fcf060a72c4c..0ce5094c0bb9 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -244,8 +244,7 @@ static void iris_vpu_disable_irq(struct iris_core *core)

void iris_vpu_power_off(struct iris_core *core)
{
- if (!iris_vpu_watchdog(core, core->intr_status))
- iris_vpu_disable_irq(core);
+ iris_vpu_disable_irq(core);

iris_opp_set_rate(core->dev, 0);
core->iris_platform_data->vpu_ops->power_off_hw(core);
@@ -531,6 +530,10 @@ int iris_vpu_power_on(struct iris_core *core)

core->iris_platform_data->vpu_ops->interrupt_init(core);
core->intr_status = 0;
+ if (core->wdt_irq_enable_pending) {
+ core->wdt_irq_enable_pending = false;
+ enable_irq(core->irq);
+ }
enable_irq(core->irq);

return 0;

--
2.34.1