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