1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc}; 4 5 use crate::driver::Bar0; 6 use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon}; 7 use crate::fb::SysmemFlush; 8 use crate::firmware::{Firmware, FIRMWARE_VERSION}; 9 use crate::gfw; 10 use crate::gsp::Gsp; 11 use crate::regs; 12 use core::fmt; 13 14 macro_rules! define_chipset { 15 ({ $($variant:ident = $value:expr),* $(,)* }) => 16 { 17 /// Enum representation of the GPU chipset. 18 #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 19 pub(crate) enum Chipset { 20 $($variant = $value),*, 21 } 22 23 impl Chipset { 24 pub(crate) const ALL: &'static [Chipset] = &[ 25 $( Chipset::$variant, )* 26 ]; 27 28 ::kernel::macros::paste!( 29 /// Returns the name of this chipset, in lowercase. 30 /// 31 /// # Examples 32 /// 33 /// ``` 34 /// let chipset = Chipset::GA102; 35 /// assert_eq!(chipset.name(), "ga102"); 36 /// ``` 37 pub(crate) const fn name(&self) -> &'static str { 38 match *self { 39 $( 40 Chipset::$variant => stringify!([<$variant:lower>]), 41 )* 42 } 43 } 44 ); 45 } 46 47 // TODO[FPRI]: replace with something like derive(FromPrimitive) 48 impl TryFrom<u32> for Chipset { 49 type Error = kernel::error::Error; 50 51 fn try_from(value: u32) -> Result<Self, Self::Error> { 52 match value { 53 $( $value => Ok(Chipset::$variant), )* 54 _ => Err(ENODEV), 55 } 56 } 57 } 58 } 59 } 60 61 define_chipset!({ 62 // Turing 63 TU102 = 0x162, 64 TU104 = 0x164, 65 TU106 = 0x166, 66 TU117 = 0x167, 67 TU116 = 0x168, 68 // Ampere 69 GA100 = 0x170, 70 GA102 = 0x172, 71 GA103 = 0x173, 72 GA104 = 0x174, 73 GA106 = 0x176, 74 GA107 = 0x177, 75 // Ada 76 AD102 = 0x192, 77 AD103 = 0x193, 78 AD104 = 0x194, 79 AD106 = 0x196, 80 AD107 = 0x197, 81 }); 82 83 impl Chipset { 84 pub(crate) fn arch(&self) -> Architecture { 85 match self { 86 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 87 Architecture::Turing 88 } 89 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 90 Architecture::Ampere 91 } 92 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 93 Architecture::Ada 94 } 95 } 96 } 97 } 98 99 // TODO 100 // 101 // The resulting strings are used to generate firmware paths, hence the 102 // generated strings have to be stable. 103 // 104 // Hence, replace with something like strum_macros derive(Display). 105 // 106 // For now, redirect to fmt::Debug for convenience. 107 impl fmt::Display for Chipset { 108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 109 write!(f, "{self:?}") 110 } 111 } 112 113 /// Enum representation of the GPU generation. 114 #[derive(fmt::Debug)] 115 pub(crate) enum Architecture { 116 Turing = 0x16, 117 Ampere = 0x17, 118 Ada = 0x19, 119 } 120 121 impl TryFrom<u8> for Architecture { 122 type Error = Error; 123 124 fn try_from(value: u8) -> Result<Self> { 125 match value { 126 0x16 => Ok(Self::Turing), 127 0x17 => Ok(Self::Ampere), 128 0x19 => Ok(Self::Ada), 129 _ => Err(ENODEV), 130 } 131 } 132 } 133 134 pub(crate) struct Revision { 135 major: u8, 136 minor: u8, 137 } 138 139 impl Revision { 140 fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self { 141 Self { 142 major: boot0.major_revision(), 143 minor: boot0.minor_revision(), 144 } 145 } 146 } 147 148 impl fmt::Display for Revision { 149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 150 write!(f, "{:x}.{:x}", self.major, self.minor) 151 } 152 } 153 154 /// Structure holding the metadata of the GPU. 155 pub(crate) struct Spec { 156 chipset: Chipset, 157 /// The revision of the chipset. 158 revision: Revision, 159 } 160 161 impl Spec { 162 fn new(bar: &Bar0) -> Result<Spec> { 163 let boot0 = regs::NV_PMC_BOOT_0::read(bar); 164 165 Ok(Self { 166 chipset: boot0.chipset()?, 167 revision: Revision::from_boot0(boot0), 168 }) 169 } 170 } 171 172 /// Structure holding the resources required to operate the GPU. 173 #[pin_data] 174 pub(crate) struct Gpu { 175 spec: Spec, 176 /// MMIO mapping of PCI BAR 0 177 bar: Arc<Devres<Bar0>>, 178 fw: Firmware, 179 /// System memory page required for flushing all pending GPU-side memory writes done through 180 /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation). 181 sysmem_flush: SysmemFlush, 182 /// GSP falcon instance, used for GSP boot up and cleanup. 183 gsp_falcon: Falcon<GspFalcon>, 184 /// SEC2 falcon instance, used for GSP boot up and cleanup. 185 sec2_falcon: Falcon<Sec2Falcon>, 186 /// GSP runtime data. Temporarily an empty placeholder. 187 #[pin] 188 gsp: Gsp, 189 } 190 191 impl Gpu { 192 pub(crate) fn new<'a>( 193 pdev: &'a pci::Device<device::Bound>, 194 devres_bar: Arc<Devres<Bar0>>, 195 bar: &'a Bar0, 196 ) -> impl PinInit<Self, Error> + 'a { 197 try_pin_init!(Self { 198 spec: Spec::new(bar).inspect(|spec| { 199 dev_info!( 200 pdev.as_ref(), 201 "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n", 202 spec.chipset, 203 spec.chipset.arch(), 204 spec.revision 205 ); 206 })?, 207 208 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. 209 _: { 210 gfw::wait_gfw_boot_completion(bar) 211 .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?; 212 }, 213 214 fw <- Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?, 215 216 sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?, 217 218 gsp_falcon: Falcon::new( 219 pdev.as_ref(), 220 spec.chipset, 221 bar, 222 spec.chipset > Chipset::GA100, 223 ) 224 .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, 225 226 sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar, true)?, 227 228 gsp <- Gsp::new(), 229 230 _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? }, 231 232 bar: devres_bar, 233 }) 234 } 235 236 /// Called when the corresponding [`Device`](device::Device) is unbound. 237 /// 238 /// Note: This method must only be called from `Driver::unbind`. 239 pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) { 240 kernel::warn_on!(self 241 .bar 242 .access(dev) 243 .inspect(|bar| self.sysmem_flush.unregister(bar)) 244 .is_err()); 245 } 246 } 247