1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{ 4 device, 5 fmt, 6 io::Io, 7 num::Bounded, 8 pci, 9 prelude::*, // 10 }; 11 12 use crate::{ 13 bounded_enum, 14 driver::Bar0, 15 falcon::{ 16 gsp::Gsp as GspFalcon, 17 sec2::Sec2 as Sec2Falcon, 18 Falcon, // 19 }, 20 fb::SysmemFlush, 21 gsp::Gsp, 22 regs, 23 }; 24 25 mod hal; 26 27 macro_rules! define_chipset { 28 ({ $($variant:ident = $value:expr),* $(,)* }) => 29 { 30 /// Enum representation of the GPU chipset. 31 #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 32 pub(crate) enum Chipset { 33 $($variant = $value),*, 34 } 35 36 impl Chipset { 37 pub(crate) const ALL: &'static [Chipset] = &[ 38 $( Chipset::$variant, )* 39 ]; 40 41 ::kernel::macros::paste!( 42 /// Returns the name of this chipset, in lowercase. 43 /// 44 /// # Examples 45 /// 46 /// ``` 47 /// let chipset = Chipset::GA102; 48 /// assert_eq!(chipset.name(), "ga102"); 49 /// ``` 50 pub(crate) const fn name(&self) -> &'static str { 51 match *self { 52 $( 53 Chipset::$variant => stringify!([<$variant:lower>]), 54 )* 55 } 56 } 57 ); 58 } 59 60 // TODO[FPRI]: replace with something like derive(FromPrimitive) 61 impl TryFrom<u32> for Chipset { 62 type Error = kernel::error::Error; 63 64 fn try_from(value: u32) -> Result<Self, Self::Error> { 65 match value { 66 $( $value => Ok(Chipset::$variant), )* 67 _ => Err(ENODEV), 68 } 69 } 70 } 71 } 72 } 73 74 define_chipset!({ 75 // Turing 76 TU102 = 0x162, 77 TU104 = 0x164, 78 TU106 = 0x166, 79 TU117 = 0x167, 80 TU116 = 0x168, 81 // Ampere 82 GA100 = 0x170, 83 GA102 = 0x172, 84 GA103 = 0x173, 85 GA104 = 0x174, 86 GA106 = 0x176, 87 GA107 = 0x177, 88 // Hopper 89 GH100 = 0x180, 90 // Ada 91 AD102 = 0x192, 92 AD103 = 0x193, 93 AD104 = 0x194, 94 AD106 = 0x196, 95 AD107 = 0x197, 96 // Blackwell GB10x 97 GB100 = 0x1a0, 98 GB102 = 0x1a2, 99 // Blackwell GB20x 100 GB202 = 0x1b2, 101 GB203 = 0x1b3, 102 GB205 = 0x1b5, 103 GB206 = 0x1b6, 104 GB207 = 0x1b7, 105 }); 106 107 impl Chipset { 108 pub(crate) const fn arch(self) -> Architecture { 109 match self { 110 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 111 Architecture::Turing 112 } 113 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 114 Architecture::Ampere 115 } 116 Self::GH100 => Architecture::Hopper, 117 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 118 Architecture::Ada 119 } 120 Self::GB100 | Self::GB102 => Architecture::BlackwellGB10x, 121 Self::GB202 | Self::GB203 | Self::GB205 | Self::GB206 | Self::GB207 => { 122 Architecture::BlackwellGB20x 123 } 124 } 125 } 126 127 /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC. 128 /// 129 /// This includes all chipsets < GA102. 130 pub(crate) const fn needs_fwsec_bootloader(self) -> bool { 131 matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100) 132 } 133 } 134 135 // TODO 136 // 137 // The resulting strings are used to generate firmware paths, hence the 138 // generated strings have to be stable. 139 // 140 // Hence, replace with something like strum_macros derive(Display). 141 // 142 // For now, redirect to fmt::Debug for convenience. 143 impl fmt::Display for Chipset { 144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 145 write!(f, "{self:?}") 146 } 147 } 148 149 bounded_enum! { 150 /// Enum representation of the GPU generation. 151 #[derive(fmt::Debug, Copy, Clone)] 152 pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> { 153 Turing = 0x16, 154 Ampere = 0x17, 155 Hopper = 0x18, 156 Ada = 0x19, 157 BlackwellGB10x = 0x1a, 158 BlackwellGB20x = 0x1b, 159 } 160 } 161 162 #[derive(Clone, Copy)] 163 pub(crate) struct Revision { 164 major: Bounded<u8, 4>, 165 minor: Bounded<u8, 4>, 166 } 167 168 impl From<regs::NV_PMC_BOOT_42> for Revision { 169 fn from(boot0: regs::NV_PMC_BOOT_42) -> Self { 170 Self { 171 major: boot0.major_revision().cast(), 172 minor: boot0.minor_revision().cast(), 173 } 174 } 175 } 176 177 impl fmt::Display for Revision { 178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 179 write!(f, "{:x}.{:x}", self.major, self.minor) 180 } 181 } 182 183 /// Structure holding a basic description of the GPU: `Chipset` and `Revision`. 184 #[derive(Clone, Copy)] 185 pub(crate) struct Spec { 186 chipset: Chipset, 187 revision: Revision, 188 } 189 190 impl Spec { 191 fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> { 192 // Some brief notes about boot0 and boot42, in chronological order: 193 // 194 // NV04 through NV50: 195 // 196 // Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs. 197 // boot42 may not even exist on some of these GPUs. 198 // 199 // Fermi through Volta: 200 // 201 // Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42 202 // is also guaranteed to be both present and accurate. 203 // 204 // Turing and later: 205 // 206 // Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not 207 // from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU. 208 // Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs. 209 210 let boot0 = bar.read(regs::NV_PMC_BOOT_0); 211 212 if boot0.is_older_than_fermi() { 213 return Err(ENODEV); 214 } 215 216 let boot42 = bar.read(regs::NV_PMC_BOOT_42); 217 Spec::try_from(boot42).inspect_err(|_| { 218 dev_err!(dev, "Unsupported chipset: {}\n", boot42); 219 }) 220 } 221 } 222 223 impl TryFrom<regs::NV_PMC_BOOT_42> for Spec { 224 type Error = Error; 225 226 fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> { 227 Ok(Self { 228 chipset: boot42.chipset()?, 229 revision: boot42.into(), 230 }) 231 } 232 } 233 234 impl fmt::Display for Spec { 235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 236 f.write_fmt(fmt!( 237 "Chipset: {}, Architecture: {:?}, Revision: {}", 238 self.chipset, 239 self.chipset.arch(), 240 self.revision 241 )) 242 } 243 } 244 245 /// Structure holding the resources required to operate the GPU. 246 #[pin_data] 247 pub(crate) struct Gpu<'gpu> { 248 spec: Spec, 249 /// MMIO mapping of PCI BAR 0. 250 bar: &'gpu Bar0, 251 /// System memory page required for flushing all pending GPU-side memory writes done through 252 /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). 253 sysmem_flush: SysmemFlush<'gpu>, 254 /// GSP falcon instance, used for GSP boot up and cleanup. 255 gsp_falcon: Falcon<GspFalcon>, 256 /// SEC2 falcon instance, used for GSP boot up and cleanup. 257 sec2_falcon: Falcon<Sec2Falcon>, 258 /// GSP runtime data. Temporarily an empty placeholder. 259 #[pin] 260 gsp: Gsp, 261 } 262 263 impl<'gpu> Gpu<'gpu> { 264 pub(crate) fn new( 265 pdev: &'gpu pci::Device<device::Bound>, 266 bar: &'gpu Bar0, 267 ) -> impl PinInit<Self, Error> + 'gpu { 268 try_pin_init!(Self { 269 spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| { 270 dev_info!(pdev,"NVIDIA ({})\n", spec); 271 })?, 272 273 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. 274 _: { 275 hal::gpu_hal(spec.chipset).wait_gfw_boot_completion(bar) 276 .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?; 277 }, 278 279 bar, 280 281 sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, 282 283 gsp_falcon: Falcon::new( 284 pdev.as_ref(), 285 spec.chipset, 286 ) 287 .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, 288 289 sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, 290 291 gsp <- Gsp::new(pdev), 292 293 _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? }, 294 }) 295 } 296 } 297