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