Upon reset, the GPU executes the GFW (GPU Firmware) in order to initialize its base parameters such as clocks. The driver must ensure that this step is completed before using the hardware.
Signed-off-by: Alexandre Courbot <acour...@nvidia.com> --- drivers/gpu/nova-core/gfw.rs | 37 +++++++++++++++++++++++++++++++++++++ drivers/gpu/nova-core/gpu.rs | 5 +++++ drivers/gpu/nova-core/nova_core.rs | 1 + drivers/gpu/nova-core/regs.rs | 25 +++++++++++++++++++++++++ drivers/gpu/nova-core/util.rs | 1 - 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gfw.rs b/drivers/gpu/nova-core/gfw.rs new file mode 100644 index 0000000000000000000000000000000000000000..11ad480e1da826555e264101ef56ff0f69db8f95 --- /dev/null +++ b/drivers/gpu/nova-core/gfw.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! GPU Firmware (GFW) support. +//! +//! 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. + +use core::time::Duration; + +use kernel::bindings; +use kernel::prelude::*; + +use crate::driver::Bar0; +use crate::regs; +use crate::util; + +/// Wait until GFW (GPU Firmware) completes, or a 4 seconds timeout elapses. +pub(crate) fn wait_gfw_boot_completion(bar: &Bar0) -> Result<()> { + util::wait_on(Duration::from_secs(4), || { + // Check that FWSEC has lowered its protection level before reading the GFW_BOOT + // status. + let gfw_booted = regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar) + .read_protection_level0() + && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed(); + + if gfw_booted { + Some(()) + } else { + // Avoid busy-looping. + // SAFETY: msleep should be safe to call with any parameter. + unsafe { bindings::msleep(1) }; + + None + } + }) +} diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 99c6796e73e924cb5fd2b6f49d84589c1ce5f627..50417f608dc7b445958ae43444a13c7593204fcf 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -4,6 +4,7 @@ use crate::driver::Bar0; use crate::firmware::{Firmware, FIRMWARE_VERSION}; +use crate::gfw; use crate::regs; use crate::util; use core::fmt; @@ -182,6 +183,10 @@ pub(crate) fn new( spec.revision ); + // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. + gfw::wait_gfw_boot_completion(bar) + .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?; + Ok(pin_init!(Self { spec, bar: devres_bar, diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs index 618632f0abcc8f5ef6945a04fc084acc4ecbf20b..c3fde3e132ea658888851137ab47fcb7b3637577 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -4,6 +4,7 @@ mod driver; mod firmware; +mod gfw; mod gpu; mod regs; mod util; diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 5a12732303066f78b8ec5745096cef632ff3bfba..cba442da51181971f209b338249307c11ac481e3 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -37,3 +37,28 @@ pub(crate) fn chipset(self) -> Result<Chipset> { .and_then(Chipset::try_from) } } + +/* PGC6 */ + +register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128 { + 0:0 read_protection_level0 as bool, "Set after FWSEC lowers its protection level"; +}); + +// TODO: This is an array of registers. +register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234 { + 31:0 value as u32; +}); + +register!( + NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT => NV_PGC6_AON_SECURE_SCRATCH_GROUP_05, + "Scratch group 05 register 0 used as GFW boot progress indicator" { + 7:0 progress as u8, "Progress of GFW boot (0xff means completed)"; + } +); + +impl NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT { + /// Returns `true` if GFW boot is completed. + pub(crate) fn completed(self) -> bool { + self.progress() == 0xff + } +} diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs index afb525228431a2645afe7bb34988e9537757b1d7..81fcfff1f6f437d2f6a2130ce2249fbf4c1501be 100644 --- a/drivers/gpu/nova-core/util.rs +++ b/drivers/gpu/nova-core/util.rs @@ -34,7 +34,6 @@ pub(crate) const fn const_bytes_to_str(bytes: &[u8]) -> &str { /// /// TODO: replace with `read_poll_timeout` once it is available. /// (https://lore.kernel.org/lkml/20250220070611.214262-8-fujita.tomon...@gmail.com/) -#[expect(dead_code)] pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(timeout: Duration, cond: F) -> Result<R> { let start_time = Ktime::ktime_get(); -- 2.49.0