[PATCH v4 15/20] drm/tyr: add Job IRQ handling
From: Deborah Brouwer
Date: Fri Apr 24 2026 - 19:41:27 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>
Signed-off-by: Deborah Brouwer <deborah.brouwer@xxxxxxxxxxxxx>
---
drivers/gpu/drm/tyr/driver.rs | 70 ++++++++++++++++++++++++
drivers/gpu/drm/tyr/fw.rs | 3 ++
drivers/gpu/drm/tyr/fw/irq.rs | 121 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 246bc3cb8580..da007aded92d 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,
@@ -25,6 +27,13 @@
poll,
Io, //
},
+ irq::{
+ Flags,
+ IrqReturn,
+ ThreadedHandler,
+ ThreadedIrqReturn,
+ ThreadedRegistration, //
+ },
new_mutex,
of,
platform,
@@ -239,3 +248,64 @@ struct Regulators {
_mali: Regulator<regulator::Enabled>,
_sram: Regulator<regulator::Enabled>,
}
+
+pub(crate) trait TyrIrqTrait: Sync {
+ fn read_status(&self, dev: &Device<Bound>) -> u32;
+ fn clear_mask(&self, dev: &Device<Bound>);
+ fn reenable_mask(&self, dev: &Device<Bound>);
+ fn read_raw_status(&self, dev: &Device<Bound>) -> u32;
+ fn clear_status(&self, dev: &Device<Bound>, 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 + 'static> TyrIrq<T> {
+ pub(crate) fn request<'a>(
+ pdev: &'a platform::Device<Bound>,
+ name: &'static CStr,
+ irq: T,
+ ) -> Result<impl PinInit<ThreadedRegistration<Self>, Error> + 'a> {
+ let handler = try_pin_init!(Self {
+ irq,
+ _pin: PhantomPinned,
+ });
+
+ Ok(pdev.request_threaded_irq_by_name(Flags::SHARED, name, name, handler))
+ }
+}
+
+impl<T: TyrIrqTrait> ThreadedHandler for TyrIrq<T> {
+ fn handle(&self, dev: &Device<Bound>) -> ThreadedIrqReturn {
+ let masked_status = self.irq.read_status(dev);
+
+ if masked_status == 0 {
+ return ThreadedIrqReturn::None;
+ }
+ self.irq.clear_mask(dev);
+ ThreadedIrqReturn::WakeThread
+ }
+
+ fn handle_threaded(&self, dev: &Device<Bound>) -> IrqReturn {
+ let mut ret = IrqReturn::None;
+
+ loop {
+ let raw_status = self.irq.read_raw_status(dev) & self.irq.mask();
+ if raw_status == 0 {
+ break;
+ }
+ self.irq.handle(raw_status);
+ self.irq.clear_status(dev, raw_status);
+ ret = IrqReturn::Handled;
+ }
+
+ self.irq.reenable_mask(dev);
+ ret
+ }
+}
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index cb2546350f0a..b5ccacf891a3 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -62,8 +62,11 @@
vm::Vm, //
};
+pub(crate) mod irq;
mod parser;
+const MAX_CSG: u32 = 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..0eff5a14f69e
--- /dev/null
+++ b/drivers/gpu/drm/tyr/fw/irq.rs
@@ -0,0 +1,121 @@
+// 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,
+ Device, //
+ },
+ devres::Devres,
+ 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 {
+ iomem: Arc<Devres<IoMem>>,
+ fw_ready: Arc<AtomicBool>,
+ ready_wait: Arc<Wait>,
+}
+
+pub(crate) fn job_irq_init<'a>(
+ pdev: &'a platform::Device<Bound>,
+ iomem: Arc<Devres<IoMem>>,
+ fw_ready: Arc<AtomicBool>,
+ ready_wait: Arc<Wait>,
+) -> Result<impl PinInit<ThreadedRegistration<TyrIrq<JobIrq>>, Error> + 'a> {
+ let io = iomem.access(pdev.as_ref())?;
+ io.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, dev: &Device<Bound>) -> u32 {
+ match self.iomem.access(dev) {
+ Ok(io) => io.read(JOB_IRQ_STATUS).into_raw(),
+ Err(_) => 0,
+ }
+ }
+
+ fn clear_mask(&self, dev: &Device<Bound>) {
+ if let Ok(io) = self.iomem.access(dev) {
+ io.write_reg(JOB_IRQ_MASK::zeroed());
+ }
+ }
+
+ fn reenable_mask(&self, dev: &Device<Bound>) {
+ if let Ok(io) = self.iomem.access(dev) {
+ io.write_reg(
+ JOB_IRQ_MASK::zeroed()
+ .with_const_csg::<CSG_IRQ_MASK>()
+ .with_glb(true),
+ );
+ }
+ }
+
+ fn read_raw_status(&self, dev: &Device<Bound>) -> u32 {
+ match self.iomem.access(dev) {
+ Ok(io) => io.read(JOB_IRQ_RAWSTAT).into_raw(),
+ Err(_) => 0,
+ }
+ }
+
+ fn clear_status(&self, dev: &Device<Bound>, status: u32) {
+ if let Ok(io) = self.iomem.access(dev) {
+ io.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.53.0