xref: /linux/drivers/gpu/nova-core/gpu.rs (revision f77be04d00d8ce403ecaf547f1515a844bbde060)
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 impl fmt::Display for Spec {
184     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185         write!(
186             f,
187             "Chipset: {}, Architecture: {:?}, Revision: {}",
188             self.chipset,
189             self.chipset.arch(),
190             self.revision
191         )
192     }
193 }
194 
195 /// Structure holding the resources required to operate the GPU.
196 #[pin_data]
197 pub(crate) struct Gpu {
198     spec: Spec,
199     /// MMIO mapping of PCI BAR 0
200     bar: Arc<Devres<Bar0>>,
201     /// System memory page required for flushing all pending GPU-side memory writes done through
202     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
203     sysmem_flush: SysmemFlush,
204     /// GSP falcon instance, used for GSP boot up and cleanup.
205     gsp_falcon: Falcon<GspFalcon>,
206     /// SEC2 falcon instance, used for GSP boot up and cleanup.
207     sec2_falcon: Falcon<Sec2Falcon>,
208     /// GSP runtime data. Temporarily an empty placeholder.
209     #[pin]
210     gsp: Gsp,
211 }
212 
213 impl Gpu {
214     pub(crate) fn new<'a>(
215         pdev: &'a pci::Device<device::Bound>,
216         devres_bar: Arc<Devres<Bar0>>,
217         bar: &'a Bar0,
218     ) -> impl PinInit<Self, Error> + 'a {
219         try_pin_init!(Self {
220             spec: Spec::new(bar).inspect(|spec| {
221                 dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
222             })?,
223 
224             // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
225             _: {
226                 gfw::wait_gfw_boot_completion(bar)
227                     .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
228             },
229 
230             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
231 
232             gsp_falcon: Falcon::new(
233                 pdev.as_ref(),
234                 spec.chipset,
235             )
236             .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
237 
238             sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
239 
240             gsp <- Gsp::new(pdev)?,
241 
242             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
243 
244             bar: devres_bar,
245         })
246     }
247 
248     /// Called when the corresponding [`Device`](device::Device) is unbound.
249     ///
250     /// Note: This method must only be called from `Driver::unbind`.
251     pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
252         kernel::warn_on!(self
253             .bar
254             .access(dev)
255             .inspect(|bar| self.sysmem_flush.unregister(bar))
256             .is_err());
257     }
258 }
259