[PATCH v2 03/10] gpu: nova-core: convert PFB registers to kernel register macro

From: Alexandre Courbot

Date: Fri Mar 20 2026 - 08:20:51 EST


Convert all PFB registers to use the kernel's register macro and update
the code accordingly.

NV_PGSP_QUEUE_HEAD was somehow caught in the PFB section, so move it to
its own section and convert it as well.

Reviewed-by: Eliot Courtney <ecourtney@xxxxxxxxxx>
Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
---
drivers/gpu/nova-core/fb/hal/ga100.rs | 34 +++++++++++++--------
drivers/gpu/nova-core/fb/hal/tu102.rs | 14 +++++----
drivers/gpu/nova-core/gsp/boot.rs | 6 ++--
drivers/gpu/nova-core/gsp/cmdq.rs | 9 +++---
drivers/gpu/nova-core/regs.rs | 57 ++++++++++++++++++++---------------
5 files changed, 70 insertions(+), 50 deletions(-)

diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/fb/hal/ga100.rs
index e0acc41aa7cd..629588c75778 100644
--- a/drivers/gpu/nova-core/fb/hal/ga100.rs
+++ b/drivers/gpu/nova-core/fb/hal/ga100.rs
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: GPL-2.0

-use kernel::prelude::*;
+use kernel::{
+ io::Io,
+ num::Bounded,
+ prelude::*, //
+};

use crate::{
driver::Bar0,
@@ -13,22 +17,26 @@
struct Ga100;

pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 {
- u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT
- | u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::read(bar).adr_63_40())
+ u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT
+ | u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI).adr_63_40())
<< FLUSH_SYSMEM_ADDR_SHIFT_HI
}

pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) {
- regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default()
- // CAST: `as u32` is used on purpose since the remaining bits are guaranteed to fit within
- // a `u32`.
- .set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32)
- .write(bar);
- regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
- // CAST: `as u32` is used on purpose since we want to strip the upper bits that have been
- // written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`.
- .set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32)
- .write(bar);
+ bar.write_reg(
+ regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr_63_40(
+ Bounded::<u64, _>::from(addr)
+ .shr::<FLUSH_SYSMEM_ADDR_SHIFT_HI, _>()
+ .cast(),
+ ),
+ );
+
+ bar.write_reg(
+ regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::zeroed()
+ // CAST: `as u32` is used on purpose since we want to strip the upper bits that have
+ // been written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`.
+ .with_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32),
+ );
}

