diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs index 7e532889a1f4..cd2613a74595 100644 --- a/drivers/gpu/nova-core/falcon/hal.rs +++ b/drivers/gpu/nova-core/falcon/hal.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. use kernel::prelude::*; @@ -12,6 +13,7 @@ Architecture, Chipset, // }, + regs, }; mod ga102; @@ -70,6 +72,22 @@ fn signature_reg_fuse_version( /// these. For anything above, the PIO registers appear to be masked to the CPU, so DMA is the /// only usable method. fn load_method(&self) -> LoadMethod; + + /// Returns the causes in `latched` that are routed to the host, meaning the CPU, rather than + /// to the falcon's own RISC-V core. + /// + /// The causes routed to the core belong to the firmware running on it, and the host does not + /// service them. + #[expect(dead_code)] + fn host_routed_causes( + &self, + falcon: &Falcon<'_, E>, + latched: regs::NV_PFALCON_FALCON_IRQSTAT, + ) -> regs::NV_PFALCON_FALCON_IRQSTAT; + + /// Retriggers the falcon, which then re-emits its host-routed causes into the interrupt tree. + #[expect(dead_code)] + fn retrigger(&self, falcon: &Falcon<'_, E>); } /// Returns a boxed falcon HAL adequate for `chipset`. @@ -82,11 +100,11 @@ pub(super) fn falcon_hal( ) -> Result>> { let hal = match chipset.arch() { Architecture::Turing => { - KBox::new(tu102::Tu102::::new(), GFP_KERNEL)? as KBox> + KBox::new(tu102::Tu102::::new(false), GFP_KERNEL)? as KBox> } - // GA100 boots like Turing so use Turing HAL + // GA100 boots like Turing, but features the retrigger register. Architecture::Ampere if chipset == Chipset::GA100 => { - KBox::new(tu102::Tu102::::new(), GFP_KERNEL)? as KBox> + KBox::new(tu102::Tu102::::new(true), GFP_KERNEL)? as KBox> } Architecture::Ampere | Architecture::Ada diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs index f9a8444cf840..8e77c0bbaf61 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. use core::marker::PhantomData; @@ -169,4 +170,20 @@ fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result { fn load_method(&self) -> LoadMethod { LoadMethod::Dma } + + fn host_routed_causes( + &self, + falcon: &Falcon<'_, E>, + latched: regs::NV_PFALCON_FALCON_IRQSTAT, + ) -> regs::NV_PFALCON_FALCON_IRQSTAT { + let pfalcon2 = falcon.pfalcon2; + let mask = pfalcon2.read(regs::ga102::NV_PRISCV_RISCV_IRQMASK).value(); + let dest = pfalcon2.read(regs::ga102::NV_PRISCV_RISCV_IRQDEST).value(); + + regs::NV_PFALCON_FALCON_IRQSTAT::from(latched.into_raw() & mask & dest) + } + + fn retrigger(&self, falcon: &Falcon<'_, E>) { + super::tu102::retrigger_ga100(falcon); + } } diff --git a/drivers/gpu/nova-core/falcon/hal/tu102.rs b/drivers/gpu/nova-core/falcon/hal/tu102.rs index 7fc6e83c2566..94c4438d3ebf 100644 --- a/drivers/gpu/nova-core/falcon/hal/tu102.rs +++ b/drivers/gpu/nova-core/falcon/hal/tu102.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. use core::marker::PhantomData; use kernel::{ io::{ poll::read_poll_timeout, + register::Array, Io, // }, prelude::*, @@ -23,11 +25,33 @@ use super::FalconHal; -pub(super) struct Tu102(PhantomData); +/// Writes the trigger register available on GA100 and more recent. +#[expect(dead_code)] +pub(super) fn retrigger_ga100(falcon: &Falcon<'_, E>) { + falcon.pfalcon.write( + Array::at(0), + regs::NV_PFALCON_FALCON_INTR_RETRIGGER::zeroed().with_trigger(true), + ); +} + +/// The falcon HAL for Turing and GA100. GA100 boots like a Turing, but unlike Turing, it has a +/// retrigger register. +pub(super) struct Tu102 { + /// If `true`, the falcons have `NV_PFALCON_FALCON_INTR_RETRIGGER`. + #[expect(dead_code)] + has_intr_retrigger: bool, + _engine: PhantomData, +} impl Tu102 { - pub(super) fn new() -> Self { - Self(PhantomData) + /// Creates a new HAL. + /// + /// `has_intr_retrigger` indicates whether the retrigger register can be used. + pub(super) fn new(has_intr_retrigger: bool) -> Self { + Self { + has_intr_retrigger, + _engine: PhantomData, + } } } @@ -79,4 +103,22 @@ fn reset_eng(&self, falcon: &Falcon<'_, E>) -> Result { fn load_method(&self) -> LoadMethod { LoadMethod::Pio } + + fn host_routed_causes( + &self, + falcon: &Falcon<'_, E>, + latched: regs::NV_PFALCON_FALCON_IRQSTAT, + ) -> regs::NV_PFALCON_FALCON_IRQSTAT { + let pfalcon2 = falcon.pfalcon2; + let mask = pfalcon2.read(regs::tu102::NV_PRISCV_RISCV_IRQMASK).value(); + let dest = pfalcon2.read(regs::tu102::NV_PRISCV_RISCV_IRQDEST).value(); + + regs::NV_PFALCON_FALCON_IRQSTAT::from(latched.into_raw() & mask & dest) + } + + fn retrigger(&self, falcon: &Falcon<'_, E>) { + if self.has_intr_retrigger { + retrigger_ga100(falcon); + } + } } diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 9978fb2803b0..88662da634dc 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -124,11 +124,25 @@ pub(crate) fn usable_fb_size(self) -> u64 { register! { base: PFalconRegisters; + /// Clears the latch of every cause whose bit is written as `1`. Write-only. + /// + /// The write ends the latch and not the source, so a cause driven from outside the falcon + /// stays set. "Retriggering a falcon" in `Documentation/gpu/nova/core/interrupts.rst` names + /// those causes. pub(crate) NV_PFALCON_FALCON_IRQSCLR(u32) @ 0x00000004 { 6:6 swgen0 => bool; 4:4 halt => bool; } + /// Interrupt causes latched in the falcon, one bit per cause, whichever target each is routed + /// to. + /// + /// The causes routed to the host are the ones also set in `NV_PRISCV_RISCV_IRQMASK` and + /// `NV_PRISCV_RISCV_IRQDEST`. + pub(crate) NV_PFALCON_FALCON_IRQSTAT(u32) @ 0x00000008 { + 6:6 swgen0 => bool; + } + pub(crate) NV_PFALCON_FALCON_MAILBOX0(u32) @ 0x00000040 { 31:0 value => u32; } @@ -256,6 +270,16 @@ pub(crate) fn usable_fb_size(self) -> u64 { 0:0 reset => bool; } + /// Makes the falcon re-emit its host-routed causes into the interrupt tree. Write-only. + /// + /// Present from GA100 on. See "Retriggering a falcon" in + /// `Documentation/gpu/nova/core/interrupts.rst`. + /// + /// The hardware headers declare two elements, and OpenRM writes only the first. + pub(crate) NV_PFALCON_FALCON_INTR_RETRIGGER(u32)[2] @ 0x000003e8 { + 0:0 trigger => bool; + } + pub(crate) NV_PFALCON_FBIF_TRANSCFG(u32)[8] @ 0x00000600 { 2:2 mem_type => FalconFbifMemType; 1:0 target ?=> FalconFbifTarget; @@ -414,6 +438,29 @@ pub(crate) mod gm107 { } } +pub(crate) mod tu102 { + use kernel::io::register; + + use crate::falcon::PFalcon2Registers; + + // The RISC-V interrupt routing registers, at the offsets that Turing and GA100 use. + + register! { + base: PFalcon2Registers; + + /// Enabled causes, one bit per cause. Read-only to the host. + pub(crate) NV_PRISCV_RISCV_IRQMASK(u32) @ 0x000002b4 { + 31:0 value => u32; + } + + /// Causes routed to the host, one bit per cause. A clear bit routes the cause to the + /// RISC-V core. + pub(crate) NV_PRISCV_RISCV_IRQDEST(u32) @ 0x000002b8 { + 31:0 value => u32; + } + } +} + pub(crate) mod ga100 { use kernel::io::register; @@ -430,6 +477,28 @@ pub(crate) mod ga100 { } } +pub(crate) mod ga102 { + use kernel::io::register; + + use crate::falcon::PFalcon2Registers; + + // The RISC-V interrupt routing registers, at the offsets that GA102 and later use. + + register! { + base: PFalcon2Registers; + + /// Same as [`super::tu102::NV_PRISCV_RISCV_IRQMASK`], at the GA102 offset. + pub(crate) NV_PRISCV_RISCV_IRQMASK(u32) @ 0x00000528 { + 31:0 value => u32; + } + + /// Same as [`super::tu102::NV_PRISCV_RISCV_IRQDEST`], at the GA102 offset. + pub(crate) NV_PRISCV_RISCV_IRQDEST(u32) @ 0x0000052c { + 31:0 value => u32; + } + } +} + pub(crate) const NV_THERM_I2CS_SCRATCH_FSP_BOOT_COMPLETE_STATUS_SUCCESS: u32 = 0xff; pub(crate) mod gh100 {