[PATCH v13 1/9] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations
From: Alexandre Courbot
Date: Wed Jun 03 2026 - 03:42:37 EST
From: John Hubbard <jhubbard@xxxxxxxxxx>
Add external memory (EMEM) read/write operations to the GPU's FSP falcon
engine. These operations use Falcon PIO (Programmed I/O) to communicate
with the FSP through indirect memory access.
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
Signed-off-by: Alexandre Courbot <acourbot@xxxxxxxxxx>
---
drivers/gpu/nova-core/falcon/fsp.rs | 80 ++++++++++++++++++++++++++++++++++---
drivers/gpu/nova-core/regs.rs | 18 +++++++++
2 files changed, 93 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
index d9f87262e8b1..0956a75ef7aa 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -6,12 +6,26 @@
//! The FSP falcon handles secure boot and Chain of Trust operations
//! on Hopper and Blackwell architectures, replacing SEC2's role.
-use kernel::io::register::RegisterBase;
+use kernel::{
+ io::{
+ register::{
+ RegisterBase,
+ WithBase, //
+ },
+ Io, //
+ },
+ prelude::*,
+};
-use crate::falcon::{
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
+use crate::{
+ driver::Bar0,
+ falcon::{
+ Falcon,
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
+ },
+ regs,
};
/// Type specifying the `Fsp` falcon engine. Cannot be instantiated.
@@ -26,3 +40,59 @@ impl RegisterBase<PFalcon2Base> for Fsp {
}
impl FalconEngine for Fsp {}
+
+impl Falcon<Fsp> {
+ /// Writes `data` to FSP external memory at offset `0`.
+ ///
+ /// `data` is interpreted as little-endian 32-bit words. Returns `EINVAL`
+ /// if the `data` length is not 4-byte aligned.
+ #[expect(dead_code)]
+ fn write_emem(&mut self, bar: &Bar0, data: &[u8]) -> Result {
+ if data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ // Begin a write burst at offset `0`, auto-incrementing on each write.
+ bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincw(true),
+ );
+
+ for chunk in data.chunks_exact(4) {
+ let value = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
+
+ // Write the next 32-bit `value`; hardware advances the offset.
+ bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEMD::zeroed().with_data(value),
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Reads FSP external memory from offset `0` into `data`.
+ ///
+ /// `data` is stored as little-endian 32-bit words. Returns `EINVAL` if
+ /// the `data` length is not 4-byte aligned.
+ #[expect(dead_code)]
+ fn read_emem(&mut self, bar: &Bar0, data: &mut [u8]) -> Result {
+ if data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ // Begin a read burst at offset `0`, auto-incrementing on each read.
+ bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEMC::zeroed().with_aincr(true),
+ );
+
+ for chunk in data.chunks_exact_mut(4) {
+ // Read the next 32-bit word; hardware advances the offset.
+ let value = bar.read(regs::NV_PFALCON_FALCON_EMEMD::of::<Fsp>()).data();
+ chunk.copy_from_slice(&value.to_le_bytes());
+ }
+
+ Ok(())
+ }
+}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 2cb1f02f35a4..e602c7860459 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -475,6 +475,24 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
pub(crate) NV_PFALCON_FBIF_CTL(u32) @ PFalconBase + 0x00000624 {
7:7 allow_phys_no_ctx => bool;
}
+
+ // Falcon EMEM PIO registers (used by FSP on Hopper/Blackwell).
+ // These provide the falcon external memory communication interface.
+
+ pub(crate) NV_PFALCON_FALCON_EMEMC(u32) @ PFalconBase + 0x00000ac0 {
+ /// EMEM byte offset (4-byte aligned) within the block.
+ 7:2 offs;
+ /// EMEM block to access.
+ 15:8 blk;
+ /// Auto-increment the offset after each write.
+ 24:24 aincw => bool;
+ /// Auto-increment the offset after each read.
+ 25:25 aincr => bool;
+ }
+
+ pub(crate) NV_PFALCON_FALCON_EMEMD(u32) @ PFalconBase + 0x00000ac4 {
+ 31:0 data => u32;
+ }
}
impl NV_PFALCON_FALCON_DMACTL {
--
2.54.0