Skip to content

Commit 6554ad6

Browse files
GnurouDanilo Krummrich
authored andcommitted
gpu: nova-core: register sysmem flush page
Reserve a page of system memory so sysmembar can perform a read on it if a system write occurred since the last flush. Do this early as it can be required to e.g. reset the GPU falcons. Chipsets capabilities differ in that respect, so this commit also introduces the FB HAL. Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Link: https://lore.kernel.org/r/20250619-nova-frts-v6-14-ecf41ef99252@nvidia.com [ * Use kernel::page::PAGE_SIZE instead of kernel::bindings::PAGE_SIZE. * Get rid of the Option for SysmemFlush. * Slightly reword SysmemFlush doc-comments. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 9bcc046 commit 6554ad6

7 files changed

Lines changed: 215 additions & 2 deletions

File tree

drivers/gpu/nova-core/fb.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::prelude::*;
4+
use kernel::types::ARef;
5+
use kernel::{dev_warn, device};
6+
7+
use crate::dma::DmaObject;
8+
use crate::driver::Bar0;
9+
use crate::gpu::Chipset;
10+
11+
mod hal;
12+
13+
/// Type holding the sysmem flush memory page, a page of memory to be written into the
14+
/// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.
15+
///
16+
/// Users are responsible for manually calling [`Self::unregister`] before dropping this object,
17+
/// otherwise the GPU might still use it even after it has been freed.
18+
pub(crate) struct SysmemFlush {
19+
/// Chipset we are operating on.
20+
chipset: Chipset,
21+
device: ARef<device::Device>,
22+
/// Keep the page alive as long as we need it.
23+
page: DmaObject,
24+
}
25+
26+
impl SysmemFlush {
27+
/// Allocate a memory page and register it as the sysmem flush page.
28+
pub(crate) fn register(
29+
dev: &device::Device<device::Bound>,
30+
bar: &Bar0,
31+
chipset: Chipset,
32+
) -> Result<Self> {
33+
let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?;
34+
35+
hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?;
36+
37+
Ok(Self {
38+
chipset,
39+
device: dev.into(),
40+
page,
41+
})
42+
}
43+
44+
/// Unregister the managed sysmem flush page.
45+
///
46+
/// In order to gracefully tear down the GPU, users must make sure to call this method before
47+
/// dropping the object.
48+
pub(crate) fn unregister(&self, bar: &Bar0) {
49+
let hal = hal::fb_hal(self.chipset);
50+
51+
if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
52+
let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
53+
dev_warn!(
54+
&self.device,
55+
"failed to unregister sysmem flush page: {:?}",
56+
e
57+
)
58+
});
59+
} else {
60+
// Another page has been registered after us for some reason - warn as this is a bug.
61+
dev_warn!(
62+
&self.device,
63+
"attempt to unregister a sysmem flush page that is not active\n"
64+
);
65+
}
66+
}
67+
}

