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