mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
We will need to perform things like allocating DMA memory during device creation, so make sure to take the device context that will allow us to perform these actions. This also allows us to use Devres::access to obtain the BAR without holding a RCU lock. Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://lore.kernel.org/r/20250507-nova-frts-v3-4-fcb02749754d@nvidia.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
208 lines
5.2 KiB
Rust
208 lines
5.2 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
use kernel::{
|
|
device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString,
|
|
};
|
|
|
|
use crate::driver::Bar0;
|
|
use crate::regs;
|
|
use crate::util;
|
|
use core::fmt;
|
|
|
|
macro_rules! define_chipset {
|
|
({ $($variant:ident = $value:expr),* $(,)* }) =>
|
|
{
|
|
/// Enum representation of the GPU chipset.
|
|
#[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
|
pub(crate) enum Chipset {
|
|
$($variant = $value),*,
|
|
}
|
|
|
|
impl Chipset {
|
|
pub(crate) const ALL: &'static [Chipset] = &[
|
|
$( Chipset::$variant, )*
|
|
];
|
|
|
|
pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [
|
|
$( util::const_bytes_to_str(
|
|
util::to_lowercase_bytes::<{ stringify!($variant).len() }>(
|
|
stringify!($variant)
|
|
).as_slice()
|
|
), )*
|
|
];
|
|
}
|
|
|
|
// TODO replace with something like derive(FromPrimitive)
|
|
impl TryFrom<u32> for Chipset {
|
|
type Error = kernel::error::Error;
|
|
|
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
|
match value {
|
|
$( $value => Ok(Chipset::$variant), )*
|
|
_ => Err(ENODEV),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
define_chipset!({
|
|
// Turing
|
|
TU102 = 0x162,
|
|
TU104 = 0x164,
|
|
TU106 = 0x166,
|
|
TU117 = 0x167,
|
|
TU116 = 0x168,
|
|
// Ampere
|
|
GA100 = 0x170,
|
|
GA102 = 0x172,
|
|
GA103 = 0x173,
|
|
GA104 = 0x174,
|
|
GA106 = 0x176,
|
|
GA107 = 0x177,
|
|
// Ada
|
|
AD102 = 0x192,
|
|
AD103 = 0x193,
|
|
AD104 = 0x194,
|
|
AD106 = 0x196,
|
|
AD107 = 0x197,
|
|
});
|
|
|
|
impl Chipset {
|
|
pub(crate) fn arch(&self) -> Architecture {
|
|
match self {
|
|
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
|
|
Architecture::Turing
|
|
}
|
|
Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
|
|
Architecture::Ampere
|
|
}
|
|
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
|
|
Architecture::Ada
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO
|
|
//
|
|
// The resulting strings are used to generate firmware paths, hence the
|
|
// generated strings have to be stable.
|
|
//
|
|
// Hence, replace with something like strum_macros derive(Display).
|
|
//
|
|
// For now, redirect to fmt::Debug for convenience.
|
|
impl fmt::Display for Chipset {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{:?}", self)
|
|
}
|
|
}
|
|
|
|
/// Enum representation of the GPU generation.
|
|
#[derive(fmt::Debug)]
|
|
pub(crate) enum Architecture {
|
|
Turing,
|
|
Ampere,
|
|
Ada,
|
|
}
|
|
|
|
pub(crate) struct Revision {
|
|
major: u8,
|
|
minor: u8,
|
|
}
|
|
|
|
impl Revision {
|
|
fn from_boot0(boot0: regs::Boot0) -> Self {
|
|
Self {
|
|
major: boot0.major_rev(),
|
|
minor: boot0.minor_rev(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Revision {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{:x}.{:x}", self.major, self.minor)
|
|
}
|
|
}
|
|
|
|
/// Structure holding the metadata of the GPU.
|
|
pub(crate) struct Spec {
|
|
chipset: Chipset,
|
|
/// The revision of the chipset.
|
|
revision: Revision,
|
|
}
|
|
|
|
impl Spec {
|
|
fn new(bar: &Bar0) -> Result<Spec> {
|
|
let boot0 = regs::Boot0::read(bar);
|
|
|
|
Ok(Self {
|
|
chipset: boot0.chipset().try_into()?,
|
|
revision: Revision::from_boot0(boot0),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Structure encapsulating the firmware blobs required for the GPU to operate.
|
|
#[expect(dead_code)]
|
|
pub(crate) struct Firmware {
|
|
booter_load: firmware::Firmware,
|
|
booter_unload: firmware::Firmware,
|
|
bootloader: firmware::Firmware,
|
|
gsp: firmware::Firmware,
|
|
}
|
|
|
|
impl Firmware {
|
|
fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> {
|
|
let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?;
|
|
chip_name.make_ascii_lowercase();
|
|
|
|
let request = |name_| {
|
|
CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
|
|
.and_then(|path| firmware::Firmware::request(&path, dev))
|
|
};
|
|
|
|
Ok(Firmware {
|
|
booter_load: request("booter_load")?,
|
|
booter_unload: request("booter_unload")?,
|
|
bootloader: request("bootloader")?,
|
|
gsp: request("gsp")?,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Structure holding the resources required to operate the GPU.
|
|
#[pin_data]
|
|
pub(crate) struct Gpu {
|
|
spec: Spec,
|
|
/// MMIO mapping of PCI BAR 0
|
|
bar: Devres<Bar0>,
|
|
fw: Firmware,
|
|
}
|
|
|
|
impl Gpu {
|
|
pub(crate) fn new(
|
|
pdev: &pci::Device<device::Bound>,
|
|
devres_bar: Devres<Bar0>,
|
|
) -> Result<impl PinInit<Self>> {
|
|
let bar = devres_bar.access(pdev.as_ref())?;
|
|
let spec = Spec::new(bar)?;
|
|
let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
|
|
|
|
dev_info!(
|
|
pdev.as_ref(),
|
|
"NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
|
|
spec.chipset,
|
|
spec.chipset.arch(),
|
|
spec.revision
|
|
);
|
|
|
|
Ok(pin_init!(Self {
|
|
spec,
|
|
bar: devres_bar,
|
|
fw
|
|
}))
|
|
}
|
|
}
|