Commit 09880996 authored by Alexandre Courbot's avatar Alexandre Courbot
Browse files

gpu: nova-core: register: add support for relative array registers



Add support for declaring arrays of registers available from a variable
base. This is effectively a combination of the relative and array
registers features.

nova-core does not make much use of this yet, but it will become helpful
to have for GSP boot.

Reviewed-by: default avatarLyude Paul <lyude@redhat.com>
Link: https://lore.kernel.org/r/20250718-nova-regs-v2-19-7b6a762aa1cd@nvidia.com


Signed-off-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
parent ec2f6c81
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -131,7 +131,6 @@ crate so it can be used by other components as well.

Features desired before this happens:

* Arrays of registers with build-time index validation,
* Make I/O optional I/O (for field values that are not registers),
* Support other sizes than `u32`,
* Allow visibility control for registers and individual fields,
+1 −1
Original line number Diff line number Diff line
@@ -516,7 +516,7 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
    pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
        regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
        regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
        regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, |v| {
        regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| {
            v.set_target(FalconFbifTarget::CoherentSysmem)
                .set_mem_type(FalconFbifMemType::Physical)
        });
+1 −1
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ fn signature_reg_fuse_version_ga102(
fn program_brom_ga102<E: FalconEngine>(bar: &Bar0, params: &FalconBromParams) -> Result {
    regs::NV_PFALCON2_FALCON_BROM_PARAADDR::default()
        .set_value(params.pkc_data_offset)
        .write(bar, &E::ID);
        .write(bar, &E::ID, 0);
    regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::default()
        .set_value(u32::from(params.engine_id_mask))
        .write(bar, &E::ID);
+4 −4
Original line number Diff line number Diff line
@@ -289,8 +289,7 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
    0:0     reset as bool;
});

// TODO[REGA]: this is an array of registers.
register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600] {
register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] {
    1:0     target as u8 ?=> FalconFbifTarget;
    2:2     mem_type as bool => FalconFbifMemType;
});
@@ -313,8 +312,9 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
    31:0    value as u32;
});

// TODO[REGA]: this is an array of registers.
register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210] {
// OpenRM defines this as a register array, but doesn't specify its size and only uses its first
// element. Be conservative until we know the actual size or need to use more registers.
register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] {
    31:0    value as u32;
});

+242 −0
Original line number Diff line number Diff line
@@ -213,6 +213,74 @@ pub(crate) trait RegisterBase<T> {
/// # Ok(())
/// # }
/// ```
///
/// ## Relative arrays of registers
///
/// Combining the two features described in the sections above, arrays of registers accessible from
/// a base can also be defined:
///
/// ```no_run
/// # fn no_run() -> Result<(), Error> {
/// # fn get_scratch_idx() -> usize {
/// #   0x15
/// # }
/// // Type used as parameter of `RegisterBase` to specify the base.
/// pub(crate) struct CpuCtlBase;
///
/// // ZST describing `CPU0`.
/// struct Cpu0;
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
///     const BASE: usize = 0x100;
/// }
/// // Singleton of `CPU0` used to identify it.
/// const CPU0: Cpu0 = Cpu0;
///
/// // ZST describing `CPU1`.
/// struct Cpu1;
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
///     const BASE: usize = 0x200;
/// }
/// // Singleton of `CPU1` used to identify it.
/// const CPU1: Cpu1 = Cpu1;
///
/// // 64 per-cpu scratch registers, arranged as an contiguous array.
/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {
///     31:0    value as u32;
/// });
///
/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value();
/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value();
///
/// // This won't build.
/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value();
///
/// // Runtime-obtained array index.
/// let scratch_idx = get_scratch_idx();
/// // Access on a runtime value returns an error if it is out-of-bounds.
/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value();
///
/// // `SCRATCH[8]` is used to convey the firmware exit code.
/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]],
///     "Per-CPU firmware exit status code" {
///     7:0     status as u8;
/// });
///
/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status();
///
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
/// // registers of the two declarations below are interleaved.
/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]],
///           "Scratch registers bank 0" {
///     31:0    value as u32;
/// });
/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]],
///           "Scratch registers bank 1" {
///     31:0    value as u32;
/// });
/// # Ok(())
/// # }
/// ```
macro_rules! register {
    // Creates a register at a fixed offset of the MMIO space.
    ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
@@ -260,7 +328,41 @@ macro_rules! register {
        } );
    };

    // Creates an array of registers at a relative offset from a base address provider.
    (
        $name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]
            $(, $comment:literal)? { $($fields:tt)* }
    ) => {
        static_assert!(::core::mem::size_of::<u32>() <= $stride);
        register!(@core $name $(, $comment)? { $($fields)* } );
        register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
    };

    // Shortcut for contiguous array of relative registers (stride == size of element).
    (
        $name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? {
            $($fields:tt)*
        }
    ) => {
        register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ]
            $(, $comment)? { $($fields)* } );
    };

    // Creates an alias of register `idx` of relative array of registers `alias` with its own
    // fields.
    (
        $name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? {
            $($fields:tt)*
        }
    ) => {
        static_assert!($idx < $alias::SIZE);
        register!(@core $name $(, $comment)? { $($fields)* } );
        register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
    };

    // Creates an alias of register `idx` of array of registers `alias` with its own fields.
    // This rule belongs to the (non-relative) register arrays set, but needs to be put last
    // to avoid it being interpreted in place of the relative register array alias rule.
    ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
        static_assert!($idx < $alias::SIZE);
        register!(@core $name $(, $comment)? { $($fields)* } );
