xref: /linux/drivers/gpu/nova-core/gpu.rs (revision 6dfafbd0299a60bfb5d5e277fdf100037c7ded07)
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) 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 
110 // TODO
111 //
112 // The resulting strings are used to generate firmware paths, hence the
113 // generated strings have to be stable.
114 //
115 // Hence, replace with something like strum_macros derive(Display).
116 //
117 // For now, redirect to fmt::Debug for convenience.
118 impl fmt::Display for Chipset {
119     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120         write!(f, "{self:?}")
121     }
122 }
123 
124 /// Enum representation of the GPU generation.
125 ///
126 /// TODO: remove the `Default` trait implementation, and the `#[default]`
127 /// attribute, once the register!() macro (which creates Architecture items) no
128 /// longer requires it for read-only fields.
129 #[derive(fmt::Debug, Default, Copy, Clone)]
130 #[repr(u8)]
131 pub(crate) enum Architecture {
132     #[default]
133     Turing = 0x16,
134     Ampere = 0x17,
135     Ada = 0x19,
136 }
137 
138 impl TryFrom<u8> for Architecture {
139     type Error = Error;
140 
141     fn try_from(value: u8) -> Result<Self> {
142         match value {
143             0x16 => Ok(Self::Turing),
144             0x17 => Ok(Self::Ampere),
145             0x19 => Ok(Self::Ada),
146             _ => Err(ENODEV),
147         }
148     }
149 }
150 
151 impl From<Architecture> for u8 {
152     fn from(value: Architecture) -> Self {
153         // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
154         value as u8
155     }
156 }
157 
158 pub(crate) struct Revision {
159     major: u8,
160     minor: u8,
161 }
162 
163 impl From<regs::NV_PMC_BOOT_42> for Revision {
164     fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
165         Self {
166             major: boot0.major_revision(),
167             minor: boot0.minor_revision(),
168         }
169     }
170 }
171 
172 impl fmt::Display for Revision {
173     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174         write!(f, "{:x}.{:x}", self.major, self.minor)
175     }
176 }
177 
178 /// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
179 pub(crate) struct Spec {
180     chipset: Chipset,
181     revision: Revision,
182 }
183 
184 impl Spec {
185     fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> {
186         // Some brief notes about boot0 and boot42, in chronological order:
187         //
188         // NV04 through NV50:
189         //
190         //    Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs.
191         //    boot42 may not even exist on some of these GPUs.
192         //
193         // Fermi through Volta:
194         //
195         //     Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42
196         //     is also guaranteed to be both present and accurate.
197         //
198         // Turing and later:
199         //
200         //     Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not
201         //     from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU.
202         //     Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs.
203 
204         let boot0 = regs::NV_PMC_BOOT_0::read(bar);
205 
206         if boot0.is_older_than_fermi() {
207             return Err(ENODEV);
208         }
209 
210         let boot42 = regs::NV_PMC_BOOT_42::read(bar);
211         Spec::try_from(boot42).inspect_err(|_| {
212             dev_err!(dev, "Unsupported chipset: {}\n", boot42);
213         })
214     }
215 }
216 
217 impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
218     type Error = Error;
219 
220     fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
221         Ok(Self {
222             chipset: boot42.chipset()?,
223             revision: boot42.into(),
224         })
225     }
226 }
227 
228 impl fmt::Display for Spec {
229     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230         f.write_fmt(fmt!(
231             "Chipset: {}, Architecture: {:?}, Revision: {}",
232             self.chipset,
233             self.chipset.arch(),
234             self.revision
235         ))
236     }
237 }
238 
239 /// Structure holding the resources required to operate the GPU.
240 #[pin_data]
241 pub(crate) struct Gpu {
242     spec: Spec,
243     /// MMIO mapping of PCI BAR 0
244     bar: Arc<Devres<Bar0>>,
245     /// System memory page required for flushing all pending GPU-side memory writes done through
246     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
247     sysmem_flush: SysmemFlush,
248     /// GSP falcon instance, used for GSP boot up and cleanup.
249     gsp_falcon: Falcon<GspFalcon>,
250     /// SEC2 falcon instance, used for GSP boot up and cleanup.
251     sec2_falcon: Falcon<Sec2Falcon>,
252     /// GSP runtime data. Temporarily an empty placeholder.
253     #[pin]
254     gsp: Gsp,
255 }
256 
257 impl Gpu {
258     pub(crate) fn new<'a>(
259         pdev: &'a pci::Device<device::Bound>,
260         devres_bar: Arc<Devres<Bar0>>,
261         bar: &'a Bar0,
262     ) -> impl PinInit<Self, Error> + 'a {
263         try_pin_init!(Self {
264             spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
265                 dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
266             })?,
267 
268             // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
269             _: {
270                 gfw::wait_gfw_boot_completion(bar)
271                     .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
272             },
273 
274             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
275 
276             gsp_falcon: Falcon::new(
277                 pdev.as_ref(),
278                 spec.chipset,
279             )
280             .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
281 
282             sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
283 
284             gsp <- Gsp::new(pdev)?,
285 
286             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
287 
288             bar: devres_bar,
289         })
290     }
291 
292     /// Called when the corresponding [`Device`](device::Device) is unbound.
293     ///
294     /// Note: This method must only be called from `Driver::unbind`.
295     pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
296         kernel::warn_on!(self
297             .bar
298             .access(dev)
299             .inspect(|bar| self.sysmem_flush.unregister(bar))
300             .is_err());
301     }
302 }
303