Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc};
4
5use crate::driver::Bar0;
6use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
7use crate::fb::FbLayout;
8use crate::fb::SysmemFlush;
9use crate::firmware::fwsec::{FwsecCommand, FwsecFirmware};
10use crate::firmware::{Firmware, FIRMWARE_VERSION};
11use crate::gfw;
12use crate::regs;
13use crate::util;
14use crate::vbios::Vbios;
15use core::fmt;
16
17macro_rules! define_chipset {
18 ({ $($variant:ident = $value:expr),* $(,)* }) =>
19 {
20 /// Enum representation of the GPU chipset.
21 #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
22 pub(crate) enum Chipset {
23 $($variant = $value),*,
24 }
25
26 impl Chipset {
27 pub(crate) const ALL: &'static [Chipset] = &[
28 $( Chipset::$variant, )*
29 ];
30
31 pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [
32 $( util::const_bytes_to_str(
33 util::to_lowercase_bytes::<{ stringify!($variant).len() }>(
34 stringify!($variant)
35 ).as_slice()
36 ), )*
37 ];
38 }
39
40 // TODO[FPRI]: replace with something like derive(FromPrimitive)
41 impl TryFrom<u32> for Chipset {
42 type Error = kernel::error::Error;
43
44 fn try_from(value: u32) -> Result<Self, Self::Error> {
45 match value {
46 $( $value => Ok(Chipset::$variant), )*
47 _ => Err(ENODEV),
48 }
49 }
50 }
51 }
52}
53
54define_chipset!({
55 // Turing
56 TU102 = 0x162,
57 TU104 = 0x164,
58 TU106 = 0x166,
59 TU117 = 0x167,
60 TU116 = 0x168,
61 // Ampere
62 GA100 = 0x170,
63 GA102 = 0x172,
64 GA103 = 0x173,
65 GA104 = 0x174,
66 GA106 = 0x176,
67 GA107 = 0x177,
68 // Ada
69 AD102 = 0x192,
70 AD103 = 0x193,
71 AD104 = 0x194,
72 AD106 = 0x196,
73 AD107 = 0x197,
74});
75
76impl Chipset {
77 pub(crate) fn arch(&self) -> Architecture {
78 match self {
79 Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
80 Architecture::Turing
81 }
82 Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
83 Architecture::Ampere
84 }
85 Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
86 Architecture::Ada
87 }
88 }
89 }
90}
91
92// TODO
93//
94// The resulting strings are used to generate firmware paths, hence the
95// generated strings have to be stable.
96//
97// Hence, replace with something like strum_macros derive(Display).
98//
99// For now, redirect to fmt::Debug for convenience.
100impl fmt::Display for Chipset {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(f, "{self:?}")
103 }
104}
105
106/// Enum representation of the GPU generation.
107#[derive(fmt::Debug)]
108pub(crate) enum Architecture {
109 Turing = 0x16,
110 Ampere = 0x17,
111 Ada = 0x19,
112}
113
114impl TryFrom<u8> for Architecture {
115 type Error = Error;
116
117 fn try_from(value: u8) -> Result<Self> {
118 match value {
119 0x16 => Ok(Self::Turing),
120 0x17 => Ok(Self::Ampere),
121 0x19 => Ok(Self::Ada),
122 _ => Err(ENODEV),
123 }
124 }
125}
126
127pub(crate) struct Revision {
128 major: u8,
129 minor: u8,
130}
131
132impl Revision {
133 fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
134 Self {
135 major: boot0.major_revision(),
136 minor: boot0.minor_revision(),
137 }
138 }
139}
140
141impl fmt::Display for Revision {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 write!(f, "{:x}.{:x}", self.major, self.minor)
144 }
145}
146
147/// Structure holding the metadata of the GPU.
148pub(crate) struct Spec {
149 chipset: Chipset,
150 /// The revision of the chipset.
151 revision: Revision,
152}
153
154impl Spec {
155 fn new(bar: &Bar0) -> Result<Spec> {
156 let boot0 = regs::NV_PMC_BOOT_0::read(bar);
157
158 Ok(Self {
159 chipset: boot0.chipset()?,
160 revision: Revision::from_boot0(boot0),
161 })
162 }
163}
164
165/// Structure holding the resources required to operate the GPU.
166#[pin_data(PinnedDrop)]
167pub(crate) struct Gpu {
168 spec: Spec,
169 /// MMIO mapping of PCI BAR 0
170 bar: Arc<Devres<Bar0>>,
171 fw: Firmware,
172 /// System memory page required for flushing all pending GPU-side memory writes done through
173 /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
174 sysmem_flush: SysmemFlush,
175}
176
177#[pinned_drop]
178impl PinnedDrop for Gpu {
179 fn drop(self: Pin<&mut Self>) {
180 // Unregister the sysmem flush page before we release it.
181 self.bar
182 .try_access_with(|b| self.sysmem_flush.unregister(b));
183 }
184}
185
186impl Gpu {
187 /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
188 /// created the WPR2 region.
189 ///
190 /// TODO: this needs to be moved into a larger type responsible for booting the whole GSP
191 /// (`GspBooter`?).
192 fn run_fwsec_frts(
193 dev: &device::Device<device::Bound>,
194 falcon: &Falcon<Gsp>,
195 bar: &Bar0,
196 bios: &Vbios,
197 fb_layout: &FbLayout,
198 ) -> Result<()> {
199 // Check that the WPR2 region does not already exists - if it does, we cannot run
200 // FWSEC-FRTS until the GPU is reset.
201 if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 {
202 dev_err!(
203 dev,
204 "WPR2 region already exists - GPU needs to be reset to proceed\n"
205 );
206 return Err(EBUSY);
207 }
208
209 let fwsec_frts = FwsecFirmware::new(
210 dev,
211 falcon,
212 bar,
213 bios,
214 FwsecCommand::Frts {
215 frts_addr: fb_layout.frts.start,
216 frts_size: fb_layout.frts.end - fb_layout.frts.start,
217 },
218 )?;
219
220 // Run FWSEC-FRTS to create the WPR2 region.
221 fwsec_frts.run(dev, falcon, bar)?;
222
223 // SCRATCH_E contains the error code for FWSEC-FRTS.
224 let frts_status = regs::NV_PBUS_SW_SCRATCH_0E::read(bar).frts_err_code();
225 if frts_status != 0 {
226 dev_err!(
227 dev,
228 "FWSEC-FRTS returned with error code {:#x}",
229 frts_status
230 );
231
232 return Err(EIO);
233 }
234
235 // Check that the WPR2 region has been created as we requested.
236 let (wpr2_lo, wpr2_hi) = (
237 regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(),
238 regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(),
239 );
240
241 match (wpr2_lo, wpr2_hi) {
242 (_, 0) => {
243 dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
244
245 Err(EIO)
246 }
247 (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
248 dev_err!(
249 dev,
250 "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
251 wpr2_lo,
252 fb_layout.frts.start,
253 );
254
255 Err(EIO)
256 }
257 (wpr2_lo, wpr2_hi) => {
258 dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
259 dev_dbg!(dev, "GPU instance built\n");
260
261 Ok(())
262 }
263 }
264 }
265
266 pub(crate) fn new(
267 pdev: &pci::Device<device::Bound>,
268 devres_bar: Arc<Devres<Bar0>>,
269 ) -> Result<impl PinInit<Self>> {
270 let bar = devres_bar.access(pdev.as_ref())?;
271 let spec = Spec::new(bar)?;
272 let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?;
273
274 dev_info!(
275 pdev.as_ref(),
276 "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
277 spec.chipset,
278 spec.chipset.arch(),
279 spec.revision
280 );
281
282 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
283 gfw::wait_gfw_boot_completion(bar)
284 .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
285
286 let sysmem_flush = SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?;
287
288 let gsp_falcon = Falcon::<Gsp>::new(
289 pdev.as_ref(),
290 spec.chipset,
291 bar,
292 spec.chipset > Chipset::GA100,
293 )?;
294 gsp_falcon.clear_swgen0_intr(bar);
295
296 let _sec2_falcon = Falcon::<Sec2>::new(pdev.as_ref(), spec.chipset, bar, true)?;
297
298 let fb_layout = FbLayout::new(spec.chipset, bar)?;
299 dev_dbg!(pdev.as_ref(), "{:#x?}\n", fb_layout);
300
301 let bios = Vbios::new(pdev, bar)?;
302
303 Self::run_fwsec_frts(pdev.as_ref(), &gsp_falcon, bar, &bios, &fb_layout)?;
304
305 Ok(pin_init!(Self {
306 spec,
307 bar: devres_bar,
308 fw,
309 sysmem_flush,
310 }))
311 }
312}