mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Laura Nao <laura.nao@collabora.com>
To: "Danilo Krummrich" <dakr@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>,
	"FUJITA Tomonori" <fujita.tomonori@gmail.com>,
	"Frederic Weisbecker" <frederic@kernel.org>,
	"Lyude Paul" <lyude@redhat.com>,
	"Thomas Gleixner" <tglx@kernel.org>,
	"Anna-Maria Behnsen" <anna-maria@linutronix.de>,
	"John Stultz" <jstultz@google.com>,
	"Stephen Boyd" <sboyd@kernel.org>
Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	 driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,
	 kernel@collabora.com, Laura Nao <laura.nao@collabora.com>,
	 Deborah Brouwer <deborah.brouwer@collabora.com>
Subject: [PATCH 8/9] drm/tyr: program CSF global interface
Date: Tue, 15 Sep 2026 12:57:41 +0200	[thread overview]
Message-ID: <20260915-tyr-interfaces-v1-8-5d28f1f75aca@collabora.com> (raw)
In-Reply-To: <20260915-tyr-interfaces-v1-0-5d28f1f75aca@collabora.com>

Initialize the CSF global (GLB) interface after firmware boot.

Program the GLB input block with initial configuration:
- enable allocation across all present shader cores
- set power-off, progress, and idle timers

Then update GLB_REQ to enable persistent features and trigger
configuration updates, and ring the global doorbell to notify the MCU.

Co-developed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
Co-developed-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Signed-off-by: Laura Nao <laura.nao@collabora.com>
---
 drivers/gpu/drm/tyr/driver.rs        |   2 +-
 drivers/gpu/drm/tyr/fw.rs            |   6 +-
 drivers/gpu/drm/tyr/fw/interfaces.rs | 222 +++++++++++++++++++++++++++++++++--
 3 files changed, 219 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index bf1cb32e374d..2dcf33ec93ea 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -159,7 +159,7 @@ fn probe<'bound>(
         )?;
 
         firmware.boot()?;
