From: John Hubbard Date: Sat, 11 Apr 2026 02:49:31 +0000 (-0700) Subject: gpu: nova-core: move GFW boot wait into a GPU HAL X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=8ff326fa0aa19babae405b5fadeaa49b57b9a75f;p=thirdparty%2Flinux.git gpu: nova-core: move GFW boot wait into a GPU HAL Introduce a GpuHal trait and per-family dispatch so GPU boot behavior can vary by architecture. Move wait_gfw_boot_completion() from the standalone gfw module into gpu/hal/tu102.rs as the first GpuHal implementation. All architectures currently dispatch to this implementation, preserving existing behavior. Signed-off-by: John Hubbard Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260411024953.473149-7-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot --- diff --git a/drivers/gpu/nova-core/gfw.rs b/drivers/gpu/nova-core/gfw.rs deleted file mode 100644 index fb75dd10a172e..0000000000000 --- a/drivers/gpu/nova-core/gfw.rs +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! GPU Firmware (`GFW`) support, a.k.a `devinit`. -//! -//! Upon reset, the GPU runs some firmware code from the BIOS to setup its core parameters. Most of -//! the GPU is considered unusable until this step is completed, so we must wait on it before -//! performing driver initialization. -//! -//! A clarification about devinit terminology: devinit is a sequence of register read/writes after -//! reset that performs tasks such as: -//! 1. Programming VRAM memory controller timings. -//! 2. Power sequencing. -//! 3. Clock and PLL configuration. -//! 4. Thermal management. -//! -//! devinit itself is a 'script' which is interpreted by an interpreter program typically running -//! on the PMU microcontroller. -//! -//! Note that the devinit sequence also needs to run during suspend/resume. - -use kernel::{ - io::{ - poll::read_poll_timeout, - Io, // - }, - prelude::*, - time::Delta, // -}; - -use crate::{ - driver::Bar0, - regs, // -}; - -/// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds timeout. -/// -/// Upon GPU reset, several microcontrollers (such as PMU, SEC2, GSP etc) run some firmware code to -/// setup its core parameters. Most of the GPU is considered unusable until this step is completed, -/// so it must be waited on very early during driver initialization. -/// -/// The `GFW` code includes several components that need to execute before the driver loads. These -/// components are located in the VBIOS ROM and executed in a sequence on these different -/// microcontrollers. The devinit sequence typically runs on the PMU, and the FWSEC runs on the -/// GSP. -/// -/// This function waits for a signal indicating that core initialization is complete. Before this -/// signal is received, little can be done with the GPU. This signal is set by the FWSEC running on -/// the GSP in Heavy-secured mode. -pub(crate) fn wait_gfw_boot_completion(bar: &Bar0) -> Result { - // Before accessing the completion status in `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05`, we must - // first check `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK`. This is because - // `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05` becomes accessible only after the secure firmware - // (FWSEC) lowers the privilege level to allow CPU (LS/Light-secured) access. We can only - // safely read the status register from CPU (LS/Light-secured) once the mask indicates - // that the privilege level has been lowered. - // - // TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put out of - // reset, and should complete in less time than that. - read_poll_timeout( - || { - Ok( - // Check that FWSEC has lowered its protection level before reading the GFW_BOOT - // status. - bar.read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK) - .read_protection_level0() - && bar - .read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT) - .completed(), - ) - }, - |&gfw_booted| gfw_booted, - Delta::from_millis(1), - Delta::from_secs(4), - ) - .map(|_| ()) -} diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 3e908b65d7a96..659f6a24ee130 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -20,11 +20,12 @@ use crate::{ Falcon, // }, fb::SysmemFlush, - gfw, gsp::Gsp, regs, }; +mod hal; + macro_rules! define_chipset { ({ $($variant:ident = $value:expr),* $(,)* }) => { @@ -274,7 +275,7 @@ impl Gpu { // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. _: { - gfw::wait_gfw_boot_completion(bar) + hal::gpu_hal(spec.chipset).wait_gfw_boot_completion(bar) .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?; }, diff --git a/drivers/gpu/nova-core/gpu/hal.rs b/drivers/gpu/nova-core/gpu/hal.rs new file mode 100644 index 0000000000000..a261c6af92bee --- /dev/null +++ b/drivers/gpu/nova-core/gpu/hal.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 + +use kernel::prelude::*; + +use crate::{ + driver::Bar0, + gpu::{ + Architecture, + Chipset, // + }, +}; + +mod tu102; + +pub(crate) trait GpuHal { + /// Waits for GFW_BOOT completion if required by this hardware family. + fn wait_gfw_boot_completion(&self, bar: &Bar0) -> Result; +} + +pub(super) fn gpu_hal(chipset: Chipset) -> &'static dyn GpuHal { + match chipset.arch() { + Architecture::Turing + | Architecture::Ampere + | Architecture::Ada + | Architecture::Hopper + | Architecture::BlackwellGB10x + | Architecture::BlackwellGB20x => tu102::TU102_HAL, + } +} diff --git a/drivers/gpu/nova-core/gpu/hal/tu102.rs b/drivers/gpu/nova-core/gpu/hal/tu102.rs new file mode 100644 index 0000000000000..08dd4434bd72a --- /dev/null +++ b/drivers/gpu/nova-core/gpu/hal/tu102.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GPU Firmware (`GFW`) support, a.k.a `devinit`. +//! +//! Upon reset, the GPU runs some firmware code from the BIOS to setup its core parameters. Most of +//! the GPU is considered unusable until this step is completed, so we must wait on it before +//! performing driver initialization. +//! +//! A clarification about devinit terminology: devinit is a sequence of register read/writes after +//! reset that performs tasks such as: +//! 1. Programming VRAM memory controller timings. +//! 2. Power sequencing. +//! 3. Clock and PLL configuration. +//! 4. Thermal management. +//! +//! devinit itself is a 'script' which is interpreted by an interpreter program typically running +//! on the PMU microcontroller. +//! +//! Note that the devinit sequence also needs to run during suspend/resume. + +use kernel::{ + io::{ + poll::read_poll_timeout, + Io, // + }, + prelude::*, + time::Delta, // +}; + +use crate::{ + driver::Bar0, + regs, // +}; + +use super::GpuHal; + +struct Tu102; + +impl GpuHal for Tu102 { + /// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds + /// timeout. + /// + /// Upon GPU reset, several microcontrollers (such as PMU, SEC2, GSP etc) run some firmware + /// code to setup its core parameters. Most of the GPU is considered unusable until this step + /// is completed, so it must be waited on very early during driver initialization. + /// + /// The `GFW` code includes several components that need to execute before the driver loads. + /// These components are located in the VBIOS ROM and executed in a sequence on these different + /// microcontrollers. The devinit sequence typically runs on the PMU, and the FWSEC runs on the + /// GSP. + /// + /// This function waits for a signal indicating that core initialization is complete. Before + /// this signal is received, little can be done with the GPU. This signal is set by the FWSEC + /// running on the GSP in Heavy-secured mode. + fn wait_gfw_boot_completion(&self, bar: &Bar0) -> Result { + // Before accessing the completion status in `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05`, we must + // first check `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK`. This is because + // `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05` becomes accessible only after the secure firmware + // (FWSEC) lowers the privilege level to allow CPU (LS/Light-secured) access. We can only + // safely read the status register from CPU (LS/Light-secured) once the mask indicates + // that the privilege level has been lowered. + // + // TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put + // out of reset, and should complete in less time than that. + read_poll_timeout( + || { + Ok( + // Check that FWSEC has lowered its protection level before reading the + // GFW_BOOT status. + bar.read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK) + .read_protection_level0() + && bar + .read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT) + .completed(), + ) + }, + |&gfw_booted| gfw_booted, + Delta::from_millis(1), + Delta::from_secs(4), + ) + .map(|_| ()) + } +} + +const TU102: Tu102 = Tu102; +pub(super) const TU102_HAL: &dyn GpuHal = &TU102; diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 04a1fa6b25f8e..3a609f6937e4f 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -17,7 +17,6 @@ mod driver; mod falcon; mod fb; mod firmware; -mod gfw; mod gpu; mod gsp; #[macro_use]