mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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 <jhubbard@nvidia.com> Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://patch.msgid.link/20260411024953.473149-7-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
parent
46d455853d
commit
8ff326fa0a
|
|
@ -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(|_| ())
|
||||
}
|
||||
|
|
@ -20,11 +20,12 @@
|
|||
Falcon, //
|
||||
},
|
||||
fb::SysmemFlush,
|
||||
gfw,
|
||||
gsp::Gsp,
|
||||
regs,
|
||||
};
|
||||
|
||||
mod hal;
|
||||
|
||||
macro_rules! define_chipset {
|
||||
({ $($variant:ident = $value:expr),* $(,)* }) =>
|
||||
{
|
||||
|
|
@ -274,7 +275,7 @@ pub(crate) fn new<'a>(
|
|||
|
||||
// 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"))?;
|
||||
},
|
||||
|
||||
|
|
|
|||
29
drivers/gpu/nova-core/gpu/hal.rs
Normal file
29
drivers/gpu/nova-core/gpu/hal.rs
Normal file
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
86
drivers/gpu/nova-core/gpu/hal/tu102.rs
Normal file
86
drivers/gpu/nova-core/gpu/hal/tu102.rs
Normal file
|
|
@ -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;
|
||||
|
|
@ -17,7 +17,6 @@
|
|||
mod falcon;
|
||||
mod fb;
|
||||
mod firmware;
|
||||
mod gfw;
|
||||
mod gpu;
|
||||
mod gsp;
|
||||
#[macro_use]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user