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