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