[PATCH v5 07/15] gpu: nova-core: wait for GFW boot in probe, not in the Gpu constructor

From: John Hubbard

Date: Tue Sep 29 2026 - 23:44:30 EST


The GPU boots its own firmware, GFW, out of reset, and the driver must
not program the GPU until GFW reports completion.

nova-core waited for GFW inside the Gpu constructor, which also boots
the GSP. Code that has to run after GFW and before GSP boot, such as a
probe-time hardware self-test, had nowhere to go.

Move the wait into probe, ahead of the Gpu constructor, and read the
chipset there from a Spec that probe builds itself. Leave the DMA mask
in the constructor, since it programs the host rather than the GPU.

Assisted-by: LLM
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
---
drivers/gpu/nova-core/driver.rs | 14 +++++++++++++-
drivers/gpu/nova-core/gpu.rs | 26 +++++++++++++++++++-------
2 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 291a047e4d86..fc321c6a10b0 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -24,7 +24,11 @@

use crate::{
api::NovaCoreApi,
- gpu::Gpu, //
+ gpu::{
+ self,
+ Gpu,
+ Spec, //
+ }, //
};

/// Counter for generating unique auxiliary device IDs.
@@ -113,6 +117,14 @@ fn probe<'bound>(
pdev.iomap_region(bar1_idx, c"nova-core/bar1")?
},

+ _: {
+ let spec = Spec::new(pdev.as_ref(), bar)?;
+
+ // We must wait for GFW_BOOT completion before doing any significant setup on
+ // the GPU.
+ gpu::wait_gfw_boot_completion(pdev.as_ref(), bar, spec.chipset)?;
+ },
+
// TODO: Use self-referential pin-init syntax once available.
gpu <- Gpu::new(
pdev,
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index fb6f8a86a503..65715f906030 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -251,7 +251,7 @@ pub struct Spec {
}

impl Spec {
- fn new(dev: &device::Device, bar: Bar0<'_>) -> Result<Spec> {
+ pub(crate) fn new(dev: &device::Device, bar: Bar0<'_>) -> Result<Spec> {
// Some brief notes about boot0 and boot42, in chronological order:
//
// NV04 through NV50:
@@ -397,10 +397,8 @@ pub(crate) fn new<'a>(
dev_info!(dev,"NVIDIA ({})\n", spec);
})?,

- // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
_: {
- let hal = hal::gpu_hal(spec.chipset);
- let dma_mask = hal.dma_mask();
+ let dma_mask = hal::gpu_hal(spec.chipset).dma_mask();

// SAFETY: `Gpu` owns all DMA allocations for this device, and we are
// still constructing it, so no concurrent DMA allocations can exist.
@@ -412,9 +410,6 @@ pub(crate) fn new<'a>(
// SAFETY: `Gpu` owns all DMA allocations for this device, and we are
// still constructing it, so no concurrent DMA allocations can exist.
unsafe { pdev.dma_set_max_seg_size(u32::MAX) };
-
- hal.wait_gfw_boot_completion(bar)
- .inspect_err(|_| dev_err!(dev, "GFW boot did not complete\n"))?;
},

// Initialize this early because `gsp_resources` depends on it.
@@ -533,6 +528,23 @@ pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Bou
}
}

+/// Waits for GFW, the GPU's boot firmware, to report completion.
+///
+/// The driver must not program the GPU before then.
+///
+/// # Errors
+///
+/// `ETIMEDOUT` if GFW does not report completion in time.
+pub(crate) fn wait_gfw_boot_completion(
+ dev: &device::Device<device::Bound>,
+ bar: Bar0<'_>,
+ chipset: Chipset,
+) -> Result {
+ hal::gpu_hal(chipset)
+ .wait_gfw_boot_completion(bar)
+ .inspect_err(|_| dev_err!(dev, "GFW boot did not complete\n"))
+}
+
/// Reads the boot0 register and returns its raw value.
pub(crate) fn boot_0_raw(bar: Bar0<'_>) -> u32 {
bar.read(regs::NV_PMC_BOOT_0).into_raw()
--
2.55.0