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