xref: /linux/drivers/gpu/nova-core/gpu.rs (revision e7c96980ea4d93e79b43b07c5489d700207b0055)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc};
4 
5 use crate::driver::Bar0;
6 use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon};
7 use crate::fb::SysmemFlush;
8 use crate::firmware::{Firmware, FIRMWARE_VERSION};
9 use crate::gfw;
10 use crate::gsp::Gsp;
11 use crate::regs;
12 use crate::util;
13 use core::fmt;
14 
15 macro_rules! define_chipset {
16     ({ $($variant:ident = $value:expr),* $(,)* }) =>
17     {
18         /// Enum representation of the GPU chipset.
19         #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
20         pub(crate) enum Chipset {
21             $($variant = $value),*,
22         }
23 
24         impl Chipset {
25             pub(crate) const ALL: &'static [Chipset] = &[
26                 $( Chipset::$variant, )*
27             ];
28 
29             pub(crate) const NAMES: [&'static str; Self::ALL.len()] = [
30                 $( util::const_bytes_to_str(
31                         util::to_lowercase_bytes::<{ stringify!($variant).len() }>(
32                             stringify!($variant)
33                         ).as_slice()
34                 ), )*
35             ];
36         }
37 
38         // TODO[FPRI]: replace with something like derive(FromPrimitive)
39         impl TryFrom<u32> for Chipset {
40             type Error = kernel::error::Error;
41 
42             fn try_from(value: u32) -> Result<Self, Self::Error> {
43                 match value {
44                     $( $value => Ok(Chipset::$variant), )*
45                     _ => Err(ENODEV),
46                 }
47             }
48         }
49     }
50 }
51 
52 define_chipset!({
53     // Turing
54     TU102 = 0x162,
55     TU104 = 0x164,
56     TU106 = 0x166,
57     TU117 = 0x167,
58     TU116 = 0x168,
59     // Ampere
60     GA100 = 0x170,
61     GA102 = 0x172,
62     GA103 = 0x173,
63     GA104 = 0x174,
64     GA106 = 0x176,
65     GA107 = 0x177,
66     // Ada
67     AD102 = 0x192,
68     AD103 = 0x193,
69     AD104 = 0x194,
70     AD106 = 0x196,
71     AD107 = 0x197,
72 });
73 
74 impl Chipset {
75     pub(crate) fn arch(&self) -> Architecture {
76         match self {
77             Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
78                 Architecture::Turing
79             }
80             Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
81                 Architecture::Ampere
82             }
83             Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
84                 Architecture::Ada
85             }
86         }
87     }
88 }
89 
90 // TODO
91 //
92 // The resulting strings are used to generate firmware paths, hence the
93 // generated strings have to be stable.
94 //
95 // Hence, replace with something like strum_macros derive(Display).
96 //
97 // For now, redirect to fmt::Debug for convenience.
98 impl fmt::Display for Chipset {
99     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100         write!(f, "{self:?}")
101     }
102 }
103 
104 /// Enum representation of the GPU generation.
105 #[derive(fmt::Debug)]
106 pub(crate) enum Architecture {
107     Turing = 0x16,
108     Ampere = 0x17,
109     Ada = 0x19,
110 }
111 
112 impl TryFrom<u8> for Architecture {
113     type Error = Error;
114 
115     fn try_from(value: u8) -> Result<Self> {
116         match value {
117             0x16 => Ok(Self::Turing),
118             0x17 => Ok(Self::Ampere),
119             0x19 => Ok(Self::Ada),
120             _ => Err(ENODEV),
121         }
122     }
123 }
124 
125 pub(crate) struct Revision {
126     major: u8,
127     minor: u8,
128 }
129 
130 impl Revision {
131     fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
132         Self {
133             major: boot0.major_revision(),
134             minor: boot0.minor_revision(),
135         }
136     }
137 }
138 
139 impl fmt::Display for Revision {
140     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141         write!(f, "{:x}.{:x}", self.major, self.minor)
142     }
143 }
144 
145 /// Structure holding the metadata of the GPU.
146 pub(crate) struct Spec {
147     chipset: Chipset,
148     /// The revision of the chipset.
149     revision: Revision,
150 }
151 
152 impl Spec {
153     fn new(bar: &Bar0) -> Result<Spec> {
154         let boot0 = regs::NV_PMC_BOOT_0::read(bar);
155 
156         Ok(Self {
157             chipset: boot0.chipset()?,
158             revision: Revision::from_boot0(boot0),
159         })
160     }
161 }
162 
163 /// Structure holding the resources required to operate the GPU.
164 #[pin_data]
165 pub(crate) struct Gpu {
166     spec: Spec,
167     /// MMIO mapping of PCI BAR 0
168     bar: Arc<Devres<Bar0>>,
169     fw: Firmware,
170     /// System memory page required for flushing all pending GPU-side memory writes done through
171     /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
172     sysmem_flush: SysmemFlush,
173     /// GSP falcon instance, used for GSP boot up and cleanup.
174     gsp_falcon: Falcon<GspFalcon>,
175     /// SEC2 falcon instance, used for GSP boot up and cleanup.
176     sec2_falcon: Falcon<Sec2Falcon>,
177     /// GSP runtime data. Temporarily an empty placeholder.
178     #[pin]
179     gsp: Gsp,
180 }
181 
182 impl Gpu {
183     pub(crate) fn new<'a>(
184         pdev: &'a pci::Device<device::Bound>,
185         devres_bar: Arc<Devres<Bar0>>,
186         bar: &'a Bar0,
187     ) -> impl PinInit<Self, Error> + 'a {
188         try_pin_init!(Self {
189             spec: Spec::new(bar).inspect(|spec| {
190                 dev_info!(
191                     pdev.as_ref(),
192                     "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
193                     spec.chipset,
194                     spec.chipset.arch(),
195                     spec.revision
196                 );
197             })?,
198 
199             // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
200             _: {
201                 gfw::wait_gfw_boot_completion(bar)
202                     .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
203             },
204 
205             fw <- Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?,
206 
207             sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
208 
209             gsp_falcon: Falcon::new(
210                 pdev.as_ref(),
211                 spec.chipset,
212                 bar,
213                 spec.chipset > Chipset::GA100,
214             )
215             .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
216 
217             sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar, true)?,
218 
219             gsp <- Gsp::new(),
220 
221             _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
222 
223             bar: devres_bar,
224         })
225     }
226 
227     /// Called when the corresponding [`Device`](device::Device) is unbound.
228     ///
229     /// Note: This method must only be called from `Driver::unbind`.
230     pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
231         kernel::warn_on!(self
232             .bar
233             .access(dev)
234             .inspect(|bar| self.sysmem_flush.unregister(bar))
235             .is_err());
236     }
237 }
238