xref: /linux/drivers/gpu/nova-core/gpu.rs (revision 50b3e0c7c82f32e6ac3ead30f0e0ba96d36a4ff6)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use kernel::{
4     device,
5     devres::Devres,
6     fmt,
7     pci,
8     prelude::*,
9     sync::Arc, //
10 };
11 
12 use crate::{
13     driver::Bar0,
14     falcon::{
15         gsp::Gsp as GspFalcon,
16         sec2::Sec2 as Sec2Falcon,
17         Falcon, //
18     },
19     fb::SysmemFlush,
20     gfw,
21     gsp::Gsp,
22     regs,
23 };
24 
25 macro_rules! define_chipset {
26     ({ $($variant:ident = $value:expr),* $(,)* }) =>
27     {
28         /// Enum representation of the GPU chipset.
29         #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
30         pub(crate) enum Chipset {
31             $($variant = $value),*,
32         }
33 
34         impl Chipset {
35             pub(crate) const ALL: &'static [Chipset] = &[
36                 $( Chipset::$variant, )*
37             ];
38 
39             ::kernel::macros::paste!(
40             /// Returns the name of this chipset, in lowercase.
41             ///
42             /// # Examples
43             ///
44             /// ```
45             /// let chipset = Chipset::GA102;
46             /// assert_eq!(chipset.name(), "ga102");
47             /// ```
48             pub(crate) const fn name(&self) -> &'static str {
49                 match *self {
50                 $(
51                     Chipset::$variant => stringify!([<$variant:lower>]),
52                 )*
53                 }
54             }
55             );
56         }
57 
58         // TODO[FPRI]: replace with something like derive(FromPrimitive)
59         impl TryFrom<u32> for Chipset {
60             type Error = kernel::error::Error;
61 
62             fn try_from(value: u32) -> Result<Self, Self::Error> {
63                 match value {
64                     $( $value => Ok(Chipset::$variant), )*
65                     _ => Err(ENODEV),
66                 }
67             }
68         }
69     }
70 }
71 
72 define_chipset!({
73     // Turing
74     TU102 = 0x162,
75     TU104 = 0x164,
76     TU106 = 0x166,
77     TU117 = 0x167,
78     TU116 = 0x168,
79     // Ampere
80     GA100 = 0x170,
81     GA102 = 0x172,
82     GA103 = 0x173,
83     GA104 = 0x174,
84     GA106 = 0x176,
85     GA107 = 0x177,
86     // Ada
87     AD102 = 0x192,
88     AD103 = 0x193,
89     AD104 = 0x194,
90     AD106 = 0x196,
91     AD107 = 0x197,
92 });
93 
94 impl Chipset {
95     pub(crate) const fn arch(self) -> Architecture {
96         match self {
97             Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
98                 Architecture::Turing
99             }
100             Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
101                 Architecture::Ampere
102             }
103             Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
104                 Architecture::Ada
105             }
106         }
107     }
108 
109     /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC.
110     ///
111     /// This includes all chipsets < GA102.
112     pub(crate) const fn needs_fwsec_bootloader(self) -> bool {
113         matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100)
114     }
115 }
116 
117 // TODO
118 //
119 // The resulting strings are used to generate firmware paths, hence the
120 // generated strings have to be stable.
121 //
122 // Hence, replace with something like strum_macros derive(Display).
123 //
124 // For now, redirect to fmt::Debug for convenience.
125 impl fmt::Display for Chipset {
126     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127         write!(f, "{self:?}")
128     }
129 }
130 
131 /// Enum representation of the GPU generation.
132 ///
133 /// TODO: remove the `Default` trait implementation, and the `#[default]`
134 /// attribute, once the register!() macro (which creates Architecture items) no
135 /// longer requires it for read-only fields.
136 #[derive(fmt::Debug, Default, Copy, Clone)]
137 #[repr(u8)]
138 pub(crate) enum Architecture {
139     #[default]
140     Turing = 0x16,
141     Ampere = 0x17,
142     Ada = 0x19,
143 }
144 
145 impl TryFrom<u8> for Architecture {
146     type Error = Error;
147 
148     fn try_from(value: u8) -> Result<Self> {
149         match value {
150             0x16 => Ok(Self::Turing),
151             0x17 => Ok(Self::Ampere),
152             0x19 => Ok(Self::Ada),
153             _ => Err(ENODEV),
154         }
155     }
156 }
157 
158 impl From<Architecture> for u8 {
159     fn from(value: Architecture) -> Self {
160         // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
161         value as u8
162     }
163 }
164 
165 pub(crate) struct Revision {
166     major: u8,
167     minor: u8,
168 }
169 
170 impl From<regs::NV_PMC_BOOT_42> for Revision {
171     fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
172         Self {
173             major: boot0.major_revision(),
174             minor: boot0.minor_revision(),
175         }
176     }
177 }
178 
179 impl fmt::Display for Revision {
180     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181         write!(f, "{:x}.{:x}", self.major, self.minor)
182     }
183 }
184 
185 /// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
186 pub(crate) struct Spec {
187     chipset: Chipset,
188     revision: Revision,
189 }
190 
191 impl Spec {
192     fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> {
193         // Some brief notes about boot0 and boot42, in chronological order:
194         //
195         // NV04 through NV50:
196         //
197         //    Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs.
198         //    boot42 may not even exist on some of these GPUs.
199         //
200         // Fermi through Volta:
201         //
202         //     Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42
203         //     is also guaranteed to be both present and accurate.
204         //
205         // Turing and later:
206         //
207         //     Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not
208         //     from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU.
209         //     Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs.
210 
211         let boot0 = regs::NV_PMC_BOOT_0::read(bar);
212 
213         if boot0.is_older_than_fermi() {
214             return Err(ENODEV);
215         }
216 
217         let boot42 = regs::NV_PMC_BOOT_42::read(bar);
218         Spec::try_from(boot42).inspect_err(|_| {
219             dev_err!(dev, "Unsupported chipset: {}\n", boot42);
220         })
221     }
222 }
223 
224 impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
225     type Error = Error;
226 
227     fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
228         Ok(Self {
229             chipset: boot42.chipset()?,
230             revision: boot42.into(),
231         })
232     }
233 }
234 
235 impl fmt::Display for Spec {
236     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237         f.write_fmt(fmt!(
238             "Chipset: {}, Architecture: {:?}, Revision: {}",
239             self.chipset,
240             self.chipset.arch(),
241             self.revision
242         ))
243     }
244 }
245 
246 /// Structure holding the resources required to operate the GPU.
247 #[pin_data]
248 pub(crate) struct Gpu {
249     spec: Spec,
250     /// MMIO mapping of PCI BAR 0
251     bar: Arc<Devres<Bar0>>,
252     /// System memory page required for flushing all pending GPU-side memory writes done through
253     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
254     sysmem_flush: SysmemFlush,
255     /// GSP falcon instance, used for GSP boot up and cleanup.
256     gsp_falcon: Falcon<GspFalcon>,
257     /// SEC2 falcon instance, used for GSP boot up and cleanup.
258     sec2_falcon: Falcon<Sec2Falcon>,
259     /// GSP runtime data. Temporarily an empty placeholder.
260     #[pin]
261     gsp: Gsp,
262 }
263 
264 impl Gpu {
265     pub(crate) fn new<'a>(
266         pdev: &'a pci::Device<device::Bound>,
267         devres_bar: Arc<Devres<Bar0>>,
268         bar: &'a Bar0,
269     ) -> impl PinInit<Self, Error> + 'a {
270         try_pin_init!(Self {
271             spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
272                 dev_info!(pdev,"NVIDIA ({})\n", spec);
273             })?,
274 
275             // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
276             _: {
277                 gfw::wait_gfw_boot_completion(bar)
278                     .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
279             },
280 
281             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
282 
283             gsp_falcon: Falcon::new(
284                 pdev.as_ref(),
285                 spec.chipset,
286             )
287             .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
288 
289             sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
290 
291             gsp <- Gsp::new(pdev),
292 
293             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
294 
295             bar: devres_bar,
296         })
297     }
298 
299     /// Called when the corresponding [`Device`](device::Device) is unbound.
300     ///
301     /// Note: This method must only be called from `Driver::unbind`.
302     pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
303         kernel::warn_on!(self
304             .bar
305             .access(dev)
306             .inspect(|bar| self.sysmem_flush.unregister(bar))
307             .is_err());
308     }
309 }
310