[PATCH 2/2] drm/tyr: add Job IRQ handling

From: Laura Nao

Date: Tue Jul 21 2026 - 11:16:51 EST


Add a threaded IRQ wrapper for Tyr interrupt sources and use it to handle
the firmware Job IRQ.

The Job IRQ reports requests from the CSF firmware, including global
interface requests and CSG attention bits. Add a Job IRQ handler that
masks the interrupt in the primary IRQ handler, processes pending raw
status in the threaded handler, clears handled bits, and reenables the
mask before returning.

Co-developed-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Co-developed-by: Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx>
Signed-off-by: Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx>
Signed-off-by: Laura Nao <laura.nao@xxxxxxxxxxxxx>
---
drivers/gpu/drm/tyr/driver.rs | 75 ++++++++++++++++++++++++
drivers/gpu/drm/tyr/fw.rs | 4 ++
drivers/gpu/drm/tyr/fw/irq.rs | 104 ++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+)
create mode 100644 drivers/gpu/drm/tyr/fw/irq.rs

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 46ba91af9aca..6ee6f5e578b7 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 or MIT

+use core::marker::PhantomPinned;
+
use kernel::{
clk::{
Clk,
@@ -21,6 +23,13 @@
poll,
Io, //
},
+ irq::{
+ Flags,
+ IrqReturn,
+ ThreadedHandler,
+ ThreadedIrqReturn,
+ ThreadedRegistration, //
+ },
new_mutex,
of,
platform,
@@ -238,3 +247,69 @@ struct Regulators {
_mali: Regulator<regulator::Enabled>,
_sram: Regulator<regulator::Enabled>,
}
+
+pub(crate) trait TyrIrqTrait: Sync {
+ fn read_status(&self) -> u32;
+ fn clear_mask(&self);
+ fn reenable_mask(&self);
+ fn read_raw_status(&self) -> u32;
+ fn clear_status(&self, status: u32);
+ fn mask(&self) -> u32;
+ fn handle(&self, status: u32);
+}
+
+#[pin_data]
+pub(crate) struct TyrIrq<T: TyrIrqTrait> {
+ irq: T,
+ #[pin]
+ _pin: PhantomPinned,
+}
+
+impl<T: TyrIrqTrait> TyrIrq<T> {
+ pub(crate) fn request<'a>(
+ pdev: &'a platform::Device<Bound>,
+ name: &'static CStr,
+ irq: T,
+ ) -> Result<impl PinInit<ThreadedRegistration<'a, Self>, Error> + 'a>
+ where
+ T: 'a,
+ {
+ let handler = try_pin_init!(Self {
+ irq,
+ _pin: PhantomPinned,
+ });
+
+ // SAFETY: The resulting `PinInit` is not leaked, it is consumed by the caller to
+ // initialize a pinned `ThreadedRegistration`.
+ Ok(unsafe { pdev.request_threaded_irq_by_name(Flags::SHARED, name, name, handler) })
+ }
+}
+
+impl<T: TyrIrqTrait> ThreadedHandler for TyrIrq<T> {
+ fn handle(&self) -> ThreadedIrqReturn {
+ let masked_status = self.irq.read_status();
+
+ if masked_status == 0 {
+ return ThreadedIrqReturn::None;
+ }
+ self.irq.clear_mask();
+ ThreadedIrqReturn::WakeThread
+ }
+
+ fn handle_threaded(&self) -> IrqReturn {
+ let mut ret = IrqReturn::None;
+
+ loop {
+ let raw_status = self.irq.read_raw_status() & self.irq.mask();
+ if raw_status == 0 {
+ break;
+ }
+ self.irq.handle(raw_status);
+ self.irq.clear_status(raw_status);
+ ret = IrqReturn::Handled;
+ }
+
+ self.irq.reenable_mask();
+ ret
+ }
+}
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index 554808a792aa..4188dc4232f5 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -66,8 +66,12 @@
vm::Vm, //
};

