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