[PATCH v3 05/14] gpu: nova-core: add the per-architecture GIN CPU interrupt HAL
From: John Hubbard
Date: Wed Sep 02 2026 - 23:18:15 EST
GIN, the GPU Interrupt and Notification unit, is the GPU's interrupt
controller. Each PCIe function has its own tree, whose leaf count
depends on the GPU family.
Message-signaled delivery stops after each edge until the CPU rearms it,
and the rearm write differs by family and interrupt type:
* Pre-Hopper MSI writes an EOI through the BAR0 PCI configuration space
mirror.
* MSI for Hopper and later cycles the TOP enable bits of every serviced
subtree.
* MSI-X on any family cycles the bits of the handler's own subtree.
Provide the leaf count and the rearm method through a per-architecture
interrupt HAL, and name the interrupt type with nova-core's own two
variants rather than the PCI core's three, which include the
level-triggered INTx that nova-core never allocates.
Assisted-by: Cursor:claude-opus-5
Reviewed-by: Will Pierce <wpierce@xxxxxxxxxx>
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
---
drivers/gpu/nova-core/irq.rs | 14 ++++
drivers/gpu/nova-core/irq/hal.rs | 106 +++++++++++++++++++++++++
drivers/gpu/nova-core/irq/hal/gh100.rs | 29 +++++++
drivers/gpu/nova-core/irq/hal/tu102.rs | 28 +++++++
4 files changed, 177 insertions(+)
create mode 100644 drivers/gpu/nova-core/irq/hal.rs
create mode 100644 drivers/gpu/nova-core/irq/hal/gh100.rs
create mode 100644 drivers/gpu/nova-core/irq/hal/tu102.rs
diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
index 3066ceeb850c..d21dee1b89a0 100644
--- a/drivers/gpu/nova-core/irq.rs
+++ b/drivers/gpu/nova-core/irq.rs
@@ -8,5 +8,19 @@
//!
//! See `Documentation/gpu/nova/core/interrupts.rst`.
+mod hal;
mod interrupt_tree;
mod regs;
+
+/// The message-signaled interrupt type a vector allocation obtained.
+///
+/// nova-core allocates MSI-X or MSI and nothing else, so the level-triggered INTx that
+/// [`kernel::pci::IrqType`] also names has no representation here.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub(crate) enum MsiType {
+ /// One message, raised by every subtree of the tree.
+ Msi,
+
+ /// One table entry per subtree.
+ MsiX,
+}
diff --git a/drivers/gpu/nova-core/irq/hal.rs b/drivers/gpu/nova-core/irq/hal.rs
new file mode 100644
index 000000000000..1ea677e37e56
--- /dev/null
+++ b/drivers/gpu/nova-core/irq/hal.rs
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Per-architecture properties of the GIN CPU interrupt tree.
+
+mod gh100;
+mod tu102;
+
+use kernel::{
+ io::Io,
+ prelude::*, //
+};
+
+use crate::{
+ driver::Bar0,
+ gpu::{
+ Architecture,
+ Chipset, //
+ }, //
+};
+
+use super::{
+ interrupt_tree::{
+ LeafCount,
+ Subtree,
+ SubtreeSet, //
+ },
+ regs,
+ MsiType, //
+};
+
+/// Register write that restores PCI interrupt delivery to the CPU.
+///
+/// A message-signaled interrupt is delivered once per edge, and the PCI side delivers no further
+/// interrupt until the CPU rearms it. A handler that returns without this write receives no more
+/// interrupts.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub(super) enum PciIrqRearmMethod {
+ /// The MSI end-of-interrupt register in the BAR0 PCI configuration-space mirror, used by
+ /// MSI on pre-Hopper GPUs.
+ ConfigMirrorEoi,
+
+ /// A clear then a set of the `TOP` enable bits of every serviced subtree, which produces the
+ /// edge that delivers the next interrupt.
+ ///
+ /// MSI has a single message that every subtree raises, so the rearm covers the whole serviced
+ /// set.
+ TopEnableCycleServiced,
+
+ /// The same enable cycle, restricted to the one subtree the handler serves.
+ ///
+ /// MSI-X gives each subtree its own table entry and its own handler.
+ TopEnableCycleSubtree,
+}
+
+impl PciIrqRearmMethod {
+ /// Performs this method's register write.
+ ///
+ /// `serviced` holds every subtree the driver services, and `subtree` is the one subtree the
+ /// calling handler serves. Each method uses whichever of the two its interrupt type delivers
+ /// on, so both are required.
+ pub(super) fn rearm(self, bar: Bar0<'_>, serviced: SubtreeSet, subtree: Subtree) {
+ let subtrees = match self {
+ // The written value is ignored, so any write rearms delivery.
+ Self::ConfigMirrorEoi => {
+ bar.write(regs::NV_XVE_CYA_2, 0u32.into());
+ return;
+ }
+ Self::TopEnableCycleServiced => serviced,
+ Self::TopEnableCycleSubtree => SubtreeSet::from(subtree),
+ };
+
+ bar.write_reg(
+ regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR::zeroed().with_subtrees(subtrees),
+ );
+ bar.write_reg(
+ regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_SET::zeroed().with_subtrees(subtrees),
+ );
+ }
+}
+
+/// Per-architecture properties of the GIN CPU interrupt tree.
+///
+/// The tree size and the method that rearms PCI interrupt delivery differ by family.
+///
+/// See `Documentation/gpu/nova/core/interrupts.rst`.
+pub(super) trait CpuInterruptHal {
+ /// Returns the number of leaves the CPU tree implements.
+ ///
+ /// [`LeafCount::subtree_set`] gives the subtrees behind them, and
+ /// [`LeafCount::vector_count`] the vectors they carry.
+ fn leaf_count(&self) -> LeafCount;
+
+ /// Returns the method that rearms PCI interrupt delivery for `msi_type`.
+ fn pci_irq_rearm_method(&self, msi_type: MsiType) -> PciIrqRearmMethod;
+}
+
+/// Returns the [`CpuInterruptHal`] for `chipset`.
+pub(super) fn cpu_interrupt_hal(chipset: Chipset) -> &'static dyn CpuInterruptHal {
+ match chipset.arch() {
+ Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ gh100::GH100_HAL
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/irq/hal/gh100.rs b/drivers/gpu/nova-core/irq/hal/gh100.rs
new file mode 100644
index 000000000000..10744ac3ab77
--- /dev/null
+++ b/drivers/gpu/nova-core/irq/hal/gh100.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+use super::{
+ CpuInterruptHal,
+ LeafCount,
+ MsiType,
+ PciIrqRearmMethod, //
+};
+
+/// GIN parameters for Hopper and Blackwell, which implement a 16-leaf CPU tree. Only 12 leaves
+/// carry sources.
+struct Gh100;
+
+impl CpuInterruptHal for Gh100 {
+ fn leaf_count(&self) -> LeafCount {
+ LeafCount::Sixteen
+ }
+
+ fn pci_irq_rearm_method(&self, msi_type: MsiType) -> PciIrqRearmMethod {
+ match msi_type {
+ MsiType::Msi => PciIrqRearmMethod::TopEnableCycleServiced,
+ MsiType::MsiX => PciIrqRearmMethod::TopEnableCycleSubtree,
+ }
+ }
+}
+
+const GH100: Gh100 = Gh100;
+pub(super) const GH100_HAL: &dyn CpuInterruptHal = &GH100;
diff --git a/drivers/gpu/nova-core/irq/hal/tu102.rs b/drivers/gpu/nova-core/irq/hal/tu102.rs
new file mode 100644
index 000000000000..fb8ded59c792
--- /dev/null
+++ b/drivers/gpu/nova-core/irq/hal/tu102.rs
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+use super::{
+ CpuInterruptHal,
+ LeafCount,
+ MsiType,
+ PciIrqRearmMethod, //
+};
+
+/// GIN parameters for Turing, Ampere, and Ada, which implement an 8-leaf CPU tree.
+struct Tu102;
+
+impl CpuInterruptHal for Tu102 {
+ fn leaf_count(&self) -> LeafCount {
+ LeafCount::Eight
+ }
+
+ fn pci_irq_rearm_method(&self, msi_type: MsiType) -> PciIrqRearmMethod {
+ match msi_type {
+ MsiType::Msi => PciIrqRearmMethod::ConfigMirrorEoi,
+ MsiType::MsiX => PciIrqRearmMethod::TopEnableCycleSubtree,
+ }
+ }
+}
+
+const TU102: Tu102 = Tu102;
+pub(super) const TU102_HAL: &dyn CpuInterruptHal = &TU102;
--
2.55.0