+pub(crate) mod irq;
mod parser;

+/// Maximum number of CSG interfaces supported by hardware.
+const MAX_CSG: usize = 16;
+
impl_flags!(
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
pub(super) struct SectionFlags(u32);
diff --git a/drivers/gpu/drm/tyr/fw/irq.rs b/drivers/gpu/drm/tyr/fw/irq.rs
new file mode 100644
index 000000000000..4084904d53f6
--- /dev/null
+++ b/drivers/gpu/drm/tyr/fw/irq.rs
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+//! IRQ handling for the Job IRQ.
+//!
+//! The Job IRQ signals events from the MCU, including global interface acknowledgements.
+#![allow(dead_code)]
+
+use core::sync::atomic::{
+ AtomicBool,
+ Ordering, //
+};
+
+use kernel::{
+ c_str,
+ device::Bound, //
+ io::Io,
+ irq::ThreadedRegistration,
+ platform,
+ prelude::*,
+ sync::Arc, //
+};
+
+use crate::{
+ driver::{
+ IoMem,
+ TyrIrq,
+ TyrIrqTrait, //
+ },
+ regs::job_control::{
+ JOB_IRQ_CLEAR,
+ JOB_IRQ_MASK,
+ JOB_IRQ_RAWSTAT,
+ JOB_IRQ_STATUS, //
+ },
+ wait::Wait, //
+};
+
+const CSG_IRQ_MASK: u32 = (1u32 << super::MAX_CSG) - 1;
+
+pub(crate) struct JobIrq<'bound> {
+ iomem: Arc<IoMem<'bound>>,
+ fw_ready: Arc<AtomicBool>,
+ ready_wait: Arc<Wait>,
+}
+
+pub(crate) fn job_irq_init<'bound>(
+ pdev: &'bound platform::Device<Bound>,
+ iomem: Arc<IoMem<'bound>>,
+ fw_ready: Arc<AtomicBool>,
+ ready_wait: Arc<Wait>,
+) -> Result<impl PinInit<ThreadedRegistration<'bound, TyrIrq<JobIrq<'bound>>>, Error> + 'bound> {
+ iomem.write_reg(
+ JOB_IRQ_MASK::zeroed()
+ .with_const_csg::<CSG_IRQ_MASK>()
+ .with_glb(true),
+ );
+ let job_irq = JobIrq {
+ iomem: iomem.clone(),
+ fw_ready,
+ ready_wait,
+ };
+
+ TyrIrq::request(pdev, c_str!("job"), job_irq)
+}
+
+impl TyrIrqTrait for JobIrq<'_> {
+ fn read_status(&self) -> u32 {
+ self.iomem.read(JOB_IRQ_STATUS).into_raw()
+ }
+
+ fn clear_mask(&self) {
+ self.iomem.write_reg(JOB_IRQ_MASK::zeroed());
+ }
+
+ fn reenable_mask(&self) {
+ self.iomem.write_reg(
+ JOB_IRQ_MASK::zeroed()
+ .with_const_csg::<CSG_IRQ_MASK>()
+ .with_glb(true),
+ );
+ }
+
+ fn read_raw_status(&self) -> u32 {
+ self.iomem.read(JOB_IRQ_RAWSTAT).into_raw()
+ }
+
+ fn clear_status(&self, status: u32) {
+ self.iomem.write_reg(JOB_IRQ_CLEAR::from_raw(status));
+ }
+
+ fn mask(&self) -> u32 {
+ JOB_IRQ_MASK::zeroed()
+ .with_const_csg::<CSG_IRQ_MASK>()
+ .with_glb(true)
+ .into_raw()
+ }
+
+ fn handle(&self, status: u32) {
+ if JOB_IRQ_RAWSTAT::from_raw(status).glb() {
+ self.fw_ready.store(true, Ordering::Release);
+ self.ready_wait.notify_all();
+ }
+ }
+}
--
2.39.5