xref: /linux/drivers/gpu/nova-core/gpu.rs (revision e54ad0cd3673c93cdafda58505eaa81610fe3aef)
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 #[derive(fmt::Debug)]
126 pub(crate) enum Architecture {
127     Turing = 0x16,
128     Ampere = 0x17,
129     Ada = 0x19,
130 }
131 
132 impl TryFrom<u8> for Architecture {
133     type Error = Error;
134 
135     fn try_from(value: u8) -> Result<Self> {
136         match value {
137             0x16 => Ok(Self::Turing),
138             0x17 => Ok(Self::Ampere),
139             0x19 => Ok(Self::Ada),
140             _ => Err(ENODEV),
141         }
142     }
143 }
144 
145 pub(crate) struct Revision {
146     major: u8,
147     minor: u8,
148 }
149 
150 impl Revision {
151     fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
152         Self {
153             major: boot0.major_revision(),
154             minor: boot0.minor_revision(),
155         }
156     }
157 }
158 
159 impl fmt::Display for Revision {
160     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161         write!(f, "{:x}.{:x}", self.major, self.minor)
162     }
163 }
164 
165 /// Structure holding the metadata of the GPU.
166 pub(crate) struct Spec {
167     chipset: Chipset,
168     /// The revision of the chipset.
169     revision: Revision,
170 }
171 
172 impl Spec {
173     fn new(bar: &Bar0) -> Result<Spec> {
174         let boot0 = regs::NV_PMC_BOOT_0::read(bar);
175 
176         Ok(Self {
177             chipset: boot0.chipset()?,
178             revision: Revision::from_boot0(boot0),
179         })
180     }
181 }
182 
183 /// Structure holding the resources required to operate the GPU.
184 #[pin_data]
185 pub(crate) struct Gpu {
186     spec: Spec,
187     /// MMIO mapping of PCI BAR 0
188     bar: Arc<Devres<Bar0>>,
189     /// System memory page required for flushing all pending GPU-side memory writes done through
190     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
191     sysmem_flush: SysmemFlush,
192     /// GSP falcon instance, used for GSP boot up and cleanup.
193     gsp_falcon: Falcon<GspFalcon>,
194     /// SEC2 falcon instance, used for GSP boot up and cleanup.
195     sec2_falcon: Falcon<Sec2Falcon>,
196     /// GSP runtime data. Temporarily an empty placeholder.
197     #[pin]
198     gsp: Gsp,
199 }
200 
201 impl Gpu {
202     pub(crate) fn new<'a>(
203         pdev: &'a pci::Device<device::Bound>,
204         devres_bar: Arc<Devres<Bar0>>,
205         bar: &'a Bar0,
206     ) -> impl PinInit<Self, Error> + 'a {
207         try_pin_init!(Self {
208             spec: Spec::new(bar).inspect(|spec| {
209                 dev_info!(
210                     pdev.as_ref(),
211                     "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
212                     spec.chipset,
213                     spec.chipset.arch(),
214                     spec.revision
215                 );
216             })?,
217 
218             // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
219             _: {
220                 gfw::wait_gfw_boot_completion(bar)
221                     .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
222             },
223 
224             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
225 
226             gsp_falcon: Falcon::new(
227                 pdev.as_ref(),
228                 spec.chipset,
229             )
230             .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
231 
232             sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
233 
234             gsp <- Gsp::new(),
235 
236             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
237 
238             bar: devres_bar,
239         })
240     }
241 
242     /// Called when the corresponding [`Device`](device::Device) is unbound.
243     ///
244     /// Note: This method must only be called from `Driver::unbind`.
245     pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
246         kernel::warn_on!(self
247             .bar
248             .access(dev)
249             .inspect(|bar| self.sysmem_flush.unregister(bar))
250             .is_err());
251     }
252 }
253