gpu: nova-core: add the falcon interrupt registers and HAL methods

A falcon latches each interrupt cause that is raised in its IRQSTAT
register. On a RISC-V falcon, each cause is routed either to the host,
meaning the CPU, or to the falcon's own core, and two routing registers
hold that routing. The falcon signals the interrupt tree only when its
set of host-routed causes goes from empty to non-empty, so a handler
that clears the tree leaf while a cause is still latched has to write
INTR_RETRIGGER, which makes the falcon re-emit its causes. Turing
falcons have no INTR_RETRIGGER, and the routing registers have
different offsets from GA102 on.

Add IRQSTAT, INTR_RETRIGGER, and the two routing registers, the last at
both sets of offsets, and add two falcon HAL methods: one intersects an
IRQSTAT value with the routing registers, and one retriggers the
falcon, or does nothing on Turing. GA100 keeps the Turing HAL, as it
does for boot, and the retrigger register is a property of that HAL's
instance.

The two methods have no user yet.

Assisted-by: LLM
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patch.msgid.link/20260930034148.590687-13-jhubbard@nvidia.com
[acourbot: remove implementation-specific comments on interface doc.]
[acourbot: make constructor take boolean instead of having two methods.]
[acourbot: add NVIDIA copyright headers to modified files.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
John Hubbard 2026-09-29 20:41:45 -07:00 committed by Alexandre Courbot
parent 621b455c56
commit e727d1c382
4 changed files with 152 additions and 6 deletions

View File

@ -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<E: FalconEngine + 'static>(
) -> Result<KBox<dyn FalconHal<E>>> {
let hal = match chipset.arch() {
Architecture::Turing => {
KBox::new(tu102::Tu102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
KBox::new(tu102::Tu102::<E>::new(false), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
// 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::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
KBox::new(tu102::Tu102::<E>::new(true), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
Architecture::Ampere
| Architecture::Ada

View File

@ -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);
}
}

View File

@ -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<E: FalconEngine>(PhantomData<E>);
/// Writes the trigger register available on GA100 and more recent.
#[expect(dead_code)]
pub(super) fn retrigger_ga100<E: FalconEngine>(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<E: FalconEngine> {
/// If `true`, the falcons have `NV_PFALCON_FALCON_INTR_RETRIGGER`.
#[expect(dead_code)]
has_intr_retrigger: bool,
_engine: PhantomData<E>,
}
impl<E: FalconEngine> Tu102<E> {
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);
}
}
}

View File

@ -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 {