1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{device, devres::Devres, error::code::*, pci, prelude::*}; 4 5 use crate::driver::Bar0; 6 use crate::firmware::{Firmware, FIRMWARE_VERSION}; 7 use crate::regs; 8 use crate::util; 9 use core::fmt; 10 11 macro_rules! define_chipset { 12 ({ $($variant:ident = $value:expr),* $(,)* }) => 13 { 14 /// Enum representation of the GPU chipset. 15 #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 16 pub(crate) enum Chipset { 17 $($variant = $value),*, 18 } 19 20 impl Chipset { 21 pub(crate) const ALL: &'static [Chipset] = &[ 22 $( Chipset::$variant, )* 23 ]; 24 25 pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [ 26 $( util::const_bytes_to_str( 27 util::to_lowercase_bytes::<{ stringify!($variant).len() }>( 28 stringify!($variant) 29 ).as_slice() 30 ), )* 31 ]; 32 } 33 34 // TODO replace with something like derive(FromPrimitive) 35 impl TryFrom<u32> for Chipset { 36 type Error = kernel::error::Error; 37 38 fn try_from(value: u32) -> Result<Self, Self::Error> { 39 match value { 40 $( $value => Ok(Chipset::$variant), )* 41 _ => Err(ENODEV), 42 } 43 } 44 } 45 } 46 } 47 48 define_chipset!({ 49 // Turing 50 TU102 = 0x162, 51 TU104 = 0x164, 52 TU106 = 0x166, 53 TU117 = 0x167, 54 TU116 = 0x168, 55 // Ampere 56 GA100 = 0x170, 57 GA102 = 0x172, 58 GA103 = 0x173, 59 GA104 = 0x174, 60 GA106 = 0x176, 61 GA107 = 0x177, 62 // Ada 63 AD102 = 0x192, 64 AD103 = 0x193, 65 AD104 = 0x194, 66 AD106 = 0x196, 67 AD107 = 0x197, 68 }); 69 70 impl Chipset { 71 pub(crate) fn arch(&self) -> Architecture { 72 match self { 73 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 74 Architecture::Turing 75 } 76 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 77 Architecture::Ampere 78 } 79 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 80 Architecture::Ada 81 } 82 } 83 } 84 } 85 86 // TODO 87 // 88 // The resulting strings are used to generate firmware paths, hence the 89 // generated strings have to be stable. 90 // 91 // Hence, replace with something like strum_macros derive(Display). 92 // 93 // For now, redirect to fmt::Debug for convenience. 94 impl fmt::Display for Chipset { 95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 write!(f, "{self:?}") 97 } 98 } 99 100 /// Enum representation of the GPU generation. 101 #[derive(fmt::Debug)] 102 pub(crate) enum Architecture { 103 Turing = 0x16, 104 Ampere = 0x17, 105 Ada = 0x19, 106 } 107 108 impl TryFrom<u8> for Architecture { 109 type Error = Error; 110 111 fn try_from(value: u8) -> Result<Self> { 112 match value { 113 0x16 => Ok(Self::Turing), 114 0x17 => Ok(Self::Ampere), 115 0x19 => Ok(Self::Ada), 116 _ => Err(ENODEV), 117 } 118 } 119 } 120 121 pub(crate) struct Revision { 122 major: u8, 123 minor: u8, 124 } 125 126 impl Revision { 127 fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self { 128 Self { 129 major: boot0.major_revision(), 130 minor: boot0.minor_revision(), 131 } 132 } 133 } 134 135 impl fmt::Display for Revision { 136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 137 write!(f, "{:x}.{:x}", self.major, self.minor) 138 } 139 } 140 141 /// Structure holding the metadata of the GPU. 142 pub(crate) struct Spec { 143 chipset: Chipset, 144 /// The revision of the chipset. 145 revision: Revision, 146 } 147 148 impl Spec { 149 fn new(bar: &Bar0) -> Result<Spec> { 150 let boot0 = regs::NV_PMC_BOOT_0::read(bar); 151 152 Ok(Self { 153 chipset: boot0.chipset()?, 154 revision: Revision::from_boot0(boot0), 155 }) 156 } 157 } 158 159 /// Structure holding the resources required to operate the GPU. 160 #[pin_data] 161 pub(crate) struct Gpu { 162 spec: Spec, 163 /// MMIO mapping of PCI BAR 0 164 bar: Devres<Bar0>, 165 fw: Firmware, 166 } 167 168 impl Gpu { 169 pub(crate) fn new( 170 pdev: &pci::Device<device::Bound>, 171 devres_bar: Devres<Bar0>, 172 ) -> Result<impl PinInit<Self>> { 173 let bar = devres_bar.access(pdev.as_ref())?; 174 let spec = Spec::new(bar)?; 175 let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?; 176 177 dev_info!( 178 pdev.as_ref(), 179 "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n", 180 spec.chipset, 181 spec.chipset.arch(), 182 spec.revision 183 ); 184 185 Ok(pin_init!(Self { 186 spec, 187 bar: devres_bar, 188 fw 189 })) 190 } 191 } 192