diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 4950cecf30ca..fcc7678fd9e3 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -17,6 +17,8 @@ pub mod resource; pub use crate::register; pub use resource::Resource; +use register::LocatedRegister; + /// Physical address type. /// /// This is a type alias to either `u32` or `u64` depending on the config option @@ -473,6 +475,49 @@ pub trait Io { Ok(()) } + /// Generic fallible write of a fully-located register value. + /// + /// # Examples + /// + /// Tuples carrying a location and a value can be used with this method: + /// + /// ```no_run + /// use kernel::io::{ + /// register, + /// Io, + /// Mmio, + /// }; + /// + /// register! { + /// VERSION(u32) @ 0x100 { + /// 15:8 major; + /// 7:0 minor; + /// } + /// } + /// + /// impl VERSION { + /// fn new(major: u8, minor: u8) -> Self { + /// VERSION::zeroed().with_major(major).with_minor(minor) + /// } + /// } + /// + /// fn do_write_reg(io: &Mmio) -> Result { + /// + /// io.try_write_reg(VERSION::new(1, 0)) + /// } + /// ``` + #[inline(always)] + fn try_write_reg(&self, value: V) -> Result + where + L: IoLoc, + V: LocatedRegister, + Self: IoCapable, + { + let (location, value) = value.into_io_op(); + + self.try_write(location, value) + } + /// Generic fallible update with runtime bounds check. /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring @@ -578,6 +623,48 @@ pub trait Io { unsafe { self.io_write(io_value, address) } } + /// Generic infallible write of a fully-located register value. + /// + /// # Examples + /// + /// Tuples carrying a location and a value can be used with this method: + /// + /// ```no_run + /// use kernel::io::{ + /// register, + /// Io, + /// Mmio, + /// }; + /// + /// register! { + /// VERSION(u32) @ 0x100 { + /// 15:8 major; + /// 7:0 minor; + /// } + /// } + /// + /// impl VERSION { + /// fn new(major: u8, minor: u8) -> Self { + /// VERSION::zeroed().with_major(major).with_minor(minor) + /// } + /// } + /// + /// fn do_write_reg(io: &Mmio<0x1000>) { + /// io.write_reg(VERSION::new(1, 0)); + /// } + /// ``` + #[inline(always)] + fn write_reg(&self, value: V) + where + L: IoLoc, + V: LocatedRegister, + Self: IoKnownSize + IoCapable, + { + let (location, value) = value.into_io_op(); + + self.write(location, value) + } + /// Generic infallible update with compile-time bounds check. /// /// Note: this does not perform any synchronization. The caller is responsible for ensuring diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index dbd458aaa761..abc49926abfe 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -80,10 +80,10 @@ //! .with_const_minor_revision::<10>() //! // Runtime value. //! .with_vendor_id(obtain_vendor_id()); -//! io.write((), new_boot0); +//! io.write_reg(new_boot0); //! //! // Or, build a new value from zero and write it: -//! io.write((), BOOT_0::zeroed() +//! io.write_reg(BOOT_0::zeroed() //! .with_const_major_revision::<3>() //! .with_const_minor_revision::<10>() //! .with_vendor_id(obtain_vendor_id()) @@ -382,6 +382,34 @@ where } } +/// Trait implemented by items that contain both a register value and the absolute I/O location at +/// which to write it. +/// +/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg). +pub trait LocatedRegister { + /// Register value to write. + type Value: Register; + /// Full location information at which to write the value. + type Location: IoLoc; + + /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write + /// operation. + fn into_io_op(self) -> (Self::Location, Self::Value); +} + +impl LocatedRegister for T +where + T: FixedRegister, +{ + type Location = FixedRegisterLoc; + type Value = T; + + #[inline(always)] + fn into_io_op(self) -> (FixedRegisterLoc, T) { + (FixedRegisterLoc::new(), self) + } +} + /// Defines a dedicated type for a register, including getter and setter methods for its fields and /// methods to read and write it from an [`Io`](kernel::io::Io) region. /// @@ -436,6 +464,9 @@ where /// // The location of fixed offset registers is already contained in their type. Thus, the /// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`. /// io.write((), val2); +/// +/// // Or, the single-argument `Io::write_reg` can be used. +/// io.write_reg(val2); /// # } /// /// ```