@@ -715,4 +817,144 @@ pub(crate) fn try_alter<const SIZE: usize, T, F>(
            }
        }
    };

    // Generates the IO accessors for an array of relative registers.
    (
        @io_relative_array $name:ident @ $base:ty
            [ $offset:literal [ $size:expr ; $stride:expr ] ]
    ) => {
        #[allow(dead_code)]
        impl $name {
            pub(crate) const OFFSET: usize = $offset;
            pub(crate) const SIZE: usize = $size;
            pub(crate) const STRIDE: usize = $stride;

            /// Read the array register at index `idx` from `io`, using the base address provided
            /// by `base` and adding the register's offset to it.
            #[inline(always)]
            pub(crate) fn read<const SIZE: usize, T, B>(
                io: &T,
                #[allow(unused_variables)]
                base: &B,
                idx: usize,
            ) -> Self where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
            {
                build_assert!(idx < Self::SIZE);

                let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
                    Self::OFFSET + (idx * Self::STRIDE);
                let value = io.read32(offset);

                Self(value)
            }

            /// Write the value contained in `self` to `io`, using the base address provided by
            /// `base` and adding the offset of array register `idx` to it.
            #[inline(always)]
            pub(crate) fn write<const SIZE: usize, T, B>(
                self,
                io: &T,
                #[allow(unused_variables)]
                base: &B,
                idx: usize
            ) where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
            {
                build_assert!(idx < Self::SIZE);

                let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
                    Self::OFFSET + (idx * Self::STRIDE);

                io.write32(self.0, offset);
            }

            /// Read the array register at index `idx` from `io`, using the base address provided
            /// by `base` and adding the register's offset to it, then run `f` on its value to
            /// obtain a new value to write back.
            #[inline(always)]
            pub(crate) fn alter<const SIZE: usize, T, B, F>(
                io: &T,
                base: &B,
                idx: usize,
                f: F,
            ) where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
                F: ::core::ops::FnOnce(Self) -> Self,
            {
                let reg = f(Self::read(io, base, idx));
                reg.write(io, base, idx);
            }

            /// Read the array register at index `idx` from `io`, using the base address provided
            /// by `base` and adding the register's offset to it.
            ///
            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
            /// access was out-of-bounds.
            #[inline(always)]
            pub(crate) fn try_read<const SIZE: usize, T, B>(
                io: &T,
                base: &B,
                idx: usize,
            ) -> ::kernel::error::Result<Self> where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
            {
                if idx < Self::SIZE {
                    Ok(Self::read(io, base, idx))
                } else {
                    Err(EINVAL)
                }
            }

            /// Write the value contained in `self` to `io`, using the base address provided by
            /// `base` and adding the offset of array register `idx` to it.
            ///
            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
            /// access was out-of-bounds.
            #[inline(always)]
            pub(crate) fn try_write<const SIZE: usize, T, B>(
                self,
                io: &T,
                base: &B,
                idx: usize,
            ) -> ::kernel::error::Result where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
            {
                if idx < Self::SIZE {
                    Ok(self.write(io, base, idx))
                } else {
                    Err(EINVAL)
                }
            }

            /// Read the array register at index `idx` from `io`, using the base address provided
            /// by `base` and adding the register's offset to it, then run `f` on its value to
            /// obtain a new value to write back.
            ///
            /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
            /// access was out-of-bounds.
            #[inline(always)]
            pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
                io: &T,
                base: &B,
                idx: usize,
                f: F,
            ) -> ::kernel::error::Result where
                T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
                B: crate::regs::macros::RegisterBase<$base>,
                F: ::core::ops::FnOnce(Self) -> Self,
            {
                if idx < Self::SIZE {
                    Ok(Self::alter(io, base, idx, f))
                } else {
                    Err(EINVAL)
                }
            }
        }
    };
}