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