1 // SPDX-License-Identifier: GPL-2.0 2 3 use kernel::{device, devres::Devres, error::code::*, pci, prelude::*}; 4 5 use crate::driver::Bar0; 6 use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon}; 7 use crate::fb::SysmemFlush; 8 use crate::firmware::{Firmware, FIRMWARE_VERSION}; 9 use crate::gfw; 10 use crate::regs; 11 use crate::util; 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 pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [ 29 $( util::const_bytes_to_str( 30 util::to_lowercase_bytes::<{ stringify!($variant).len() }>( 31 stringify!($variant) 32 ).as_slice() 33 ), )* 34 ]; 35 } 36 37 // TODO replace with something like derive(FromPrimitive) 38 impl TryFrom<u32> for Chipset { 39 type Error = kernel::error::Error; 40 41 fn try_from(value: u32) -> Result<Self, Self::Error> { 42 match value { 43 $( $value => Ok(Chipset::$variant), )* 44 _ => Err(ENODEV), 45 } 46 } 47 } 48 } 49 } 50 51 define_chipset!({ 52 // Turing 53 TU102 = 0x162, 54 TU104 = 0x164, 55 TU106 = 0x166, 56 TU117 = 0x167, 57 TU116 = 0x168, 58 // Ampere 59 GA100 = 0x170, 60 GA102 = 0x172, 61 GA103 = 0x173, 62 GA104 = 0x174, 63 GA106 = 0x176, 64 GA107 = 0x177, 65 // Ada 66 AD102 = 0x192, 67 AD103 = 0x193, 68 AD104 = 0x194, 69 AD106 = 0x196, 70 AD107 = 0x197, 71 }); 72 73 impl Chipset { 74 pub(crate) fn arch(&self) -> Architecture { 75 match self { 76 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => { 77 Architecture::Turing 78 } 79 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => { 80 Architecture::Ampere 81 } 82 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => { 83 Architecture::Ada 84 } 85 } 86 } 87 } 88 89 // TODO 90 // 91 // The resulting strings are used to generate firmware paths, hence the 92 // generated strings have to be stable. 93 // 94 // Hence, replace with something like strum_macros derive(Display). 95 // 96 // For now, redirect to fmt::Debug for convenience. 97 impl fmt::Display for Chipset { 98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 99 write!(f, "{self:?}") 100 } 101 } 102 103 /// Enum representation of the GPU generation. 104 #[derive(fmt::Debug)] 105 pub(crate) enum Architecture { 106 Turing = 0x16, 107 Ampere = 0x17, 108 Ada = 0x19, 109 } 110 111 impl TryFrom<u8> for Architecture { 112 type Error = Error; 113 114 fn try_from(value: u8) -> Result<Self> { 115 match value { 116 0x16 => Ok(Self::Turing), 117 0x17 => Ok(Self::Ampere), 118 0x19 => Ok(Self::Ada), 119 _ => Err(ENODEV), 120 } 121 } 122 } 123 124 pub(crate) struct Revision { 125 major: u8, 126 minor: u8, 127 } 128 129 impl Revision { 130 fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self { 131 Self { 132 major: boot0.major_revision(), 133 minor: boot0.minor_revision(), 134 } 135 } 136 } 137 138 impl fmt::Display for Revision { 139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 140 write!(f, "{:x}.{:x}", self.major, self.minor) 141 } 142 } 143 144 /// Structure holding the metadata of the GPU. 145 pub(crate) struct Spec { 146 chipset: Chipset, 147 /// The revision of the chipset. 148 revision: Revision, 149 } 150 151 impl Spec { 152 fn new(bar: &Bar0) -> Result<Spec> { 153 let boot0 = regs::NV_PMC_BOOT_0::read(bar); 154 155 Ok(Self { 156 chipset: boot0.chipset()?, 157 revision: Revision::from_boot0(boot0), 158 }) 159 } 160 } 161 162 /// Structure holding the resources required to operate the GPU. 163 #[pin_data(PinnedDrop)] 164 pub(crate) struct Gpu { 165 spec: Spec, 166 /// MMIO mapping of PCI BAR 0 167 bar: Devres<Bar0>, 168 fw: Firmware, 169 /// System memory page required for flushing all pending GPU-side memory writes done through 170 /// PCIE into system memory. 171 sysmem_flush: SysmemFlush, 172 } 173 174 #[pinned_drop] 175 impl PinnedDrop for Gpu { 176 fn drop(self: Pin<&mut Self>) { 177 // Unregister the sysmem flush page before we release it. 178 self.bar 179 .try_access_with(|b| self.sysmem_flush.unregister(b)); 180 } 181 } 182 183 impl Gpu { 184 pub(crate) fn new( 185 pdev: &pci::Device<device::Bound>, 186 devres_bar: Devres<Bar0>, 187 ) -> Result<impl PinInit<Self>> { 188 let bar = devres_bar.access(pdev.as_ref())?; 189 let spec = Spec::new(bar)?; 190 let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?; 191 192 dev_info!( 193 pdev.as_ref(), 194 "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n", 195 spec.chipset, 196 spec.chipset.arch(), 197 spec.revision 198 ); 199 200 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. 201 gfw::wait_gfw_boot_completion(bar) 202 .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?; 203 204 // System memory page required for sysmembar to properly flush into system memory. 205 let sysmem_flush = SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?; 206 207 let gsp_falcon = Falcon::<Gsp>::new( 208 pdev.as_ref(), 209 spec.chipset, 210 bar, 211 spec.chipset > Chipset::GA100, 212 )?; 213 gsp_falcon.clear_swgen0_intr(bar); 214 215 let _sec2_falcon = Falcon::<Sec2>::new(pdev.as_ref(), spec.chipset, bar, true)?; 216 217 Ok(pin_init!(Self { 218 spec, 219 bar: devres_bar, 220 fw, 221 sysmem_flush, 222 })) 223 } 224 } 225