drivers/gpu/nova-core/fb/hal.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use kernel::prelude::*;
4+
5+
use crate::driver::Bar0;
6+
use crate::gpu::Chipset;
7+
8+
mod ga100;
9+
mod tu102;
10+
11+
pub(crate) trait FbHal {
12+
/// Returns the address of the currently-registered sysmem flush page.
13+
fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64;
14+
15+
/// Register `addr` as the address of the sysmem flush page.
16+
///
17+
/// This might fail if the address is too large for the receiving register.
18+
fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result;
19+
}
20+
21+
/// Returns the HAL corresponding to `chipset`.
22+
pub(super) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
23+
use Chipset::*;
24+
25+
match chipset {
26+
TU102 | TU104 | TU106 | TU117 | TU116 => tu102::TU102_HAL,
27+
GA100 | GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => {
28+
ga100::GA100_HAL
29+
}
30+
}
31+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
struct Ga100;
4+
5+
use kernel::prelude::*;
6+
7+
use crate::driver::Bar0;
8+
use crate::fb::hal::FbHal;
9+
use crate::regs;
10+
11+
use super::tu102::FLUSH_SYSMEM_ADDR_SHIFT;
12+
13+
pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 {
14+
(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08() as u64) << FLUSH_SYSMEM_ADDR_SHIFT
15+
| (regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::read(bar).adr_63_40() as u64)
16+
<< FLUSH_SYSMEM_ADDR_SHIFT_HI
17+
}
18+
19+
pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) {
20+
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default()
21+
.set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32)
22+
.write(bar);
23+
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
24+
.set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32)
25+
.write(bar);
26+
}
27+
28+
/// Shift applied to the sysmem address before it is written into
29+
/// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`,
30+
const FLUSH_SYSMEM_ADDR_SHIFT_HI: u32 = 40;
31+
32+
impl FbHal for Ga100 {
33+
fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
34+
read_sysmem_flush_page_ga100(bar)
35+
}
36+
37+
fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
38+
write_sysmem_flush_page_ga100(bar, addr);
39+
40+
Ok(())
41+
}
42+
}
43+
44+
const GA100: Ga100 = Ga100;
45+
pub(super) const GA100_HAL: &dyn FbHal = &GA100;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use crate::driver::Bar0;
4+
use crate::fb::hal::FbHal;
5+
use crate::regs;
6+
use kernel::prelude::*;
7+
8+
/// Shift applied to the sysmem address before it is written into `NV_PFB_NISO_FLUSH_SYSMEM_ADDR`,
9+
/// to be used by HALs.
10+
pub(super) const FLUSH_SYSMEM_ADDR_SHIFT: u32 = 8;
11+
12+
pub(super) fn read_sysmem_flush_page_gm107(bar: &Bar0) -> u64 {
13+
(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08() as u64) << FLUSH_SYSMEM_ADDR_SHIFT
14+
}
15+
16+
pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result {
17+
// Check that the address doesn't overflow the receiving 32-bit register.
18+
if addr >> (u32::BITS + FLUSH_SYSMEM_ADDR_SHIFT) == 0 {
19+
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
20+
.set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32)
21+
.write(bar);
22+
23+
Ok(())
24+
} else {
25+
Err(EINVAL)
26+
}
27+
}
28+
29+
struct Tu102;
30+
31+
impl FbHal for Tu102 {
32+
fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
33+
read_sysmem_flush_page_gm107(bar)
34+
}
35+
36+
fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
37+
write_sysmem_flush_page_gm107(bar, addr)
38+
}
39+
}
40+
41+
const TU102: Tu102 = Tu102;
42+
pub(super) const TU102_HAL: &dyn FbHal = &TU102;

drivers/gpu/nova-core/gpu.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use kernel::{device, devres::Devres, error::code::*, pci, prelude::*};
44

55
use crate::driver::Bar0;
6+
use crate::fb::SysmemFlush;
67
use crate::firmware::{Firmware, FIRMWARE_VERSION};
78
use crate::gfw;
89
use crate::regs;
@@ -158,12 +159,24 @@ impl Spec {
158159
}
159160

160161
/// Structure holding the resources required to operate the GPU.
161-
#[pin_data]
162+
#[pin_data(PinnedDrop)]
162163
pub(crate) struct Gpu {
163164
spec: Spec,
164165
/// MMIO mapping of PCI BAR 0
165166
bar: Devres<Bar0>,
166167
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+
}
167180
}
168181

169182
impl Gpu {
@@ -187,10 +200,14 @@ impl Gpu {
187200
gfw::wait_gfw_boot_completion(bar)
188201
.inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
189202

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+
190206
Ok(pin_init!(Self {
191207
spec,
192208
bar: devres_bar,
193-
fw
209+
fw,
210+
sysmem_flush,
194211
}))
195212
}
196213
}

drivers/gpu/nova-core/nova_core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
mod dma;
66
mod driver;
7+
mod fb;
78
mod firmware;
89
mod gfw;
910
mod gpu;

drivers/gpu/nova-core/regs.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ impl NV_PMC_BOOT_0 {
3838
}
3939
}
4040

41+
/* PFB */
42+
43+
register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 {
44+
31:0 adr_39_08 as u32;
45+
});
46+
47+
register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 {
48+
23:0 adr_63_40 as u32;
49+
});
50+
4151
/* PGC6 */
4252

4353
register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128 {

0 commit comments

Comments
 (0)