Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 00/18] gpu: nova-core: register!() macro improvements
This patch series introduces a number of improvements to nova-core's
register!() macro in order to make it more useful to Nova itself, and to
bring it closer to graduation into the wider kernel crate.
The first half is trivial fixes and code reorganization to let the
following patches apply more cleanly.
The interesting stuff begins with the introduction of proper `Debug` and
`Default` implementations leveraging the field information that is made
available by the first half of the patchset. `Debug` now displays the
interpreted values of all the fields on top of the hexadecimal
representation of the register; and `Default` now initializes all the
fields to their declared default value instead of just zeroes.
Then goes a complete redesign of the way relative registers work. The
previous way was very unsafe as it accepted any literal value as the
base. Now, valid bases can (and must) be explicitly defined for specific
group of relative registers. All these bases are belong to us, and thus
can be validated at build-time.
Next come arrays of registers, a useful feature to represent contiguous
groups of registers that are interpreted identically. For these we have
both build-time and runtime checked accessors. We immediately make use
of them to clean up the FUSE registers code, which was a bit unsightly
due to the lack of this feature.
Finally, combining the two features: arrays of relative registers, which
we don't really need at the moment, but will become needed for GSP
booting.
There are still features that need to be implemented before this macro
can be considered ready for other drivers:
- Make I/O accessors optional,
- Support other sizes than `u32`,
- Allow visibility control for registers and individual fields,
- Convert the range syntax to inclusive slices instead of NVIDIA's
OpenRM format,
- ... and proper suitability assessment by other driver contributors.
These should be trivial compared to the work that is done in this
series.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
Alexandre Courbot (17):
gpu: nova-core: register: fix typo
gpu: nova-core: register: fix alias documentation
gpu: nova-core: register: simplify @leaf_accessor rule
gpu: nova-core: register: remove `try_` accessors for relative registers
gpu: nova-core: register: move OFFSET declaration to I/O impl block
gpu: nova-core: register: fix documentation and indentation
gpu: nova-core: register: add missing doccomments for fixed registers I/O
accessors
gpu: nova-core: register: add fields dispatcher internal rule
gpu: nova-core: register: improve `Debug` implementation
gpu: nova-core: register: generate correct `Default` implementation
gpu: nova-core: register: split @io rule into fixed and relative versions
gpu: nova-core: register: use #[inline(always)] for all methods
gpu: nova-core: register: redesign relative registers
gpu: nova-core: falcon: add distinct base address for PFALCON2
gpu: nova-core: register: add support for register arrays
gpu: nova-core: falcon: use register arrays for FUSE registers
gpu: nova-core: registers: add support for relative array registers
John Hubbard (1):
gpu: nova-core: register: minor grammar and spelling fixes
Documentation/gpu/nova/core/todo.rst | 2 -
drivers/gpu/nova-core/falcon.rs | 72 +--
drivers/gpu/nova-core/falcon/gsp.rs | 16 +-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 47 +-
drivers/gpu/nova-core/falcon/sec2.rs | 13 +-
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/regs.rs | 79 ++--
drivers/gpu/nova-core/regs/macros.rs | 736 +++++++++++++++++++++++++-----
8 files changed, 749 insertions(+), 218 deletions(-)
---
base-commit: 4092e1b41202ff39aad75a40a03ac1d318443670
change-id: 20250703-nova-regs-24dddef5fba3
Best regards,
--
Alexandre Courbot <acourbot at nvidia.com>
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 01/18] gpu: nova-core: register: minor grammar and spelling fixes
From: John Hubbard <jhubbard at nvidia.com> There is only one top-level macro in this file at the moment, but the "macros.rs" file name allows for more. Change the wording so that it will remain valid even if additional macros are added to the file. Fix a couple of spelling errors and grammatical errors, and break up a run-on sentence, for clarity. Cc: Alexandre Courbot <acourbot at nvidia.com> Cc: Danilo Krummrich <dakr at kernel.org> Signed-off-by: John Hubbard <jhubbard at nvidia.com> Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drivers/gpu/nova-core/regs/macros.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs index cdf668073480ed703c89ffa8628f5c9de6494687..864d1e83bed2979f5661e038f4c9fd87d33f69a7 100644 --- a/drivers/gpu/nova-core/regs/macros.rs +++ b/drivers/gpu/nova-core/regs/macros.rs @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 -//! Macro to define register layout and accessors. +//! `register!` macro to define register layout and accessors. //! //! A single register typically includes several fields, which are accessed through a combination //! of bit-shift and mask operations that introduce a class of potential mistakes, notably because //! not all possible field values are necessarily valid. //! -//! The macro in this module allow to define, using an intruitive and readable syntax, a dedicated -//! type for each register with its own field accessors that can return an error is a field's value -//! is invalid. +//! The `register!` macro in this module provides an intuitive and readable syntax for defining a +//! dedicated type for each register. Each such type comes with its own field accessors that can +//! return an error if a field's value is invalid. -/// Defines a dedicated type for a register with an absolute offset, alongside with getter and -/// setter methods for its fields and methods to read and write it from an `Io` region. +/// Defines a dedicated type for a register with an absolute offset, including getter and setter +/// methods for its fields and methods to read and write it from an `Io` region. /// /// Example: /// @@ -24,7 +24,7 @@ /// ``` /// /// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io` -/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 less +/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least /// significant bits of the register. Each field can be accessed and modified using accessor /// methods: /// -- 2.50.0
A space was missing between arguments in this invocation.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
864d1e83bed2979f5661e038f4c9fd87d33f69a7..93e9055d5ebd5f78ea534aafd44d884da0fce345
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -116,7 +116,7 @@ macro_rules! register {
) => {
register!(@common $name @ $offset $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
- register!(@io$name @ + $offset);
+ register!(@io $name @ + $offset);
};
// Creates a alias register of relative offset register `alias` with its
own fields.
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 03/18] gpu: nova-core: register: fix alias documentation
The provided example had its matching closing parentheses missing. Also
simplify the example itself and reword the explanation a bit.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
93e9055d5ebd5f78ea534aafd44d884da0fce345..8a2001c07654cd6516b09be330e7a595695b365b
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -76,15 +76,17 @@
/// for cases where a register's interpretation depends on the context:
///
/// ```no_run
-/// register!(SCRATCH_0 @ 0x0000100, "Scratch register 0" {
+/// register!(SCRATCH @ 0x0000200, "Scratch register" {
/// 31:0 value as u32, "Raw value";
+/// });
///
-/// register!(SCRATCH_0_BOOT_STATUS => SCRATCH_0, "Boot status of the
firmware" {
+/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the
firmware" {
/// 0:0 completed as bool, "Whether the firmware has completed
booting";
+/// });
/// ```
///
/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as
`SCRATCH_0`, while also
-/// providing its own `completed` method.
+/// providing its own `completed` field.
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
(
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 04/18] gpu: nova-core: register: simplify @leaf_accessor rule
The `$type` metavariable is not used in the @leaf_accessor rule, so
remove it.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
8a2001c07654cd6516b09be330e7a595695b365b..7d5cd45d3e787301acab0b4d8ce1002c2dd2cab2
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -230,7 +230,7 @@ impl $name {
$(, $comment:literal)?;
) => {
register!(
- @leaf_accessor $name $hi:$lo $field as bool
+ @leaf_accessor $name $hi:$lo $field
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
$into_type => $into_type $(, $comment)?;
);
@@ -248,7 +248,7 @@ impl $name {
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
?=> $try_into_type:ty
$(, $comment:literal)?;
) => {
- register!(@leaf_accessor $name $hi:$lo $field as $type
+ register!(@leaf_accessor $name $hi:$lo $field
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type
=>
::core::result::Result<
$try_into_type,
@@ -262,7 +262,7 @@ impl $name {
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
=> $into_type:ty
$(, $comment:literal)?;
) => {
- register!(@leaf_accessor $name $hi:$lo $field as $type
+ register!(@leaf_accessor $name $hi:$lo $field
{ |f| <$into_type>::from(f as $type) } $into_type =>
$into_type $(, $comment)?;);
};
@@ -276,7 +276,7 @@ impl $name {
// Generates the accessor methods for a single field.
(
- @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
+ @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
{ $process:expr } $to_type:ty => $res_type:ty $(,
$comment:literal)?;
) => {
::kernel::macros::paste!(
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 05/18] gpu: nova-core: register: remove `try_` accessors for relative registers
Relative registers are always accessed using a literal base, meaning
their validity can always be checked at compile-time. Thus remove the
`try_` accessors that have no purpose.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 38 +-----------------------------------
1 file changed, 1 insertion(+), 37 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
7d5cd45d3e787301acab0b4d8ce1002c2dd2cab2..087f92f78788a013702cbc0a6e156e40f7695347
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -57,9 +57,7 @@
/// definition, or the field getter and setter methods they are attached to.
///
/// Putting a `+` before the address of the register makes it relative to a
base: the `read` and
-/// `write` methods take a `base` argument that is added to the specified
address before access,
-/// and `try_read` and `try_write` methods are also created, allowing access
with offsets unknown
-/// at compile-time:
+/// `write` methods take a `base` argument that is added to the specified
address before access:
///
/// ```no_run
/// register!(CPU_CTL @ +0x0000010, "CPU core control" {
@@ -386,40 +384,6 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
let reg = f(Self::read(io, base));
reg.write(io, base);
}
-
- #[inline]
- pub(crate) fn try_read<const SIZE: usize, T>(
- io: &T,
- base: usize,
- ) -> ::kernel::error::Result<Self> where
- T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
- {
- io.try_read32(base + $offset).map(Self)
- }
-
- #[inline]
- pub(crate) fn try_write<const SIZE: usize, T>(
- self,
- io: &T,
- base: usize,
- ) -> ::kernel::error::Result<()> where
- T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
- {
- io.try_write32(self.0, base + $offset)
- }
-
- #[inline]
- pub(crate) fn try_alter<const SIZE: usize, T, F>(
- io: &T,
- base: usize,
- f: F,
- ) -> ::kernel::error::Result<()> where
- T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
- F: ::core::ops::FnOnce(Self) -> Self,
- {
- let reg = f(Self::try_read(io, base)?);
- reg.try_write(io, base)
- }
}
};
}
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 06/18] gpu: nova-core: register: move OFFSET declaration to I/O impl block
The OFFSET const is an I/O property, and having to pass it to the
@common rule makes it impossible to make I/O optional, as we want to get
to eventually.
Thus, move OFFSET to the I/O impl block so it is not needed by the
@common rule anymore.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
087f92f78788a013702cbc0a6e156e40f7695347..b096a3689483fa79dc22f16f4baac2f9a9495c28
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -92,7 +92,7 @@ macro_rules! register {
$($fields:tt)*
}
) => {
- register!(@common $name @ $offset $(, $comment)?);
+ register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ $offset);
};
@@ -103,7 +103,7 @@ macro_rules! register {
$($fields:tt)*
}
) => {
- register!(@common $name @ $alias::OFFSET $(, $comment)?);
+ register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ $alias::OFFSET);
};
@@ -114,7 +114,7 @@ macro_rules! register {
$($fields:tt)*
}
) => {
- register!(@common $name @ $offset $(, $comment)?);
+ register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ + $offset);
};
@@ -125,7 +125,7 @@ macro_rules! register {
$($fields:tt)*
}
) => {
- register!(@common $name @ $alias::OFFSET $(, $comment)?);
+ register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ + $alias::OFFSET);
};
@@ -134,7 +134,7 @@ macro_rules! register {
// Defines the wrapper `$name` type, as well as its relevant
implementations (`Debug`, `BitOr`,
// and conversion to regular `u32`).
- (@common $name:ident @ $offset:expr $(, $comment:literal)?) => {
+ (@common $name:ident $(, $comment:literal)?) => {
$(
#[doc=$comment]
)?
@@ -142,11 +142,6 @@ macro_rules! register {
#[derive(Clone, Copy, Default)]
pub(crate) struct $name(u32);
- #[allow(dead_code)]
- impl $name {
- pub(crate) const OFFSET: usize = $offset;
- }
-
// TODO[REGA]: display the raw hex value, then the value of all the
fields. This requires
// matching the fields, which will complexify the syntax
considerably...
impl ::core::fmt::Debug for $name {
@@ -319,6 +314,8 @@ pub(crate) fn [<set_ $field>](mut self, value:
$to_type) -> Self {
(@io $name:ident @ $offset:expr) => {
#[allow(dead_code)]
impl $name {
+ pub(crate) const OFFSET: usize = $offset;
+
#[inline]
pub(crate) fn read<const SIZE: usize, T>(io: &T) ->
Self where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
@@ -351,6 +348,8 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
(@io $name:ident @ + $offset:literal) => {
#[allow(dead_code)]
impl $name {
+ pub(crate) const OFFSET: usize = $offset;
+
#[inline]
pub(crate) fn read<const SIZE: usize, T>(
io: &T,
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 07/18] gpu: nova-core: register: fix documentation and indentation
Fix a few documentation inconsistencies, and harmonize indentation where
possible.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 34 +++++++++-------------------------
1 file changed, 9 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
b096a3689483fa79dc22f16f4baac2f9a9495c28..73ee72a6c7baa7e9d1032ec0da119940b8f1a3f7
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -87,44 +87,28 @@
/// providing its own `completed` field.
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
- (
- $name:ident @ $offset:literal $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
+ ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } )
=> {
register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ $offset);
};
- // Creates a alias register of fixed offset register `alias` with its own
fields.
- (
- $name:ident => $alias:ident $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
+ // Creates an alias register of fixed offset register `alias` with its own
fields.
+ ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } )
=> {
register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ $alias::OFFSET);
};
// Creates a register at a relative offset from a base address.
- (
- $name:ident @ + $offset:literal $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
+ ($name:ident @ + $offset:literal $(, $comment:literal)? { $($fields:tt)* }
) => {
register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ + $offset);
};
- // Creates a alias register of relative offset register `alias` with its
own fields.
- (
- $name:ident => + $alias:ident $(, $comment:literal)? {
- $($fields:tt)*
- }
- ) => {
+ // Creates an alias register of relative offset register `alias` with its
own fields.
+ ($name:ident => + $alias:ident $(, $comment:literal)? { $($fields:tt)* }
) => {
register!(@common $name $(, $comment)?);
register!(@field_accessors $name { $($fields)* });
register!(@io $name @ + $alias::OFFSET);
@@ -259,7 +243,7 @@ impl $name {
{ |f| <$into_type>::from(f as $type) } $into_type =>
$into_type $(, $comment)?;);
};
- // Shortcut for fields defined as non-`bool` without the `=>` or
`?=>` syntax.
+ // Shortcut for non-boolean fields defined without the `=>` or `?=>`
syntax.
(
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
$(, $comment:literal)?;
@@ -310,7 +294,7 @@ pub(crate) fn [<set_ $field>](mut self, value:
$to_type) -> Self {
);
};
- // Creates the IO accessors for a fixed offset register.
+ // Generates the IO accessors for a fixed offset register.
(@io $name:ident @ $offset:expr) => {
#[allow(dead_code)]
impl $name {
@@ -344,7 +328,7 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
}
};
- // Create the IO accessors for a relative offset register.
+ // Generates the IO accessors for a relative offset register.
(@io $name:ident @ + $offset:literal) => {
#[allow(dead_code)]
impl $name {
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 08/18] gpu: nova-core: register: add missing doccomments for fixed registers I/O accessors
Add the missing doccomments for these accessors, as having a bit of
inline documentation is always helpful.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
73ee72a6c7baa7e9d1032ec0da119940b8f1a3f7..60e5e6a2250a9db453a7a648108af6acaa047342
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -300,6 +300,7 @@ pub(crate) fn [<set_ $field>](mut self, value:
$to_type) -> Self {
impl $name {
pub(crate) const OFFSET: usize = $offset;
+ /// Read the register from its address in `io`.
#[inline]
pub(crate) fn read<const SIZE: usize, T>(io: &T) ->
Self where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
@@ -307,6 +308,7 @@ pub(crate) fn read<const SIZE: usize, T>(io: &T)
-> Self where
Self(io.read32($offset))
}
+ /// Write the value contained in `self` to the register address in
`io`.
#[inline]
pub(crate) fn write<const SIZE: usize, T>(self, io: &T)
where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
@@ -314,6 +316,8 @@ pub(crate) fn write<const SIZE: usize, T>(self, io:
&T) where
io.write32(self.0, $offset)
}
+ /// Read the register from its address in `io` and run `f` on its
value to obtain a new
+ /// value to write back.
#[inline]
pub(crate) fn alter<const SIZE: usize, T, F>(
io: &T,
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 09/18] gpu: nova-core: register: add fields dispatcher internal rule
Fields are complex and cumbersome to match in a rule, and were only
captured in order to generate the field accessors. However, there are
other places (like the `Debug` and `Default` implementations) where we
would benefit from having access to at least some of the field
information, but refrained from doing so because it would have meant
matching the whole fields in a rule more complex than we need.
Introduce a new `@fields_dispatcher` internal rule that captures all the
field information and passes it to `@field_accessors`. It does not
provide any functional change in itself, but allows us to reuse the
captured field information partially to provide better `Debug` and
`Default` implementations in following patches.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 42 +++++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
60e5e6a2250a9db453a7a648108af6acaa047342..1d473ceb7b97e18e36246569abf0bb04e7b02060
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -88,37 +88,33 @@
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } )
=> {
- register!(@common $name $(, $comment)?);
- register!(@field_accessors $name { $($fields)* });
+ register!(@core $name $(, $comment)? { $($fields)* } );
register!(@io $name @ $offset);
};
// Creates an alias register of fixed offset register `alias` with its own
fields.
($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } )
=> {
- register!(@common $name $(, $comment)?);
- register!(@field_accessors $name { $($fields)* });
+ register!(@core $name $(, $comment)? { $($fields)* } );
register!(@io $name @ $alias::OFFSET);
};
// Creates a register at a relative offset from a base address.
($name:ident @ + $offset:literal $(, $comment:literal)? { $($fields:tt)* }
) => {
- register!(@common $name $(, $comment)?);
- register!(@field_accessors $name { $($fields)* });
+ register!(@core $name $(, $comment)? { $($fields)* } );
register!(@io $name @ + $offset);
};
// Creates an alias register of relative offset register `alias` with its
own fields.
($name:ident => + $alias:ident $(, $comment:literal)? { $($fields:tt)* }
) => {
- register!(@common $name $(, $comment)?);
- register!(@field_accessors $name { $($fields)* });
+ register!(@core $name $(, $comment)? { $($fields)* } );
register!(@io $name @ + $alias::OFFSET);
};
// All rules below are helpers.
// Defines the wrapper `$name` type, as well as its relevant
implementations (`Debug`, `BitOr`,
- // and conversion to regular `u32`).
- (@common $name:ident $(, $comment:literal)?) => {
+ // and conversion to the value type) and field accessor methods.
+ (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
$(
#[doc=$comment]
)?
@@ -149,6 +145,32 @@ fn from(reg: $name) -> u32 {
reg.0
}
}
+
+ register!(@fields_dispatcher $name { $($fields)* });
+ };
+
+ // Captures the fields and passes them to all the implementers that require
field information.
+ //
+ // Used to simplify the matching rules for implementers, so they don't
need to match the entire
+ // complex fields rule even though they only make use of part of it.
+ (@fields_dispatcher $name:ident {
+ $($hi:tt:$lo:tt $field:ident as $type:tt
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ register!(@field_accessors $name {
+ $(
+ $hi:$lo $field as $type
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ $(, $comment)?
+ ;
+ )*
+ });
};
// Defines all the field getter/methods methods for `$name`.
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 10/18] gpu: nova-core: register: improve `Debug` implementation
Now that we have an internal rule to dispatch field information where
needed, use it to generate a better `Debug` implementation where the raw
hexadecimal value of the register is displayed, as well as the `Debug`
values of its individual fields.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
1d473ceb7b97e18e36246569abf0bb04e7b02060..75b7b742b117240543ad292fc69e5e1341728174
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -122,16 +122,6 @@ macro_rules! register {
#[derive(Clone, Copy, Default)]
pub(crate) struct $name(u32);
- // TODO[REGA]: display the raw hex value, then the value of all the
fields. This requires
- // matching the fields, which will complexify the syntax
considerably...
- impl ::core::fmt::Debug for $name {
- fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
- f.debug_tuple(stringify!($name))
- .field(&format_args!("0x{0:x}", &self.0))
- .finish()
- }
- }
-
impl ::core::ops::BitOr for $name {
type Output = Self;
@@ -171,6 +161,7 @@ fn from(reg: $name) -> u32 {
;
)*
});
+ register!(@debug $name { $($field;)* });
};
// Defines all the field getter/methods methods for `$name`.
@@ -316,6 +307,20 @@ pub(crate) fn [<set_ $field>](mut self, value:
$to_type) -> Self {
);
};
+ // Generates the `Debug` implementation for `$name`.
+ (@debug $name:ident { $($field:ident;)* }) => {
+ impl ::core::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
+ f.debug_struct(stringify!($name))
+ .field("<raw>",
&format_args!("{:#x}", &self.0))
+ $(
+ .field(stringify!($field), &self.$field())
+ )*
+ .finish()
+ }
+ }
+ };
+
// Generates the IO accessors for a fixed offset register.
(@io $name:ident @ $offset:expr) => {
#[allow(dead_code)]
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 11/18] gpu: nova-core: register: generate correct `Default` implementation
The `Default` implementation of a register should be the aggregate of
the default values of all its fields, and not simply be zeroed.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
75b7b742b117240543ad292fc69e5e1341728174..97895800ff7031d287b3bbd03a00b1ca71014aa8
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -112,14 +112,14 @@ macro_rules! register {
// All rules below are helpers.
- // Defines the wrapper `$name` type, as well as its relevant
implementations (`Debug`, `BitOr`,
- // and conversion to the value type) and field accessor methods.
+ // Defines the wrapper `$name` type, as well as its relevant
implementations (`Debug`,
+ // `Default`, `BitOr`, and conversion to the value type) and field accessor
methods.
(@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
$(
#[doc=$comment]
)?
#[repr(transparent)]
- #[derive(Clone, Copy, Default)]
+ #[derive(Clone, Copy)]
pub(crate) struct $name(u32);
impl ::core::ops::BitOr for $name {
@@ -162,6 +162,7 @@ fn from(reg: $name) -> u32 {
)*
});
register!(@debug $name { $($field;)* });
+ register!(@default $name { $($field;)* });
};
// Defines all the field getter/methods methods for `$name`.
@@ -321,6 +322,25 @@ fn fmt(&self, f: &mut
::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
}
};
+ // Generates the `Default` implementation for `$name`.
+ (@default $name:ident { $($field:ident;)* }) => {
+ /// Returns a value for the register where all fields are set to their
default value.
+ impl ::core::default::Default for $name {
+ fn default() -> Self {
+ #[allow(unused_mut)]
+ let mut value = Self(Default::default());
+
+ ::kernel::macros::paste!(
+ $(
+ value.[<set_ $field>](Default::default());
+ )*
+ );
+
+ value
+ }
+ }
+ };
+
// Generates the IO accessors for a fixed offset register.
(@io $name:ident @ $offset:expr) => {
#[allow(dead_code)]
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 12/18] gpu: nova-core: register: split @io rule into fixed and relative versions
We used the same @io rule with different patterns to define both the
fixed and relative I/O accessors. This can be confusing as the matching
rules are very similar.
Since all call sites know which version they want to call, split @io
into @io_fixed and @io_relative to remove any ambiguity.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
97895800ff7031d287b3bbd03a00b1ca71014aa8..1b2074e38381f9918c48691301e5a6f62861501f
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -89,25 +89,25 @@ macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } )
=> {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io $name @ $offset);
+ register!(@io_fixed $name @ $offset);
};
// Creates an alias register of fixed offset register `alias` with its own
fields.
($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } )
=> {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io $name @ $alias::OFFSET);
+ register!(@io_fixed $name @ $alias::OFFSET);
};
// Creates a register at a relative offset from a base address.
($name:ident @ + $offset:literal $(, $comment:literal)? { $($fields:tt)* }
) => {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io $name @ + $offset);
+ register!(@io_relative $name @ + $offset);
};
// Creates an alias register of relative offset register `alias` with its
own fields.
($name:ident => + $alias:ident $(, $comment:literal)? { $($fields:tt)* }
) => {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io $name @ + $alias::OFFSET);
+ register!(@io_relative $name @ + $alias::OFFSET);
};
// All rules below are helpers.
@@ -342,7 +342,7 @@ fn default() -> Self {
};
// Generates the IO accessors for a fixed offset register.
- (@io $name:ident @ $offset:expr) => {
+ (@io_fixed $name:ident @ $offset:expr) => {
#[allow(dead_code)]
impl $name {
pub(crate) const OFFSET: usize = $offset;
@@ -380,7 +380,7 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
};
// Generates the IO accessors for a relative offset register.
- (@io $name:ident @ + $offset:literal) => {
+ (@io_relative $name:ident @ + $offset:literal) => {
#[allow(dead_code)]
impl $name {
pub(crate) const OFFSET: usize = $offset;
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 13/18] gpu: nova-core: register: use #[inline(always)] for all methods
These methods should always be inlined, so use the strongest compiler
hint that exists to maximize the chance they will indeed be.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/regs/macros.rs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
1b2074e38381f9918c48691301e5a6f62861501f..316b67ee1cea7c7fa2894c1778e7d43f853bdb19
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -280,7 +280,7 @@ impl $name {
#[doc="Returns the value of this field:"]
#[doc=$comment]
)?
- #[inline]
+ #[inline(always)]
pub(crate) fn $field(self) -> $res_type {
::kernel::macros::paste!(
const MASK: u32 = $name::[<$field:upper _MASK>];
@@ -296,7 +296,7 @@ pub(crate) fn $field(self) -> $res_type {
#[doc="Sets the value of this field:"]
#[doc=$comment]
)?
- #[inline]
+ #[inline(always)]
pub(crate) fn [<set_ $field>](mut self, value: $to_type) ->
Self {
const MASK: u32 = $name::[<$field:upper _MASK>];
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
@@ -348,7 +348,7 @@ impl $name {
pub(crate) const OFFSET: usize = $offset;
/// Read the register from its address in `io`.
- #[inline]
+ #[inline(always)]
pub(crate) fn read<const SIZE: usize, T>(io: &T) ->
Self where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
{
@@ -356,7 +356,7 @@ pub(crate) fn read<const SIZE: usize, T>(io: &T)
-> Self where
}
/// Write the value contained in `self` to the register address in
`io`.
- #[inline]
+ #[inline(always)]
pub(crate) fn write<const SIZE: usize, T>(self, io: &T)
where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
{
@@ -365,7 +365,7 @@ pub(crate) fn write<const SIZE: usize, T>(self, io:
&T) where
/// Read the register from its address in `io` and run `f` on its
value to obtain a new
/// value to write back.
- #[inline]
+ #[inline(always)]
pub(crate) fn alter<const SIZE: usize, T, F>(
io: &T,
f: F,
@@ -385,7 +385,7 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
impl $name {
pub(crate) const OFFSET: usize = $offset;
- #[inline]
+ #[inline(always)]
pub(crate) fn read<const SIZE: usize, T>(
io: &T,
base: usize,
@@ -395,7 +395,7 @@ pub(crate) fn read<const SIZE: usize, T>(
Self(io.read32(base + $offset))
}
- #[inline]
+ #[inline(always)]
pub(crate) fn write<const SIZE: usize, T>(
self,
io: &T,
@@ -406,7 +406,7 @@ pub(crate) fn write<const SIZE: usize, T>(
io.write32(self.0, base + $offset)
}
- #[inline]
+ #[inline(always)]
pub(crate) fn alter<const SIZE: usize, T, F>(
io: &T,
base: usize,
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 14/18] gpu: nova-core: register: redesign relative registers
The relative registers are currently very unsafe to use: callers can
specify any constant as the base address for access, meaning they can
effectively interpret any I/O address as any relative register.
Ideally, valid base addresses for a family of registers should be
explicitly defined in the code, and could only be used with the relevant
registers
This patch changes the relative register declaration into this:
register!(REGISTER_NAME @ BaseTrait[offset] ...
Where `BaseTrait` is the name of a ZST used as a parameter of the
`RegisterBase<>` trait to define a trait unique to a class of register.
This specialized trait is then implemented for every type that provides
a valid base address, enabling said types to be passed as the base
address provider for the register's I/O accessor methods.
This design thus makes it impossible to pass an unexpected base address
to a relative register, and, since the valid bases are all known at
compile-time, also guarantees that all I/O accesses are done within the
valid bounds of the I/O range.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
Documentation/gpu/nova/core/todo.rst | 1 -
drivers/gpu/nova-core/falcon.rs | 67 +++++++++-------
drivers/gpu/nova-core/falcon/gsp.rs | 12 ++-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 14 ++--
drivers/gpu/nova-core/falcon/sec2.rs | 9 ++-
drivers/gpu/nova-core/regs.rs | 50 ++++++------
drivers/gpu/nova-core/regs/macros.rs | 126 +++++++++++++++++++++++-------
7 files changed, 182 insertions(+), 97 deletions(-)
diff --git a/Documentation/gpu/nova/core/todo.rst
b/Documentation/gpu/nova/core/todo.rst
index
894a1e9c3741a43ad4eb76d24a9486862999874e..a1d12c1b289d89251d914fc271b7243ced11d487
100644
--- a/Documentation/gpu/nova/core/todo.rst
+++ b/Documentation/gpu/nova/core/todo.rst
@@ -131,7 +131,6 @@ crate so it can be used by other components as well.
Features desired before this happens:
-* Relative register with build-time base address validation,
* 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`,
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index
c2c6f9eb380ab390befe5af1b3c5df260ccd7595..66400d07d186938735f705c116d7c7df5fb77fca
100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -14,6 +14,7 @@
use crate::driver::Bar0;
use crate::gpu::Chipset;
use crate::regs;
+use crate::regs::macros::RegisterBase;
use crate::util;
pub(crate) mod gsp;
@@ -259,10 +260,16 @@ fn from(value: bool) -> Self {
}
}
-/// Trait defining the parameters of a given Falcon instance.
-pub(crate) trait FalconEngine: Sync {
- /// Base I/O address for the falcon, relative from which its registers are
accessed.
- const BASE: usize;
+/// Type used to represent the `PFALCON` registers address base for a given
falcon engine.
+pub(crate) struct PFalconBase(());
+
+/// Trait defining the parameters of a given Falcon engine.
+///
+/// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The
`ID` constant is used
+/// to identify a given Falcon instance with register I/O methods.
+pub(crate) trait FalconEngine: Sync + RegisterBase<PFalconBase> + Sized {
+ /// Singleton of the engine, used to identify it with register I/O methods.
+ const ID: Self;
}
/// Represents a portion of the firmware to be loaded into a particular memory
(e.g. IMEM or DMEM).
@@ -328,13 +335,13 @@ pub(crate) fn new(
bar: &Bar0,
need_riscv: bool,
) -> Result<Self> {
- let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
+ let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, &E::ID);
// Check that the revision and security model contain valid values.
let _ = hwcfg1.core_rev()?;
let _ = hwcfg1.security_model()?;
if need_riscv {
- let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
+ let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
if !hwcfg2.riscv() {
dev_err!(
dev,
@@ -354,7 +361,7 @@ pub(crate) fn new(
fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 20ms.
util::wait_on(Delta::from_millis(20), || {
- if regs::NV_PFALCON_FALCON_HWCFG2::read(bar,
E::BASE).mem_scrubbing_done() {
+ if regs::NV_PFALCON_FALCON_HWCFG2::read(bar,
&E::ID).mem_scrubbing_done() {
Some(())
} else {
None
@@ -364,12 +371,12 @@ fn reset_wait_mem_scrubbing(&self, bar: &Bar0)
-> Result {
/// Reset the falcon engine.
fn reset_eng(&self, bar: &Bar0) -> Result {
- let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
+ let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
// According to OpenRM's `kflcnPreResetWait_GA102` documentation,
HW sometimes does not set
// RESET_READY so a non-failing timeout is used.
let _ = util::wait_on(Delta::from_micros(150), || {
- let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
+ let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
if r.reset_ready() {
Some(())
} else {
@@ -377,13 +384,13 @@ fn reset_eng(&self, bar: &Bar0) -> Result {
}
});
- regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v|
v.set_reset(true));
+ regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v|
v.set_reset(true));
// TODO[DLAY]: replace with udelay() or equivalent once available.
// TIMEOUT: falcon engine should not take more than 10us to reset.
let _: Result = util::wait_on(Delta::from_micros(10), || None);
- regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v|
v.set_reset(false));
+ regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v|
v.set_reset(false));
self.reset_wait_mem_scrubbing(bar)?;
@@ -398,7 +405,7 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result
{
regs::NV_PFALCON_FALCON_RM::default()
.set_value(regs::NV_PMC_BOOT_0::read(bar).into())
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
Ok(())
}
@@ -449,10 +456,10 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
regs::NV_PFALCON_FALCON_DMATRFBASE::default()
.set_base((dma_start >> 8) as u32)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
.set_base((dma_start >> 40) as u16)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
.set_size(DmaTrfCmdSize::Size256B)
@@ -463,17 +470,17 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
// Perform a transfer of size `DMA_LEN`.
regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
.set_offs(load_offsets.dst_start + pos)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
.set_offs(src_start + pos)
- .write(bar, E::BASE);
- cmd.write(bar, E::BASE);
+ .write(bar, &E::ID);
+ cmd.write(bar, &E::ID);
// Wait for the transfer to complete.
// TIMEOUT: arbitrarily large value, no DMA transfer to the
falcon's small memories
// should ever take that long.
util::wait_on(Delta::from_secs(2), || {
- let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
+ let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar,
&E::ID);
if r.idle() {
Some(())
} else {
@@ -487,9 +494,9 @@ fn dma_wr<F: FalconFirmware<Target = E>>(
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the
falcon to run it.
pub(crate) fn dma_load<F: FalconFirmware<Target =
E>>(&self, bar: &Bar0, fw: &F) -> Result {
- regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v|
v.set_allow_phys_no_ctx(true));
- regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
- regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
+ 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| {
v.set_target(FalconFbifTarget::CoherentSysmem)
.set_mem_type(FalconFbifMemType::Physical)
});
@@ -502,7 +509,7 @@ pub(crate) fn dma_load<F: FalconFirmware<Target =
E>>(&self, bar: &Bar0, fw: &F)
// Set `BootVec` to start of non-secure code.
regs::NV_PFALCON_FALCON_BOOTVEC::default()
.set_value(fw.boot_addr())
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
Ok(())
}
@@ -523,27 +530,27 @@ pub(crate) fn boot(
if let Some(mbox0) = mbox0 {
regs::NV_PFALCON_FALCON_MAILBOX0::default()
.set_value(mbox0)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
}
if let Some(mbox1) = mbox1 {
regs::NV_PFALCON_FALCON_MAILBOX1::default()
.set_value(mbox1)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
}
- match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
+ match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en()
{
true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
.set_startcpu(true)
- .write(bar, E::BASE),
+ .write(bar, &E::ID),
false => regs::NV_PFALCON_FALCON_CPUCTL::default()
.set_startcpu(true)
- .write(bar, E::BASE),
+ .write(bar, &E::ID),
}
// TIMEOUT: arbitrarily large value, firmwares should complete in less
than 2 seconds.
util::wait_on(Delta::from_secs(2), || {
- let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
+ let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID);
if r.halted() {
Some(())
} else {
@@ -552,8 +559,8 @@ pub(crate) fn boot(
})?;
let (mbox0, mbox1) = (
- regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
- regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
+ regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(),
+ regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(),
);
Ok((mbox0, mbox1))
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
b/drivers/gpu/nova-core/falcon/gsp.rs
index
d622e9a64470932af0b48032be5a1d4b518bf4a7..0db9f94036a6a7ced5a461aec2cff2ce246a5e0e
100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -2,23 +2,27 @@
use crate::{
driver::Bar0,
- falcon::{Falcon, FalconEngine},
- regs,
+ falcon::{Falcon, FalconEngine, PFalconBase},
+ regs::{self, macros::RegisterBase},
};
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
pub(crate) struct Gsp(());
-impl FalconEngine for Gsp {
+impl RegisterBase<PFalconBase> for Gsp {
const BASE: usize = 0x00110000;
}
+impl FalconEngine for Gsp {
+ const ID: Self = Gsp(());
+}
+
impl Falcon<Gsp> {
/// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
/// allow GSP to signal CPU for processing new messages in message queue.
pub(crate) fn clear_swgen0_intr(&self, bar: &Bar0) {
regs::NV_PFALCON_FALCON_IRQSCLR::default()
.set_swgen0(true)
- .write(bar, Gsp::BASE);
+ .write(bar, &Gsp::ID);
}
}
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs
b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index
52c33d3f22a8e920742b45940c346c47fdc70e93..3fdacd19322dd122eb00e245de4be8d1edd61a5f
100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -16,15 +16,15 @@
use super::FalconHal;
fn select_core_ga102<E: FalconEngine>(bar: &Bar0) -> Result {
- let bcr_ctrl = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
+ let bcr_ctrl = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID);
if bcr_ctrl.core_select() != PeregrineCoreSelect::Falcon {
regs::NV_PRISCV_RISCV_BCR_CTRL::default()
.set_core_select(PeregrineCoreSelect::Falcon)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
// TIMEOUT: falcon core should take less than 10ms to report being
enabled.
util::wait_on(Delta::from_millis(10), || {
- let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, E::BASE);
+ let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID);
if r.valid() {
Some(())
} else {
@@ -76,16 +76,16 @@ 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::BASE);
+ .write(bar, &E::ID);
regs::NV_PFALCON2_FALCON_BROM_ENGIDMASK::default()
.set_value(u32::from(params.engine_id_mask))
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
regs::NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID::default()
.set_ucode_id(params.ucode_id)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
regs::NV_PFALCON2_FALCON_MOD_SEL::default()
.set_algo(FalconModSelAlgo::Rsa3k)
- .write(bar, E::BASE);
+ .write(bar, &E::ID);
Ok(())
}
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs
b/drivers/gpu/nova-core/falcon/sec2.rs
index
5147d9e2a7fe859210727504688d84cca4de991b..dbc486a712ffce30efa3a4264b0757974962302e
100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,10 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::falcon::FalconEngine;
+use crate::falcon::{FalconEngine, PFalconBase};
+use crate::regs::macros::RegisterBase;
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
pub(crate) struct Sec2(());
-impl FalconEngine for Sec2 {
+impl RegisterBase<PFalconBase> for Sec2 {
const BASE: usize = 0x00840000;
}
+
+impl FalconEngine for Sec2 {
+ const ID: Self = Sec2(());
+}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index
e8b8aabce3f36abe6a7fba3e11f677e36baa3897..4e047fc4947b6dc09dbd34e26eeda1ff14dff4ad
100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -5,11 +5,11 @@
#![allow(non_camel_case_types)]
#[macro_use]
-mod macros;
+pub(crate) mod macros;
use crate::falcon::{
DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType,
FalconFbifTarget,
- FalconModSelAlgo, FalconSecurityModel, PeregrineCoreSelect,
+ FalconModSelAlgo, FalconSecurityModel, PFalconBase, PeregrineCoreSelect,
};
use crate::gpu::{Architecture, Chipset};
use kernel::prelude::*;
@@ -181,24 +181,24 @@ pub(crate) fn vga_workspace_addr(self) ->
Option<u64> {
/* PFALCON */
-register!(NV_PFALCON_FALCON_IRQSCLR @ +0x00000004 {
+register!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] {
4:4 halt as bool;
6:6 swgen0 as bool;
});
-register!(NV_PFALCON_FALCON_MAILBOX0 @ +0x00000040 {
+register!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] {
31:0 value as u32;
});
-register!(NV_PFALCON_FALCON_MAILBOX1 @ +0x00000044 {
+register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] {
31:0 value as u32;
});
-register!(NV_PFALCON_FALCON_RM @ +0x00000084 {
+register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] {
31:0 value as u32;
});
-register!(NV_PFALCON_FALCON_HWCFG2 @ +0x000000f4 {
+register!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] {
10:10 riscv as bool;
12:12 mem_scrubbing as bool, "Set to 0 after memory scrubbing is
completed";
31:31 reset_ready as bool, "Signal indicating that reset is
completed (GA102+)";
@@ -211,17 +211,17 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
}
}
-register!(NV_PFALCON_FALCON_CPUCTL @ +0x00000100 {
+register!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] {
1:1 startcpu as bool;
4:4 halted as bool;
6:6 alias_en as bool;
});
-register!(NV_PFALCON_FALCON_BOOTVEC @ +0x00000104 {
+register!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] {
31:0 value as u32;
});
-register!(NV_PFALCON_FALCON_DMACTL @ +0x0000010c {
+register!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] {
0:0 require_ctx as bool;
1:1 dmem_scrubbing as bool;
2:2 imem_scrubbing as bool;
@@ -229,15 +229,15 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
7:7 secure_stat as bool;
});
-register!(NV_PFALCON_FALCON_DMATRFBASE @ +0x00000110 {
+register!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] {
31:0 base as u32;
});
-register!(NV_PFALCON_FALCON_DMATRFMOFFS @ +0x00000114 {
+register!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] {
23:0 offs as u32;
});
-register!(NV_PFALCON_FALCON_DMATRFCMD @ +0x00000118 {
+register!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] {
0:0 full as bool;
1:1 idle as bool;
3:2 sec as u8;
@@ -248,60 +248,60 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
16:16 set_dmtag as u8;
});
-register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ +0x0000011c {
+register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] {
31:0 offs as u32;
});
-register!(NV_PFALCON_FALCON_DMATRFBASE1 @ +0x00000128 {
+register!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] {
8:0 base as u16;
});
-register!(NV_PFALCON_FALCON_HWCFG1 @ +0x0000012c {
+register!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] {
3:0 core_rev as u8 ?=> FalconCoreRev, "Core revision";
5:4 security_model as u8 ?=> FalconSecurityModel, "Security
model";
7:6 core_rev_subversion as u8 ?=> FalconCoreRevSubversion,
"Core revision subversion";
});
-register!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ +0x00000130 {
+register!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] {
1:1 startcpu as bool;
});
// Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE`
depending on the falcon
// instance.
-register!(NV_PFALCON_FALCON_ENGINE @ +0x000003c0 {
+register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] {
0:0 reset as bool;
});
// TODO[REGA]: this is an array of registers.
-register!(NV_PFALCON_FBIF_TRANSCFG @ +0x00000600 {
+register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600] {
1:0 target as u8 ?=> FalconFbifTarget;
2:2 mem_type as bool => FalconFbifMemType;
});
-register!(NV_PFALCON_FBIF_CTL @ +0x00000624 {
+register!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] {
7:7 allow_phys_no_ctx as bool;
});
-register!(NV_PFALCON2_FALCON_MOD_SEL @ +0x00001180 {
+register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalconBase[0x00001180] {
7:0 algo as u8 ?=> FalconModSelAlgo;
});
-register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ +0x00001198 {
+register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalconBase[0x00001198] {
7:0 ucode_id as u8;
});
-register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ +0x0000119c {
+register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalconBase[0x0000119c] {
31:0 value as u32;
});
// TODO[REGA]: this is an array of registers.
-register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ +0x00001210 {
+register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalconBase[0x00001210] {
31:0 value as u32;
});
/* PRISCV */
-register!(NV_PRISCV_RISCV_BCR_CTRL @ +0x00001668 {
+register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] {
0:0 valid as bool;
4:4 core_select as bool => PeregrineCoreSelect;
8:8 br_fetch as bool;
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
316b67ee1cea7c7fa2894c1778e7d43f853bdb19..1f0375d20b5f4328d496a14120eea8a54123f8cc
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -10,6 +10,16 @@
//! dedicated type for each register. Each such type comes with its own field
accessors that can
//! return an error if a field's value is invalid.
+/// Trait providing a base address to be added to the offset of a relative
register to obtain
+/// its actual offset.
+///
+/// The `T` generic argument is used to distinguish which base to use, in case
a type provides
+/// several bases. It is given to the `register!` macro to restrict the use of
the register to
+/// implementors of this particular variant.
+pub(crate) trait RegisterBase<T> {
+ const BASE: usize;
+}
+
/// Defines a dedicated type for a register with an absolute offset, including
getter and setter
/// methods for its fields and methods to read and write it from an `Io`
region.
///
@@ -56,20 +66,6 @@
/// The documentation strings are optional. If present, they will be added to
the type's
/// definition, or the field getter and setter methods they are attached to.
///
-/// Putting a `+` before the address of the register makes it relative to a
base: the `read` and
-/// `write` methods take a `base` argument that is added to the specified
address before access:
-///
-/// ```no_run
-/// register!(CPU_CTL @ +0x0000010, "CPU core control" {
-/// 0:0 start as bool, "Start the CPU core";
-/// });
-///
-/// // Flip the `start` switch for the CPU core which base address is at
`CPU_BASE`.
-/// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
-/// pr_info!("CPU CTL: {:#x}", cpuctl);
-/// cpuctl.set_start(true).write(&bar, CPU_BASE);
-/// ```
-///
/// It is also possible to create a alias register by using the `=> ALIAS`
syntax. This is useful
/// for cases where a register's interpretation depends on the context:
///
@@ -85,6 +81,57 @@
///
/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as
`SCRATCH_0`, while also
/// providing its own `completed` field.
+///
+/// ## Relative registers
+///
+/// A register can be defined as being accessible from a fixed offset of a
provided base. For this,
+/// use the `Base[Offset]` syntax when specifying the register's address.
+///
+/// `Base` is an arbitrary type (typically a ZST) to be used as a generic
parameter of the
+/// `RegisterBase` to provide the base as a constant, i.e. each type providing
a base for this
+/// register needs to implement `RegisterBase<Base>`.
+///
+/// The`read`, `write` and `alter` methods of relative registers take an extra
`base` argument that
+/// is used to resolve the final address of the register.
+///
+/// ```no_run
+/// // 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 = 0xf00;
+/// }
+/// // Singleton of `CPU0` used to identify it.
+/// const CPU0: Cpu0 = Cpu0;
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x1f00;
+/// }
+/// // Singleton of `CPU1` used to identify it.
+/// const CPU1: Cpu1 = Cpu1;
+///
+/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
+/// 0:0 start as bool, "Start the CPU core";
+/// });
+///
+/// // Start `CPU0`.
+/// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true));
+///
+/// // Start `CPU1`.
+/// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true));
+///
+/// // Alias of a relative register.
+/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core
control" {
+/// 1:1 alias_start as bool, "Start the aliased CPU core";
+/// });
+///
+/// // Start the aliased `CPU0`.
+/// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
+/// ```
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } )
=> {
@@ -98,16 +145,16 @@ macro_rules! register {
register!(@io_fixed $name @ $alias::OFFSET);
};
- // Creates a register at a relative offset from a base address.
- ($name:ident @ + $offset:literal $(, $comment:literal)? { $($fields:tt)* }
) => {
+ // Creates a register at a relative offset from a base address provider.
+ ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? {
$($fields:tt)* } ) => {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io_relative $name @ + $offset);
+ register!(@io_relative $name @ $base [ $offset ]);
};
// Creates an alias register of relative offset register `alias` with its
own fields.
- ($name:ident => + $alias:ident $(, $comment:literal)? { $($fields:tt)* }
) => {
+ ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? {
$($fields:tt)* }) => {
register!(@core $name $(, $comment)? { $($fields)* } );
- register!(@io_relative $name @ + $alias::OFFSET);
+ register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
// All rules below are helpers.
@@ -380,39 +427,62 @@ pub(crate) fn alter<const SIZE: usize, T, F>(
};
// Generates the IO accessors for a relative offset register.
- (@io_relative $name:ident @ + $offset:literal) => {
+ (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
#[allow(dead_code)]
impl $name {
pub(crate) const OFFSET: usize = $offset;
+ /// Read the register 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>(
+ pub(crate) fn read<const SIZE: usize, T, B>(
io: &T,
- base: usize,
+ #[allow(unused_variables)]
+ base: &B,
) -> Self where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ B: crate::regs::macros::RegisterBase<$base>,
{
- Self(io.read32(base + $offset))
+ const OFFSET: usize = $name::OFFSET;
+
+ let value = io.read32(
+ <B as
crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
+ );
+
+ Self(value)
}
+ /// Write the value contained in `self` to `io`, using the base
address provided by
+ /// `base` and adding the register's offset to it.
#[inline(always)]
- pub(crate) fn write<const SIZE: usize, T>(
+ pub(crate) fn write<const SIZE: usize, T, B>(
self,
io: &T,
- base: usize,
+ #[allow(unused_variables)]
+ base: &B,
) where
T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ B: crate::regs::macros::RegisterBase<$base>,
{
- io.write32(self.0, base + $offset)
+ const OFFSET: usize = $name::OFFSET;
+
+ io.write32(
+ self.0,
+ <B as
crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
+ );
}
+ /// Read the register 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, F>(
+ pub(crate) fn alter<const SIZE: usize, T, B, F>(
io: &T,
- base: usize,
+ base: &B,
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));
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 15/18] gpu: nova-core: falcon: add distinct base address for PFALCON2
Falcon engines have two distinct register bases: `PFALCON` and
`PFALCON2`. So far we assumed that `PFALCON2` was located at `PFALCON +
0x1000` because that is the case of most engines, but there are
exceptions (NVDEC uses `0x1c00`).
Fix this shortcoming by leveraging the redesigned relative registers
definitions to assign a distinct `PFalcon2Base` base address to each
falcon engine.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 7 ++++++-
drivers/gpu/nova-core/falcon/gsp.rs | 6 +++++-
drivers/gpu/nova-core/falcon/sec2.rs | 6 +++++-
drivers/gpu/nova-core/regs.rs | 12 +++++++-----
4 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index
66400d07d186938735f705c116d7c7df5fb77fca..23a4ff591c8db8d4600ec43dfaa42233cbf65c0e
100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -263,11 +263,16 @@ fn from(value: bool) -> Self {
/// Type used to represent the `PFALCON` registers address base for a given
falcon engine.
pub(crate) struct PFalconBase(());
+/// Type used to represent the `PFALCON2` registers address base for a given
falcon engine.
+pub(crate) struct PFalcon2Base(());
+
/// Trait defining the parameters of a given Falcon engine.
///
/// Each engine provides one base for `PFALCON` and `PFALCON2` registers. The
`ID` constant is used
/// to identify a given Falcon instance with register I/O methods.
-pub(crate) trait FalconEngine: Sync + RegisterBase<PFalconBase> + Sized {
+pub(crate) trait FalconEngine:
+ Sync + RegisterBase<PFalconBase> + RegisterBase<PFalcon2Base> +
Sized
+{
/// Singleton of the engine, used to identify it with register I/O methods.
const ID: Self;
}
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs
b/drivers/gpu/nova-core/falcon/gsp.rs
index
0db9f94036a6a7ced5a461aec2cff2ce246a5e0e..f17599cb49fa1e5077a554dc14b3715aa62a4ebd
100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -2,7 +2,7 @@
use crate::{
driver::Bar0,
- falcon::{Falcon, FalconEngine, PFalconBase},
+ falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
regs::{self, macros::RegisterBase},
};
@@ -13,6 +13,10 @@ impl RegisterBase<PFalconBase> for Gsp {
const BASE: usize = 0x00110000;
}
+impl RegisterBase<PFalcon2Base> for Gsp {
+ const BASE: usize = 0x00111000;
+}
+
impl FalconEngine for Gsp {
const ID: Self = Gsp(());
}
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs
b/drivers/gpu/nova-core/falcon/sec2.rs
index
dbc486a712ffce30efa3a4264b0757974962302e..815786c8480db6cb74541d7ab574112baeb816fe
100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::falcon::{FalconEngine, PFalconBase};
+use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
use crate::regs::macros::RegisterBase;
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
@@ -10,6 +10,10 @@ impl RegisterBase<PFalconBase> for Sec2 {
const BASE: usize = 0x00840000;
}
+impl RegisterBase<PFalcon2Base> for Sec2 {
+ const BASE: usize = 0x00841000;
+}
+
impl FalconEngine for Sec2 {
const ID: Self = Sec2(());
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index
4e047fc4947b6dc09dbd34e26eeda1ff14dff4ad..e6382c116adf699ab24de097c15ddca08017b36d
100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -9,7 +9,7 @@
use crate::falcon::{
DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType,
FalconFbifTarget,
- FalconModSelAlgo, FalconSecurityModel, PFalconBase, PeregrineCoreSelect,
+ FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase,
PeregrineCoreSelect,
};
use crate::gpu::{Architecture, Chipset};
use kernel::prelude::*;
@@ -282,20 +282,22 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
7:7 allow_phys_no_ctx as bool;
});
-register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalconBase[0x00001180] {
+/* PFALCON2 */
+
+register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] {
7:0 algo as u8 ?=> FalconModSelAlgo;
});
-register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalconBase[0x00001198] {
+register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198] {
7:0 ucode_id as u8;
});
-register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalconBase[0x0000119c] {
+register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] {
31:0 value as u32;
});
// TODO[REGA]: this is an array of registers.
-register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalconBase[0x00001210] {
+register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210] {
31:0 value as u32;
});
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 16/18] gpu: nova-core: register: add support for register arrays
Having registers that can be interpreted identically in a contiguous I/O
area (or at least, following a given stride) is a common way to organize
registers, and is used by NVIDIA hardware. Thus, add a way to simply and
safely declare such a layout using the register!() macro.
Build-time bound-checking is effective for array accesses performed with
a constant. For cases where the index cannot be known at compile time,
`try_` variants of the accessors are also made available that return
`EINVAL` if the access is out-of-bounds.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 +-
drivers/gpu/nova-core/regs.rs | 15 +--
drivers/gpu/nova-core/regs/macros.rs | 191 +++++++++++++++++++++++++++++++++++
3 files changed, 200 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index
8e32af16b669ca773e63e184d34c3e0427bc9b76..d4a0dc2e9a6a3cef368e2f577271f659f0f2c71b
100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -221,7 +221,7 @@ fn run_fwsec_frts(
fwsec_frts.run(dev, falcon, bar)?;
// SCRATCH_E contains the error code for FWSEC-FRTS.
- let frts_status =
regs::NV_PBUS_SW_SCRATCH_0E::read(bar).frts_err_code();
+ let frts_status =
regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR::read(bar).frts_err_code();
if frts_status != 0 {
dev_err!(
dev,
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index
e6382c116adf699ab24de097c15ddca08017b36d..95b2c0d6b88f3b04af96f07c055c539870649937
100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -44,8 +44,10 @@ pub(crate) fn chipset(self) -> Result<Chipset> {
/* PBUS */
-// TODO[REGA]: this is an array of registers.
-register!(NV_PBUS_SW_SCRATCH_0E at 0x00001438 {
+register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {});
+
+register!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR => NV_PBUS_SW_SCRATCH[0xe],
+ "scratch register 0xe used as FRTS firmware error code" {
31:16 frts_err_code as u16;
});
@@ -110,13 +112,12 @@ pub(crate) fn higher_bound(self) -> u64 {
0:0 read_protection_level0 as bool, "Set after FWSEC lowers its
protection level";
});
-// TODO[REGA]: This is an array of registers.
-register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234 {
- 31:0 value as u32;
-});
+// 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_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {});
register!(
- NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT =>
NV_PGC6_AON_SECURE_SCRATCH_GROUP_05,
+ NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT =>
NV_PGC6_AON_SECURE_SCRATCH_GROUP_05[0],
"Scratch group 05 register 0 used as GFW boot progress indicator"
{
7:0 progress as u8, "Progress of GFW boot (0xff means
completed)";
}
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
1f0375d20b5f4328d496a14120eea8a54123f8cc..af432f0535d2944fa4609f4a44c98a0b1394a2c0
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -132,6 +132,53 @@ pub(crate) trait RegisterBase<T> {
/// // Start the aliased `CPU0`.
/// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
/// ```
+///
+/// ## Arrays of registers
+///
+/// Some I/O areas contain consecutive values that can be interpreted in the
same way. These areas
+/// can be defined as an array of identical registers, allowing them to be
accessed by index with
+/// compile-time or runtime bound checking. Simply define their address as
`Address[Size]`, and add
+/// an `idx` parameter to their `read`, `write` and `alter` methods:
+///
+/// ```no_run
+/// # fn no_run() -> Result<(), Error> {
+/// # fn get_scratch_idx() -> usize {
+/// # 0x15
+/// # }
+/// register!(SCRATCH @ 0x00000c00[64], "Scratch registers" {
+/// 31:0 value as u32;
+/// });
+///
+/// let scratch_0 = SCRATCH::read(bar, 0).value();
+/// let scratch_15 = SCRATCH::read(bar, 15).value();
+/// // This won't build.
+/// // let scratch_128 = SCRATCH::read(bar, 128).value();
+///
+/// // Runtime-obtained array index.
+/// let scratch_idx = get_scratch_idx();
+/// // Access on a runtime index returns an error if it is out-of-bounds.
+/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();
+///
+/// // Alias to a particular register in an array.
+/// // `SCRATCH[8]` is used to convey the firmware exit code.
+/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status
code" {
+/// 7:0 status as u8;
+/// });
+///
+/// let status = FIRMWARE_STATUS::read(bar).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!(SCRATCH_INTERLEAVED_0 @ 0x00000d00[16 ; 8], "Scratch
registers bank 0" {
+/// 31:0 value as u32;
+/// });
+/// register!(SCRATCH_INTERLEAVED_1 @ 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)* } )
=> {
@@ -157,6 +204,35 @@ macro_rules! register {
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
+ // Creates an array of registers at a fixed offset of the MMIO space.
+ (
+ $name:ident @ $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_array $name @ $offset [ $size ; $stride ]);
+ };
+
+ // Shortcut for contiguous array of registers (stride == size of element).
+ (
+ $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {
+ $($fields:tt)*
+ }
+ ) => {
+ register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>()
] $(, $comment)? {
+ $($fields)*
+ } );
+ };
+
+ // Creates an alias of register `idx` of array of registers `alias` with
its own fields.
+ ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? {
$($fields:tt)* }) => {
+ static_assert!($idx < $alias::SIZE);
+ register!(@core $name $(, $comment)? { $($fields)* } );
+ register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
+ };
+
// All rules below are helpers.
// Defines the wrapper `$name` type, as well as its relevant
implementations (`Debug`,
@@ -490,4 +566,119 @@ pub(crate) fn alter<const SIZE: usize, T, B, F>(
}
}
};
+
+ // Generates the IO accessors for an array of registers.
+ (@io_array $name:ident @ $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 its address in
`io`.
+ #[inline(always)]
+ pub(crate) fn read<const SIZE: usize, T>(
+ io: &T,
+ idx: usize,
+ ) -> Self where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = Self::OFFSET + (idx * Self::STRIDE);
+ let value = io.read32(offset);
+
+ Self(value)
+ }
+
+ /// Write the value contained in `self` to the array register with
index `idx` in `io`.
+ #[inline(always)]
+ pub(crate) fn write<const SIZE: usize, T>(
+ self,
+ io: &T,
+ idx: usize
+ ) where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ {
+ build_assert!(idx < Self::SIZE);
+
+ let offset = Self::OFFSET + (idx * Self::STRIDE);
+
+ io.write32(self.0, offset);
+ }
+
+ /// Read the array register at index `idx` in `io` and run `f` on
its value to obtain a
+ /// new value to write back.
+ #[inline(always)]
+ pub(crate) fn alter<const SIZE: usize, T, F>(
+ io: &T,
+ idx: usize,
+ f: F,
+ ) where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ let reg = f(Self::read(io, idx));
+ reg.write(io, idx);
+ }
+
+ /// Read the array register at index `idx` from its address in
`io`.
+ ///
+ /// 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>(
+ io: &T,
+ idx: usize,
+ ) -> ::kernel::error::Result<Self> where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::read(io, idx))
+ } else {
+ Err(EINVAL)
+ }
+ }
+
+ /// Write the value contained in `self` to the array register with
index `idx` in `io`.
+ ///
+ /// 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>(
+ self,
+ io: &T,
+ idx: usize,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ {
+ if idx < Self::SIZE {
+ Ok(self.write(io, idx))
+ } else {
+ Err(EINVAL)
+ }
+ }
+
+ /// Read the array register at index `idx` in `io` and 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, F>(
+ io: &T,
+ idx: usize,
+ f: F,
+ ) -> ::kernel::error::Result where
+ T: ::core::ops::Deref<Target =
::kernel::io::Io<SIZE>>,
+ F: ::core::ops::FnOnce(Self) -> Self,
+ {
+ if idx < Self::SIZE {
+ Ok(Self::alter(io, idx, f))
+ } else {
+ Err(EINVAL)
+ }
+ }
+ }
+ };
}
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 17/18] gpu: nova-core: falcon: use register arrays for FUSE registers
FUSE registers are an array of 16 consecutive registers. Use the
newly available register array feature to define them properly and
improve the code using them.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
drivers/gpu/nova-core/falcon/hal/ga102.rs | 33 ++++++++++++++-----------------
drivers/gpu/nova-core/regs.rs | 8 +++++---
2 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs
b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index
3fdacd19322dd122eb00e245de4be8d1edd61a5f..13c945fd6d6b7b1acbb466678af0bf18da506265
100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -42,35 +42,32 @@ fn signature_reg_fuse_version_ga102(
engine_id_mask: u16,
ucode_id: u8,
) -> Result<u32> {
- // TODO[REGA]: The ucode fuse versions are contained in the
- // FUSE_OPT_FPF_<ENGINE>_UCODE<X>_VERSION registers, which are
an array. Our register
- // definition macros do not allow us to manage them properly, so we need to
hardcode their
- // addresses for now. Clean this up once we support register arrays.
+ const NV_FUSE_OPT_FPF_SIZE: u8 = regs::NV_FUSE_OPT_FPF_SIZE as u8;
// Each engine has 16 ucode version registers numbered from 1 to 16.
- if ucode_id == 0 || ucode_id > 16 {
- dev_err!(dev, "invalid ucode id {:#x}", ucode_id);
- return Err(EINVAL);
- }
+ let ucode_idx = match ucode_id {
+ 1..=NV_FUSE_OPT_FPF_SIZE => (ucode_id - 1) as usize,
+ _ => {
+ dev_err!(dev, "invalid ucode id {:#x}", ucode_id);
+ return Err(EINVAL);
+ }
+ };
- // Base address of the FUSE registers array corresponding to the engine.
- let reg_fuse_base = if engine_id_mask & 0x0001 != 0 {
- regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::OFFSET
+ // `ucode_idx` is guaranteed to be in the range [0..15], making the `read`
calls provable valid
+ // at build-time.
+ let reg_fuse_version = if engine_id_mask & 0x0001 != 0 {
+ regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::read(bar, ucode_idx).data()
} else if engine_id_mask & 0x0004 != 0 {
- regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::OFFSET
+ regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::read(bar, ucode_idx).data()
} else if engine_id_mask & 0x0400 != 0 {
- regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::OFFSET
+ regs::NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION::read(bar, ucode_idx).data()
} else {
dev_err!(dev, "unexpected engine_id_mask {:#x}",
engine_id_mask);
return Err(EINVAL);
};
- // Read `reg_fuse_base[ucode_id - 1]`.
- let reg_fuse_version - bar.read32(reg_fuse_base + ((ucode_id - 1) as
usize * core::mem::size_of::<u32>()));
-
// TODO[NUMM]: replace with `last_set_bit` once it lands.
- Ok(u32::BITS - reg_fuse_version.leading_zeros())
+ Ok(u16::BITS - reg_fuse_version.leading_zeros())
}
fn program_brom_ga102<E: FalconEngine>(bar: &Bar0, params:
&FalconBromParams) -> Result {
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index
95b2c0d6b88f3b04af96f07c055c539870649937..5156ab1910501e86b4391a34549817d2e7818e07
100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -168,15 +168,17 @@ pub(crate) fn vga_workspace_addr(self) ->
Option<u64> {
/* FUSE */
-register!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100 {
+pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize = 16;
+
+register!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @
0x00824100[NV_FUSE_OPT_FPF_SIZE] {
15:0 data as u16;
});
-register!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140 {
+register!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @
0x00824140[NV_FUSE_OPT_FPF_SIZE] {
15:0 data as u16;
});
-register!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0 {
+register!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_SIZE]
{
15:0 data as u16;
});
--
2.50.0
Alexandre Courbot
2025-Jul-04 07:25 UTC
[PATCH 18/18] gpu: nova-core: registers: 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.
Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>
---
Documentation/gpu/nova/core/todo.rst | 1 -
drivers/gpu/nova-core/falcon.rs | 2 +-
drivers/gpu/nova-core/falcon/hal/ga102.rs | 2 +-
drivers/gpu/nova-core/regs.rs | 8 +-
drivers/gpu/nova-core/regs/macros.rs | 241 ++++++++++++++++++++++++++++++
5 files changed, 247 insertions(+), 7 deletions(-)
diff --git a/Documentation/gpu/nova/core/todo.rst
b/Documentation/gpu/nova/core/todo.rst
index
a1d12c1b289d89251d914fc271b7243ced11d487..48b20656dcb16056db7784fa186f161126aae9aa
100644
--- a/Documentation/gpu/nova/core/todo.rst
+++ b/Documentation/gpu/nova/core/todo.rst
@@ -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,
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index
23a4ff591c8db8d4600ec43dfaa42233cbf65c0e..2929032a97a6ccecb2d090b82af966991b53797b
100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -501,7 +501,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)
});
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs
b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index
13c945fd6d6b7b1acbb466678af0bf18da506265..0b1cbe7853b3e85cb9c03f8e3987ec50a30253fb
100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -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);
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index
5156ab1910501e86b4391a34549817d2e7818e07..0a5ccd845eae755c3dbfe25971b6d0cdf4189f8e
100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -275,8 +275,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;
});
@@ -299,8 +298,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;
});
diff --git a/drivers/gpu/nova-core/regs/macros.rs
b/drivers/gpu/nova-core/regs/macros.rs
index
af432f0535d2944fa4609f4a44c98a0b1394a2c0..0a342f1ac665fc99eecb08e61889427543f63bc8
100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -179,6 +179,73 @@ 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 = 0xf00;
+/// }
+/// // Singleton of `CPU0` used to identify it.
+/// const CPU0: Cpu0 = Cpu0;
+///
+/// // ZST describing `CPU1`.
+/// struct Cpu1;
+/// impl RegisterBase<CpuCtlBase> for Cpu1 {
+/// const BASE: usize = 0x1f00;
+/// }
+/// // 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[0x1000[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)* } )
=> {
@@ -226,7 +293,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)* } );
@@ -681,4 +782,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)
+ }
+ }
+ }
+ };
}
--
2.50.0