-        firmware.enable_global_interface()?;
+        firmware.enable_global_interface(&gpu_info, &core_clk)?;
 
         let reg_data = pin_init!(TyrDrmRegistrationData {
                 pdev,
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index 5abd50238ca6..1499ffdef51f 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -14,6 +14,7 @@
 //! [`Section`]: crate::fw::Section
 
 use kernel::{
+    clk::Clk,
     device::{
         Bound,
         Device, //
@@ -370,7 +371,7 @@ fn stop(&self) -> Result {
     }
 
     /// Enable the global interface.
-    pub(crate) fn enable_global_interface(&self) -> Result {
+    pub(crate) fn enable_global_interface(&self, gpu_info: &GpuInfo, core_clk: &Clk) -> Result {
         let shared_section = self.shared_section()?;
         let version = interfaces::probe_version(&shared_section.mem)?;
 
@@ -379,7 +380,7 @@ pub(crate) fn enable_global_interface(&self) -> Result {
         match version.major().get() {
             1..=4 => match &mut *self.global_iface.lock() {
                 FwIfaces::V1(iface) => {
-                    iface.enable(shared_section)
+                    iface.enable(&self.iomem, shared_section, gpu_info, core_clk)
                 }
             },
             0 => {
@@ -391,6 +392,5 @@ pub(crate) fn enable_global_interface(&self) -> Result {
                 Err(ENODEV)
             }
         }
-
     }
 }
diff --git a/drivers/gpu/drm/tyr/fw/interfaces.rs b/drivers/gpu/drm/tyr/fw/interfaces.rs
index 1cdfef2340c9..673ebeafb68e 100644
--- a/drivers/gpu/drm/tyr/fw/interfaces.rs
+++ b/drivers/gpu/drm/tyr/fw/interfaces.rs
@@ -96,11 +96,15 @@
 //! version can be read before the layout is known.
 //!
 
-use crate::fw::Section;
-
 mod v1;
 
 mod layout;
+use crate::{
+    driver::IoMem,
+    fw::Section,
+    gpu::GpuInfo,
+    regs::doorbell_block::DOORBELL, //
+};
 
 use iface::{
     FwInterface,
@@ -108,8 +112,17 @@
     IfaceBlock, //
 };
 use kernel::{
-    io::io_read,
-    prelude::*,  //
+    bindings::SZ_1K,
+    clk::Clk,
+    num::Bounded,
+    io:: {
+        io_read,
+        io_write,
+        register::Array,
+        Io, //
+    },
+    prelude::*,
+    time::arch_timer_get_rate, //
 };
 
 /// Offset from GLB_CONTROL_BLOCK start to the first GROUP_CONTROL block.
@@ -280,7 +293,6 @@ pub(super) fn new(view: MappedBoViewMut<'drm>) -> Result<Self> {
         }
 
         /// Returns the write token for this block.
-        #[expect(dead_code)]
         pub(super) fn io(&mut self) -> IoMutToken<'_, 'drm, B> {
             IoMutToken(self)
         }
@@ -761,6 +773,7 @@ fn from(exc_type: CsFatalExceptionType) -> Self {
     }
 }
 
+use glb::*;
 use v1::*;
 
 /// The per-version type profile of the CSF interface.
@@ -860,6 +873,72 @@ pub(super) fn new() -> Result<Self> {
     }
 }
 
+/// Converts a timeout in microseconds to a timeout field value and timer source.
+///
+/// The firmware supports two timer sources:
+/// - System timestamp (arch timer): preferred when available, so the timeout
+///   tracks real elapsed time independently of GPU clock rate.
+/// - GPU cycle counter: fallback when the system timestamp is unavailable.
+///
+/// Returns the encoded timeout value and the selected timer source.
+fn conv_timeout(core_clk: &Clk, timeout_us: u32) -> Result<(u32, TimestampSource)> {
+    // The max timeout is determined by the 31 bit size of the timeout field.
+    let max_timeout = (1u32 << 31) - 1;
+    let core_rate = core_clk.rate().as_hz() as u64;
+
+    let (timer_rate, timer_source) = match arch_timer_get_rate() {
+        Some(rate) => (u64::from(rate), TimestampSource::SystemTimestamp),
+        _ if core_rate != 0 => (core_rate, TimestampSource::GpuCounter),
+        _ => return Err(EINVAL),
+    };
+
+    let timeout_in_cycles = u64::from(timeout_us) * timer_rate;
+
+    // The hardware stores the represented timeout value with a shr(10) to save space.
+    let timeout_shift = u64::from(SZ_1K);
+    let us_per_second = 1_000_000u64;
+
+    let timeout_val = timeout_in_cycles.div_ceil(us_per_second * timeout_shift);
+    let timeout_val = timeout_val.min(u64::from(max_timeout)) as u32;
+
+    Ok((timeout_val, timer_source))
+}
+
+/// Request/acknowledge communication between Tyr and CSF.
+struct GlobalInterfaceRequests<'a, 'drm> {
+    /// Global input block where driver writes requests.
+    input: &'a mut FwInterfaceMut<'drm, GlbInputV1>,
+    /// Global output block where firmware writes acknowledgements.
+    output: &'a FwInterface<'drm, GlbOutputV1>,
+}
+
+impl<'a, 'drm> GlobalInterfaceRequests<'a, 'drm> {
+    fn new(
+        input: &'a mut FwInterfaceMut<'drm, GlbInputV1>,
+        output: &'a FwInterface<'drm, GlbOutputV1>,
+    ) -> Self {
+        Self { input, output }
+    }
+
+    /// Use to make requests, where simply changing the bit value is
+    /// sufficient to make a request; the bit value has no meaning in itself.
+    fn toggle_requests(&mut self, reqs_mask: GLB_REQ) -> Result {
+        let reqs_mask_val = reqs_mask.into_raw();
+
+        let cur_ack_val = io_read!(self.output, .ack).into_raw();
+
+        // Calculate which bits to toggle based on ACK state
+        let toggled_bits = (cur_ack_val ^ reqs_mask_val) & reqs_mask_val;
+
+        let cur_req_val = io_read!(&*self.input, .req).into_raw();
+        let preserved_bits = cur_req_val & !reqs_mask_val;
+        let new_val = toggled_bits | preserved_bits;
+
+        io_write!(self.input.io(), .req, GLB_REQ::from_raw(new_val));
+        Ok(())
+    }
+}
+
 /// State of the global interface.
 enum GlobalInterfaceState<'drm> {
     /// Interface is not yet initialized.
@@ -963,7 +1042,13 @@ pub(super) fn new() -> Result<Self> {
     /// This reads the firmware's control block to set up the global input/output
     /// interfaces; it configures timers and shader core allocation; and it discovers
     /// available CSG interfaces.
-    pub(crate) fn enable(&mut self, shared_section: &Section<'drm>) -> Result {
+    pub(crate) fn enable(
+            &mut self,
+            io: &IoMem<'_>,
+            shared_section: &Section<'drm>,
+            gpu_info: &GpuInfo,
+            core_clk: &Clk,
+        ) -> Result {
         // Drop any previous state first.
         // This lets enable() run again after an MCU reset.
         self.state = GlobalInterfaceState::Disabled;
@@ -986,7 +1071,7 @@ pub(crate) fn enable(&mut self, shared_section: &Section<'drm>) -> Result {
         );
 
         let input_va = io_read!(&glb_control, .input_va).value().get();
-        let glb_input = FwInterfaceMut::<GlbInputV1>::new(mem.try_view_mut(
+        let mut glb_input = FwInterfaceMut::<GlbInputV1>::new(mem.try_view_mut(
             input_va.into(),
             GlbInputV1::ARCH_SIZE as u64,
             core::mem::size_of::<GlbInputV1>() as u64,
@@ -999,6 +1084,14 @@ pub(crate) fn enable(&mut self, shared_section: &Section<'drm>) -> Result {
             core::mem::size_of::<GlbOutputV1>() as u64,
         )?)?;
 
+        Self::configure_glb_input(&mut glb_input, gpu_info, core_clk)?;
+        Self::configure_glb_requests(&mut glb_input, &glb_output)?;
+
+        io.write(Array::at(0), DOORBELL::zeroed().with_ring(true));
+
+        // Wait for the firmware to acknowledge the initial global configuration.
+        GlobalInterfaceRequests::new(&mut glb_input, &glb_output);
+
         // Read how many CSG interfaces exist.
         let csg_num = io_read!(&glb_control, .group_num).value().get();
 
@@ -1041,6 +1134,121 @@ pub(crate) fn enable(&mut self, shared_section: &Section<'drm>) -> Result {
         Ok(())
     }
 
+    /// Programs GLB input-block configuration registers.
+    ///
+    /// Writes shader core allocation and timer values. These settings are applied
+    /// by firmware only after the corresponding GLB_REQ bits are updated.
+    fn configure_glb_input(
+        glb_input: &mut FwInterfaceMut<'drm, GlbInputV1>,
+        gpu_info: &GpuInfo,
+        core_clk: &Clk,
+    ) -> Result {
+        // Make all present shader cores available for endpoint allocation.
+        io_write!(
+            glb_input.io(),
+            .alloc_en,
+            GLB_ALLOC_EN::zeroed().with_mask(gpu_info.shader_present)
+        );
+
+        // Configure power-down delay for shader and tiler domains.
+        // The firmware powers down a domain after it has been idle for this duration,
+        // and cancels the timeout if work arrives before expiry.
+
+        // Power-down delay after idle, in microseconds.
+        const PWROFF_HYSTERESIS_US: u32 = 10_000;
+        let (pwroff_timeout, pwroff_source) = conv_timeout(core_clk, PWROFF_HYSTERESIS_US)?;
+        let pwroff_timeout = Bounded::<u32, 31>::try_new(pwroff_timeout).ok_or(EINVAL)?;
+        io_write!(
+            glb_input.io(),
+            .pwroff_timer,
+            GLB_PWROFF_TIMER::zeroed()
+                .with_timeout(pwroff_timeout)
+                .with_timer_source(pwroff_source)
+        );
+
+        // Configure forward progress timeout.
+        //
+        // Keep this aligned with panthor, which programs a fixed GPU-cycle timeout.
+        // The real-time duration therefore varies with the GPU clock rate (e.g. ~5.24 s
+        // at 500 MHz, longer at lower frequencies).
+        //
+        // The hardware stores the timeout in units of 1024 cycles, so encode the raw
+        // cycle count by shifting right by 10.
+        const PROGRESS_TIMEOUT_CYCLES: u32 = 5 * 500 * 1024 * 1024;
+        const PROGRESS_TIMEOUT_SCALE_SHIFT: u32 = 10;
+        let progress_timeout = PROGRESS_TIMEOUT_CYCLES >> PROGRESS_TIMEOUT_SCALE_SHIFT;
+        io_write!(
+            glb_input.io(),
+            .progress_timer,
+            GLB_PROGRESS_TIMER::zeroed().with_timeout(progress_timeout)
+        );
+
+        // Configure the delay before reporting the GPU as idle.
+        const IDLE_HYSTERESIS_US: u32 = 800;
+        let (idle_timeout, idle_source) = conv_timeout(core_clk, IDLE_HYSTERESIS_US)?;
+        let idle_timeout = Bounded::<u32, 31>::try_new(idle_timeout).ok_or(EINVAL)?;
+        io_write!(
+            glb_input.io(),
+            .idle_timer,
+            GLB_IDLE_TIMER::zeroed()
+                .with_timeout(idle_timeout)
+                .with_timer_source(idle_source)
+        );
+
+        Ok(())
+    }
+
+    /// Programs GLB_REQ and ACK IRQ mask after GLB input registers are configured.
+    ///
+    /// This sets desired persistent states, toggles configuration-update requests,
+    /// and returns the GLB_REQ bits that must be acknowledged by firmware.
+    fn configure_glb_requests(
+        glb_input: &mut FwInterfaceMut<'drm, GlbInputV1>,
+        glb_output: &FwInterface<'drm, GlbOutputV1>,
+    ) -> Result<GLB_REQ> {
+        // Firmware updates GLB_ACK (output block) in response to GLB_REQ.
+        // GLB_ACK_IRQ_MASK selects which of these updates trigger a host interrupt.
+        io_write!(
+            glb_input.io(),
+            .ack_irq_mask,
+            GLB_ACK_IRQ_MASK::zeroed()
+                .with_cfg_progress_timer(true)
+                .with_cfg_alloc_en(true)
+                .with_cfg_pwroff_timer(true)
+                .with_idle_enable(true)
+                .with_idle_event(true)
+                .with_counter_enable(true)
+        );
+
+        // Requests whose value represents the desired persistent state.
+        let cur_req = io_read!(&*glb_input, .req);
+        io_write!(
+            glb_input.io(),
+            .req,
+            cur_req.with_idle_enable(true).with_counter_enable(true)
+        );
+
+        let mut request_field = GlobalInterfaceRequests::new(glb_input, glb_output);
+
+        // Fields that require toggle semantics.
+        let toggle_mask = GLB_REQ::zeroed()
+            .with_cfg_progress_timer(true)
+            .with_cfg_alloc_en(true)
+            .with_cfg_pwroff_timer(true);
+
+        request_field.toggle_requests(toggle_mask)?;
+
+        // All fields we want to wait for completion on (REQ == ACK).
+        let ack_mask = GLB_REQ::zeroed()
+            .with_cfg_progress_timer(true)
+            .with_cfg_alloc_en(true)
+            .with_cfg_pwroff_timer(true)
+            .with_idle_enable(true)
+            .with_counter_enable(true);
+
+        Ok(ack_mask)
+    }
+
     /// Initialize CSG interfaces.
     ///
     /// This uses the previously read CSG count to create and enable each CSG interface.

-- 
2.39.5


  parent reply	other threads:[~2026-09-15 10:58 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-15 10:57 [PATCH 0/9] drm/tyr: add CSF firmware interface support Laura Nao
2026-09-15 10:57 ` [PATCH 1/9] drm/tyr: validate presence of CSF shared section Laura Nao
2026-09-15 10:57 ` [PATCH 2/9] rust: io: drop the CONFIG_64BIT restriction on system memory u64 access Laura Nao
2026-09-15 11:18   ` Gary Guo
2026-09-15 10:57 ` [PATCH 3/9] drm/tyr: add MappedBo, a kernel BO with an always-valid CPU mapping Laura Nao
2026-09-15 10:57 ` [PATCH 4/9] drm/tyr: add McuVa and claim-checked MappedBo views Laura Nao
2026-09-15 10:57 ` [PATCH 5/9] drm/tyr: drop unused KernelBo::bo() function Laura Nao
2026-09-15 10:57 ` [PATCH 6/9] drm/tyr: add CSF firmware interface support Laura Nao
2026-09-15 10:57 ` [PATCH 7/9] rust: time: add arch_timer_get_rate wrapper Laura Nao
2026-09-15 10:57 ` Laura Nao [this message]
2026-09-15 10:57 ` [PATCH 9/9] drm/tyr: wait for global interface readiness Laura Nao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260915-tyr-interfaces-v1-8-5d28f1f75aca@collabora.com \
    --to=laura.nao@collabora.com \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=anna-maria@linutronix.de \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=deborah.brouwer@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=driver-core@lists.linux.dev \
    --cc=frederic@kernel.org \
    --cc=fujita.tomonori@gmail.com \
    --cc=gary@garyguo.net \
    --cc=jstultz@google.com \
    --cc=kernel@collabora.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sboyd@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tamird@kernel.org \
    --cc=tglx@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®