pub(super) fn display_enabled_ga100(bar: &Bar0) -> bool {
diff --git a/drivers/gpu/nova-core/fb/hal/tu102.rs b/drivers/gpu/nova-core/fb/hal/tu102.rs
index eec984f4e816..515d50872224 100644
--- a/drivers/gpu/nova-core/fb/hal/tu102.rs
+++ b/drivers/gpu/nova-core/fb/hal/tu102.rs
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0

-use kernel::prelude::*;
+use kernel::{
+ io::Io,
+ prelude::*, //
+};

use crate::{
driver::Bar0,
@@ -13,7 +16,7 @@
pub(super) const FLUSH_SYSMEM_ADDR_SHIFT: u32 = 8;

pub(super) fn read_sysmem_flush_page_gm107(bar: &Bar0) -> u64 {
- u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT
+ u64::from(bar.read(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT
}

pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result {
@@ -21,9 +24,7 @@ pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result {
u32::try_from(addr >> FLUSH_SYSMEM_ADDR_SHIFT)
.map_err(|_| EINVAL)
.map(|addr| {
- regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
- .set_adr_39_08(addr)
- .write(bar)
+ bar.write_reg(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::zeroed().with_adr_39_08(addr))
})
}

@@ -32,7 +33,8 @@ pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool {
}

pub(super) fn vidmem_size_gp102(bar: &Bar0) -> u64 {
- regs::NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE::read(bar).usable_fb_size()
+ bar.read(regs::NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE)
+ .usable_fb_size()
}

struct Tu102;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 06b8313c1219..55d8aef5ff1e 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -58,7 +58,7 @@ fn run_fwsec_frts(
) -> Result<()> {
// Check that the WPR2 region does not already exists - if it does, we cannot run
// FWSEC-FRTS until the GPU is reset.
- if regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound() != 0 {
+ if bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 {
dev_err!(
dev,
"WPR2 region already exists - GPU needs to be reset to proceed\n"
@@ -103,8 +103,8 @@ fn run_fwsec_frts(

// Check that the WPR2 region has been created as we requested.
let (wpr2_lo, wpr2_hi) = (
- regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO::read(bar).lower_bound(),
- regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI::read(bar).higher_bound(),
+ bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(),
+ bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(),
);

match (wpr2_lo, wpr2_hi) {
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index d36a62ba1c60..b8b0edba5afd 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -11,7 +11,10 @@
DmaAddress, //
},
dma_write,
- io::poll::read_poll_timeout,
+ io::{
+ poll::read_poll_timeout,
+ Io, //
+ },
new_mutex,
prelude::*,
sync::{
@@ -503,9 +506,7 @@ fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {

/// Notifies the GSP that we have updated the command queue pointers.
fn notify_gsp(bar: &Bar0) {
- regs::NV_PGSP_QUEUE_HEAD::default()
- .set_address(0)
- .write(bar);
+ bar.write_reg(regs::NV_PGSP_QUEUE_HEAD::zeroed().with_address(0u32));
}

/// Sends `command` to the GSP and waits for the reply.
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 533d912659ba..4f5cd64c2fce 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -120,26 +120,35 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {

// PFB

-// The following two registers together hold the physical system memory address that is used by the
-// GPU to perform sysmembar operations (see `fb::SysmemFlush`).
+io::register! {
+ /// Low bits of the physical system memory address used by the GPU to perform sysmembar
+ /// operations (see [`crate::fb::SysmemFlush`]).
+ pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR(u32) @ 0x00100c10 {
+ 31:0 adr_39_08;
+ }

-register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 {
- 31:0 adr_39_08 as u32;
-});
+ /// High bits of the physical system memory address used by the GPU to perform sysmembar
+ /// operations (see [`crate::fb::SysmemFlush`]).
+ pub(crate) NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x00100c40 {
+ 23:0 adr_63_40;
+ }

-register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 {
- 23:0 adr_63_40 as u32;
-});
+ pub(crate) NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE(u32) @ 0x00100ce0 {
+ 30:30 ecc_mode_enabled => bool;
+ 9:4 lower_mag;
+ 3:0 lower_scale;
+ }

-register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 {
- 3:0 lower_scale as u8;
- 9:4 lower_mag as u8;
- 30:30 ecc_mode_enabled as bool;
-});
+ pub(crate) NV_PFB_PRI_MMU_WPR2_ADDR_LO(u32) @ 0x001fa824 {
+ /// Bits 12..40 of the lower (inclusive) bound of the WPR2 region.
+ 31:4 lo_val;
+ }

-register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 {
- 31:0 address as u32;
-});
+ pub(crate) NV_PFB_PRI_MMU_WPR2_ADDR_HI(u32) @ 0x001fa828 {
+ /// Bits 12..40 of the higher (exclusive) bound of the WPR2 region.
+ 31:4 hi_val;
+ }
+}

impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE {
/// Returns the usable framebuffer size, in bytes.
@@ -156,10 +165,6 @@ pub(crate) fn usable_fb_size(self) -> u64 {
}
}

-register!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 {
- 31:4 lo_val as u32, "Bits 12..40 of the lower (inclusive) bound of the WPR2 region";
-});
-
impl NV_PFB_PRI_MMU_WPR2_ADDR_LO {
/// Returns the lower (inclusive) bound of the WPR2 region.
pub(crate) fn lower_bound(self) -> u64 {
@@ -167,10 +172,6 @@ pub(crate) fn lower_bound(self) -> u64 {
}
}

-register!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 {
- 31:4 hi_val as u32, "Bits 12..40 of the higher (exclusive) bound of the WPR2 region";
-});
-
impl NV_PFB_PRI_MMU_WPR2_ADDR_HI {
/// Returns the higher (exclusive) bound of the WPR2 region.
///
@@ -180,6 +181,14 @@ pub(crate) fn higher_bound(self) -> u64 {
}
}

+// PGSP
+
+io::register! {
+ pub(crate) NV_PGSP_QUEUE_HEAD(u32) @ 0x00110c00 {
+ 31:0 address;
+ }
+}
+
// PGC6 register space.
//
// `GC6` is a GPU low-power state where VRAM is in self-refresh and the GPU is powered down (except

--
2.53.0