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