Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/gpu.rs
26444 views
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, sec2::Sec2, Falcon};
7
use crate::fb::FbLayout;
8
use crate::fb::SysmemFlush;
9
use crate::firmware::fwsec::{FwsecCommand, FwsecFirmware};
10
use crate::firmware::{Firmware, FIRMWARE_VERSION};
11
use crate::gfw;
12
use crate::regs;
13
use crate::util;
14
use crate::vbios::Vbios;
15
use core::fmt;
16
17
macro_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
54
define_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
76
impl 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.
100
impl 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)]
108
pub(crate) enum Architecture {
109
Turing = 0x16,
110
Ampere = 0x17,
111
Ada = 0x19,
112
}
113
114
impl 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
127
pub(crate) struct Revision {
128
major: u8,
129
minor: u8,
130
}
131
132
impl 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
141
impl 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.
148
pub(crate) struct Spec {
149
chipset: Chipset,
150
/// The revision of the chipset.
151
revision: Revision,
152
}
153
154
impl 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)]
167
pub(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]
178
impl 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
186
impl 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
}
313
314