mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
rust: io: separate generic I/O helpers from MMIO implementation
The previous Io<SIZE> type combined both the generic I/O access helpers and MMIO implementation details in a single struct. This coupling prevented reusing the I/O helpers for other backends, such as PCI configuration space. Establish a clean separation between the I/O interface and concrete backends by separating generic I/O helpers from MMIO implementation. Introduce a new trait hierarchy to handle different access capabilities: - IoCapable<T>: A marker trait indicating that a backend supports I/O operations of a certain type (u8, u16, u32, or u64). - Io trait: Defines fallible (try_read8, try_write8, etc.) and infallibile (read8, write8, etc.) I/O methods with runtime bounds checking and compile-time bounds checking. - IoKnownSize trait: The marker trait for types support infallible I/O methods. Move the MMIO-specific logic into a dedicated Mmio<SIZE> type that implements the Io traits. Rename IoRaw to MmioRaw and update consumers to use the new types. Cc: Alexandre Courbot <acourbot@nvidia.com> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Bjorn Helgaas <helgaas@kernel.org> Cc: Gary Guo <gary@garyguo.net> Cc: Danilo Krummrich <dakr@kernel.org> Cc: John Hubbard <jhubbard@nvidia.com> Signed-off-by: Zhi Wang <zhiw@nvidia.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260121202212.4438-3-zhiw@nvidia.com [ Add #[expect(unused)] to define_{read,write}!(). - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
7043698aee
commit
121d87b28e
11 changed files with 439 additions and 135 deletions
|
|
@ -11,6 +11,7 @@ use kernel::bits::bit_u32;
|
||||||
use kernel::device::Bound;
|
use kernel::device::Bound;
|
||||||
use kernel::device::Device;
|
use kernel::device::Device;
|
||||||
use kernel::devres::Devres;
|
use kernel::devres::Devres;
|
||||||
|
use kernel::io::Io;
|
||||||
use kernel::prelude::*;
|
use kernel::prelude::*;
|
||||||
|
|
||||||
use crate::driver::IoMem;
|
use crate::driver::IoMem;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ use core::{
|
||||||
|
|
||||||
use kernel::{
|
use kernel::{
|
||||||
device,
|
device,
|
||||||
io::poll::read_poll_timeout,
|
io::{
|
||||||
|
poll::read_poll_timeout,
|
||||||
|
Io, //
|
||||||
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::{
|
time::{
|
||||||
delay::fsleep,
|
delay::fsleep,
|
||||||
|
|
|
||||||
|
|
@ -369,16 +369,18 @@ macro_rules! register {
|
||||||
|
|
||||||
/// Read the register from its address in `io`.
|
/// Read the register from its address in `io`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
|
pub(crate) fn read<T, I>(io: &T) -> Self where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
Self(io.read32($offset))
|
Self(io.read32($offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the value contained in `self` to the register address in `io`.
|
/// Write the value contained in `self` to the register address in `io`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
|
pub(crate) fn write<T, I>(self, io: &T) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
io.write32(self.0, $offset)
|
io.write32(self.0, $offset)
|
||||||
}
|
}
|
||||||
|
|
@ -386,11 +388,12 @@ macro_rules! register {
|
||||||
/// Read the register from its address in `io` and run `f` on its value to obtain a new
|
/// Read the register from its address in `io` and run `f` on its value to obtain a new
|
||||||
/// value to write back.
|
/// value to write back.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn update<const SIZE: usize, T, F>(
|
pub(crate) fn update<T, I, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
f: F,
|
f: F,
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
let reg = f(Self::read(io));
|
let reg = f(Self::read(io));
|
||||||
|
|
@ -408,12 +411,13 @@ macro_rules! register {
|
||||||
/// Read the register from `io`, using the base address provided by `base` and adding
|
/// Read the register from `io`, using the base address provided by `base` and adding
|
||||||
/// the register's offset to it.
|
/// the register's offset to it.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn read<const SIZE: usize, T, B>(
|
pub(crate) fn read<T, I, B>(
|
||||||
io: &T,
|
io: &T,
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
base: &B,
|
base: &B,
|
||||||
) -> Self where
|
) -> Self where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
const OFFSET: usize = $name::OFFSET;
|
const OFFSET: usize = $name::OFFSET;
|
||||||
|
|
@ -428,13 +432,14 @@ macro_rules! register {
|
||||||
/// Write the value contained in `self` to `io`, using the base address provided by
|
/// Write the value contained in `self` to `io`, using the base address provided by
|
||||||
/// `base` and adding the register's offset to it.
|
/// `base` and adding the register's offset to it.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn write<const SIZE: usize, T, B>(
|
pub(crate) fn write<T, I, B>(
|
||||||
self,
|
self,
|
||||||
io: &T,
|
io: &T,
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
base: &B,
|
base: &B,
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
const OFFSET: usize = $name::OFFSET;
|
const OFFSET: usize = $name::OFFSET;
|
||||||
|
|
@ -449,12 +454,13 @@ macro_rules! register {
|
||||||
/// the register's offset to it, then run `f` on its value to obtain a new value to
|
/// the register's offset to it, then run `f` on its value to obtain a new value to
|
||||||
/// write back.
|
/// write back.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn update<const SIZE: usize, T, B, F>(
|
pub(crate) fn update<T, I, B, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
base: &B,
|
base: &B,
|
||||||
f: F,
|
f: F,
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
|
|
@ -474,11 +480,12 @@ macro_rules! register {
|
||||||
|
|
||||||
/// Read the array register at index `idx` from its address in `io`.
|
/// Read the array register at index `idx` from its address in `io`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn read<const SIZE: usize, T>(
|
pub(crate) fn read<T, I>(
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> Self where
|
) -> Self where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
build_assert!(idx < Self::SIZE);
|
build_assert!(idx < Self::SIZE);
|
||||||
|
|
||||||
|
|
@ -490,12 +497,13 @@ macro_rules! register {
|
||||||
|
|
||||||
/// Write the value contained in `self` to the array register with index `idx` in `io`.
|
/// Write the value contained in `self` to the array register with index `idx` in `io`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn write<const SIZE: usize, T>(
|
pub(crate) fn write<T, I>(
|
||||||
self,
|
self,
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize
|
idx: usize
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
build_assert!(idx < Self::SIZE);
|
build_assert!(idx < Self::SIZE);
|
||||||
|
|
||||||
|
|
@ -507,12 +515,13 @@ macro_rules! register {
|
||||||
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
|
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
|
||||||
/// new value to write back.
|
/// new value to write back.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn update<const SIZE: usize, T, F>(
|
pub(crate) fn update<T, I, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
f: F,
|
f: F,
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
let reg = f(Self::read(io, idx));
|
let reg = f(Self::read(io, idx));
|
||||||
|
|
@ -524,11 +533,12 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_read<const SIZE: usize, T>(
|
pub(crate) fn try_read<T, I>(
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> ::kernel::error::Result<Self> where
|
) -> ::kernel::error::Result<Self> where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
if idx < Self::SIZE {
|
if idx < Self::SIZE {
|
||||||
Ok(Self::read(io, idx))
|
Ok(Self::read(io, idx))
|
||||||
|
|
@ -542,12 +552,13 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_write<const SIZE: usize, T>(
|
pub(crate) fn try_write<T, I>(
|
||||||
self,
|
self,
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> ::kernel::error::Result where
|
) -> ::kernel::error::Result where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
{
|
{
|
||||||
if idx < Self::SIZE {
|
if idx < Self::SIZE {
|
||||||
Ok(self.write(io, idx))
|
Ok(self.write(io, idx))
|
||||||
|
|
@ -562,12 +573,13 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_update<const SIZE: usize, T, F>(
|
pub(crate) fn try_update<T, I, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
f: F,
|
f: F,
|
||||||
) -> ::kernel::error::Result where
|
) -> ::kernel::error::Result where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
if idx < Self::SIZE {
|
if idx < Self::SIZE {
|
||||||
|
|
@ -593,13 +605,14 @@ macro_rules! register {
|
||||||
/// Read the array register at index `idx` from `io`, using the base address provided
|
/// Read the array register at index `idx` from `io`, using the base address provided
|
||||||
/// by `base` and adding the register's offset to it.
|
/// by `base` and adding the register's offset to it.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn read<const SIZE: usize, T, B>(
|
pub(crate) fn read<T, I, B>(
|
||||||
io: &T,
|
io: &T,
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> Self where
|
) -> Self where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
build_assert!(idx < Self::SIZE);
|
build_assert!(idx < Self::SIZE);
|
||||||
|
|
@ -614,14 +627,15 @@ macro_rules! register {
|
||||||
/// Write the value contained in `self` to `io`, using the base address provided by
|
/// 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.
|
/// `base` and adding the offset of array register `idx` to it.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn write<const SIZE: usize, T, B>(
|
pub(crate) fn write<T, I, B>(
|
||||||
self,
|
self,
|
||||||
io: &T,
|
io: &T,
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize
|
idx: usize
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
build_assert!(idx < Self::SIZE);
|
build_assert!(idx < Self::SIZE);
|
||||||
|
|
@ -636,13 +650,14 @@ macro_rules! register {
|
||||||
/// by `base` and adding the register's offset to it, then run `f` on its value to
|
/// by `base` and adding the register's offset to it, then run `f` on its value to
|
||||||
/// obtain a new value to write back.
|
/// obtain a new value to write back.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn update<const SIZE: usize, T, B, F>(
|
pub(crate) fn update<T, I, B, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
f: F,
|
f: F,
|
||||||
) where
|
) where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
|
|
@ -656,12 +671,13 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_read<const SIZE: usize, T, B>(
|
pub(crate) fn try_read<T, I, B>(
|
||||||
io: &T,
|
io: &T,
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> ::kernel::error::Result<Self> where
|
) -> ::kernel::error::Result<Self> where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
if idx < Self::SIZE {
|
if idx < Self::SIZE {
|
||||||
|
|
@ -677,13 +693,14 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_write<const SIZE: usize, T, B>(
|
pub(crate) fn try_write<T, I, B>(
|
||||||
self,
|
self,
|
||||||
io: &T,
|
io: &T,
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
) -> ::kernel::error::Result where
|
) -> ::kernel::error::Result where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
{
|
{
|
||||||
if idx < Self::SIZE {
|
if idx < Self::SIZE {
|
||||||
|
|
@ -700,13 +717,14 @@ macro_rules! register {
|
||||||
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
|
||||||
/// access was out-of-bounds.
|
/// access was out-of-bounds.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_update<const SIZE: usize, T, B, F>(
|
pub(crate) fn try_update<T, I, B, F>(
|
||||||
io: &T,
|
io: &T,
|
||||||
base: &B,
|
base: &B,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
f: F,
|
f: F,
|
||||||
) -> ::kernel::error::Result where
|
) -> ::kernel::error::Result where
|
||||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
T: ::core::ops::Deref<Target = I>,
|
||||||
|
I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>,
|
||||||
B: crate::regs::macros::RegisterBase<$base>,
|
B: crate::regs::macros::RegisterBase<$base>,
|
||||||
F: ::core::ops::FnOnce(Self) -> Self,
|
F: ::core::ops::FnOnce(Self) -> Self,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use core::convert::TryFrom;
|
||||||
|
|
||||||
use kernel::{
|
use kernel::{
|
||||||
device,
|
device,
|
||||||
|
io::Io,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
ptr::{
|
ptr::{
|
||||||
Alignable,
|
Alignable,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,10 @@ use kernel::{
|
||||||
clk::Clk,
|
clk::Clk,
|
||||||
device::{Bound, Core, Device},
|
device::{Bound, Core, Device},
|
||||||
devres,
|
devres,
|
||||||
io::mem::IoMem,
|
io::{
|
||||||
|
mem::IoMem,
|
||||||
|
Io, //
|
||||||
|
},
|
||||||
of, platform,
|
of, platform,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
pwm, time,
|
pwm, time,
|
||||||
|
|
|
||||||
|
|
@ -74,14 +74,17 @@ struct Inner<T: Send> {
|
||||||
/// devres::Devres,
|
/// devres::Devres,
|
||||||
/// io::{
|
/// io::{
|
||||||
/// Io,
|
/// Io,
|
||||||
/// IoRaw,
|
/// IoKnownSize,
|
||||||
/// PhysAddr,
|
/// Mmio,
|
||||||
|
/// MmioRaw,
|
||||||
|
/// PhysAddr, //
|
||||||
/// },
|
/// },
|
||||||
|
/// prelude::*,
|
||||||
/// };
|
/// };
|
||||||
/// use core::ops::Deref;
|
/// use core::ops::Deref;
|
||||||
///
|
///
|
||||||
/// // See also [`pci::Bar`] for a real example.
|
/// // See also [`pci::Bar`] for a real example.
|
||||||
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
|
/// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>);
|
||||||
///
|
///
|
||||||
/// impl<const SIZE: usize> IoMem<SIZE> {
|
/// impl<const SIZE: usize> IoMem<SIZE> {
|
||||||
/// /// # Safety
|
/// /// # Safety
|
||||||
|
|
@ -96,7 +99,7 @@ struct Inner<T: Send> {
|
||||||
/// return Err(ENOMEM);
|
/// return Err(ENOMEM);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?))
|
/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?))
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|
@ -108,11 +111,11 @@ struct Inner<T: Send> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
||||||
/// type Target = Io<SIZE>;
|
/// type Target = Mmio<SIZE>;
|
||||||
///
|
///
|
||||||
/// fn deref(&self) -> &Self::Target {
|
/// fn deref(&self) -> &Self::Target {
|
||||||
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
|
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
|
||||||
/// unsafe { Io::from_raw(&self.0) }
|
/// unsafe { Mmio::from_raw(&self.0) }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
|
/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
|
||||||
|
|
@ -258,6 +261,10 @@ impl<T: Send> Devres<T> {
|
||||||
/// use kernel::{
|
/// use kernel::{
|
||||||
/// device::Core,
|
/// device::Core,
|
||||||
/// devres::Devres,
|
/// devres::Devres,
|
||||||
|
/// io::{
|
||||||
|
/// Io,
|
||||||
|
/// IoKnownSize, //
|
||||||
|
/// },
|
||||||
/// pci, //
|
/// pci, //
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,16 @@ pub type ResourceSize = bindings::resource_size_t;
|
||||||
/// By itself, the existence of an instance of this structure does not provide any guarantees that
|
/// By itself, the existence of an instance of this structure does not provide any guarantees that
|
||||||
/// the represented MMIO region does exist or is properly mapped.
|
/// the represented MMIO region does exist or is properly mapped.
|
||||||
///
|
///
|
||||||
/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io`
|
/// Instead, the bus specific MMIO implementation must convert this raw representation into an
|
||||||
/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure
|
/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio`
|
||||||
/// any guarantees are given.
|
/// structure any guarantees are given.
|
||||||
pub struct IoRaw<const SIZE: usize = 0> {
|
pub struct MmioRaw<const SIZE: usize = 0> {
|
||||||
addr: usize,
|
addr: usize,
|
||||||
maxsize: usize,
|
maxsize: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SIZE: usize> IoRaw<SIZE> {
|
impl<const SIZE: usize> MmioRaw<SIZE> {
|
||||||
/// Returns a new `IoRaw` instance on success, an error otherwise.
|
/// Returns a new `MmioRaw` instance on success, an error otherwise.
|
||||||
pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
|
pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
|
||||||
if maxsize < SIZE {
|
if maxsize < SIZE {
|
||||||
return Err(EINVAL);
|
return Err(EINVAL);
|
||||||
|
|
@ -81,14 +81,16 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||||
/// ffi::c_void,
|
/// ffi::c_void,
|
||||||
/// io::{
|
/// io::{
|
||||||
/// Io,
|
/// Io,
|
||||||
/// IoRaw,
|
/// IoKnownSize,
|
||||||
|
/// Mmio,
|
||||||
|
/// MmioRaw,
|
||||||
/// PhysAddr,
|
/// PhysAddr,
|
||||||
/// },
|
/// },
|
||||||
/// };
|
/// };
|
||||||
/// use core::ops::Deref;
|
/// use core::ops::Deref;
|
||||||
///
|
///
|
||||||
/// // See also `pci::Bar` for a real example.
|
/// // See also `pci::Bar` for a real example.
|
||||||
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
|
/// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>);
|
||||||
///
|
///
|
||||||
/// impl<const SIZE: usize> IoMem<SIZE> {
|
/// impl<const SIZE: usize> IoMem<SIZE> {
|
||||||
/// /// # Safety
|
/// /// # Safety
|
||||||
|
|
@ -103,7 +105,7 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||||
/// return Err(ENOMEM);
|
/// return Err(ENOMEM);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?))
|
/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?))
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|
@ -115,11 +117,11 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
||||||
/// type Target = Io<SIZE>;
|
/// type Target = Mmio<SIZE>;
|
||||||
///
|
///
|
||||||
/// fn deref(&self) -> &Self::Target {
|
/// fn deref(&self) -> &Self::Target {
|
||||||
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
|
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
|
||||||
/// unsafe { Io::from_raw(&self.0) }
|
/// unsafe { Mmio::from_raw(&self.0) }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
|
@ -133,29 +135,31 @@ impl<const SIZE: usize> IoRaw<SIZE> {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>);
|
pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>);
|
||||||
|
|
||||||
macro_rules! define_read {
|
macro_rules! define_read {
|
||||||
($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident -> $type_name:ty) => {
|
(infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident -> $type_name:ty) => {
|
||||||
/// Read IO data from a given offset known at compile time.
|
/// Read IO data from a given offset known at compile time.
|
||||||
///
|
///
|
||||||
/// Bound checks are performed on compile time, hence if the offset is not known at compile
|
/// Bound checks are performed on compile time, hence if the offset is not known at compile
|
||||||
/// time, the build will fail.
|
/// time, the build will fail.
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn $name(&self, offset: usize) -> $type_name {
|
$vis fn $name(&self, offset: usize) -> $type_name {
|
||||||
let addr = self.io_addr_assert::<$type_name>(offset);
|
let addr = self.io_addr_assert::<$type_name>(offset);
|
||||||
|
|
||||||
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
||||||
unsafe { bindings::$c_fn(addr as *const c_void) }
|
unsafe { bindings::$c_fn(addr as *const c_void) }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident -> $type_name:ty) => {
|
||||||
/// Read IO data from a given offset.
|
/// Read IO data from a given offset.
|
||||||
///
|
///
|
||||||
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
|
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
|
||||||
/// out of bounds.
|
/// out of bounds.
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub fn $try_name(&self, offset: usize) -> Result<$type_name> {
|
$vis fn $try_name(&self, offset: usize) -> Result<$type_name> {
|
||||||
let addr = self.io_addr::<$type_name>(offset)?;
|
let addr = self.io_addr::<$type_name>(offset)?;
|
||||||
|
|
||||||
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
||||||
|
|
@ -163,74 +167,97 @@ macro_rules! define_read {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#[expect(unused)]
|
||||||
|
pub(crate) use define_read;
|
||||||
|
|
||||||
macro_rules! define_write {
|
macro_rules! define_write {
|
||||||
($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident <- $type_name:ty) => {
|
(infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident <- $type_name:ty) => {
|
||||||
/// Write IO data from a given offset known at compile time.
|
/// Write IO data from a given offset known at compile time.
|
||||||
///
|
///
|
||||||
/// Bound checks are performed on compile time, hence if the offset is not known at compile
|
/// Bound checks are performed on compile time, hence if the offset is not known at compile
|
||||||
/// time, the build will fail.
|
/// time, the build will fail.
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn $name(&self, value: $type_name, offset: usize) {
|
$vis fn $name(&self, value: $type_name, offset: usize) {
|
||||||
let addr = self.io_addr_assert::<$type_name>(offset);
|
let addr = self.io_addr_assert::<$type_name>(offset);
|
||||||
|
|
||||||
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
||||||
unsafe { bindings::$c_fn(value, addr as *mut c_void) }
|
unsafe { bindings::$c_fn(value, addr as *mut c_void) }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident <- $type_name:ty) => {
|
||||||
/// Write IO data from a given offset.
|
/// Write IO data from a given offset.
|
||||||
///
|
///
|
||||||
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
|
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
|
||||||
/// out of bounds.
|
/// out of bounds.
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub fn $try_name(&self, value: $type_name, offset: usize) -> Result {
|
$vis fn $try_name(&self, value: $type_name, offset: usize) -> Result {
|
||||||
let addr = self.io_addr::<$type_name>(offset)?;
|
let addr = self.io_addr::<$type_name>(offset)?;
|
||||||
|
|
||||||
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
|
||||||
unsafe { bindings::$c_fn(value, addr as *mut c_void) }
|
unsafe { bindings::$c_fn(value, addr as *mut c_void) };
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#[expect(unused)]
|
||||||
|
pub(crate) use define_write;
|
||||||
|
|
||||||
impl<const SIZE: usize> Io<SIZE> {
|
/// Checks whether an access of type `U` at the given `offset`
|
||||||
/// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping.
|
/// is valid within this region.
|
||||||
///
|
#[inline]
|
||||||
/// # Safety
|
const fn offset_valid<U>(offset: usize, size: usize) -> bool {
|
||||||
///
|
let type_size = core::mem::size_of::<U>();
|
||||||
/// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
|
if let Some(end) = offset.checked_add(type_size) {
|
||||||
/// `maxsize`.
|
end <= size && offset % type_size == 0
|
||||||
pub unsafe fn from_raw(raw: &IoRaw<SIZE>) -> &Self {
|
} else {
|
||||||
// SAFETY: `Io` is a transparent wrapper around `IoRaw`.
|
false
|
||||||
unsafe { &*core::ptr::from_ref(raw).cast() }
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marker trait indicating that an I/O backend supports operations of a certain type.
|
||||||
|
///
|
||||||
|
/// Different I/O backends can implement this trait to expose only the operations they support.
|
||||||
|
///
|
||||||
|
/// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`,
|
||||||
|
/// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit
|
||||||
|
/// system might implement all four.
|
||||||
|
pub trait IoCapable<T> {}
|
||||||
|
|
||||||
|
/// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
|
||||||
|
/// can perform I/O operations on regions of memory.
|
||||||
|
///
|
||||||
|
/// This is an abstract representation to be implemented by arbitrary I/O
|
||||||
|
/// backends (e.g. MMIO, PCI config space, etc.).
|
||||||
|
///
|
||||||
|
/// The [`Io`] trait provides:
|
||||||
|
/// - Base address and size information
|
||||||
|
/// - Helper methods for offset validation and address calculation
|
||||||
|
/// - Fallible (runtime checked) accessors for different data widths
|
||||||
|
///
|
||||||
|
/// Which I/O methods are available depends on which [`IoCapable<T>`] traits
|
||||||
|
/// are implemented for the type.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) are typically
|
||||||
|
/// supported. For PCI configuration space, u8, u16, and u32 are supported but u64 is not.
|
||||||
|
pub trait Io {
|
||||||
|
/// Minimum usable size of this region.
|
||||||
|
const MIN_SIZE: usize;
|
||||||
|
|
||||||
/// Returns the base address of this mapping.
|
/// Returns the base address of this mapping.
|
||||||
#[inline]
|
fn addr(&self) -> usize;
|
||||||
pub fn addr(&self) -> usize {
|
|
||||||
self.0.addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the maximum size of this mapping.
|
/// Returns the maximum size of this mapping.
|
||||||
#[inline]
|
fn maxsize(&self) -> usize;
|
||||||
pub fn maxsize(&self) -> usize {
|
|
||||||
self.0.maxsize()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
const fn offset_valid<U>(offset: usize, size: usize) -> bool {
|
|
||||||
let type_size = core::mem::size_of::<U>();
|
|
||||||
if let Some(end) = offset.checked_add(type_size) {
|
|
||||||
end <= size && offset % type_size == 0
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Returns the absolute I/O address for a given `offset`,
|
||||||
|
/// performing runtime bound checks.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn io_addr<U>(&self, offset: usize) -> Result<usize> {
|
fn io_addr<U>(&self, offset: usize) -> Result<usize> {
|
||||||
if !Self::offset_valid::<U>(offset, self.maxsize()) {
|
if !offset_valid::<U>(offset, self.maxsize()) {
|
||||||
return Err(EINVAL);
|
return Err(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,50 +266,285 @@ impl<const SIZE: usize> Io<SIZE> {
|
||||||
self.addr().checked_add(offset).ok_or(EINVAL)
|
self.addr().checked_add(offset).ok_or(EINVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the absolute I/O address for a given `offset`,
|
||||||
|
/// performing compile-time bound checks.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn io_addr_assert<U>(&self, offset: usize) -> usize {
|
fn io_addr_assert<U>(&self, offset: usize) -> usize {
|
||||||
build_assert!(Self::offset_valid::<U>(offset, SIZE));
|
build_assert!(offset_valid::<U>(offset, Self::MIN_SIZE));
|
||||||
|
|
||||||
self.addr() + offset
|
self.addr() + offset
|
||||||
}
|
}
|
||||||
|
|
||||||
define_read!(read8, try_read8, readb -> u8);
|
/// Fallible 8-bit read with runtime bounds check.
|
||||||
define_read!(read16, try_read16, readw -> u16);
|
#[inline(always)]
|
||||||
define_read!(read32, try_read32, readl -> u32);
|
fn try_read8(&self, _offset: usize) -> Result<u8>
|
||||||
|
where
|
||||||
|
Self: IoCapable<u8>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 8-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 16-bit read with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_read16(&self, _offset: usize) -> Result<u16>
|
||||||
|
where
|
||||||
|
Self: IoCapable<u16>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 16-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 32-bit read with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_read32(&self, _offset: usize) -> Result<u32>
|
||||||
|
where
|
||||||
|
Self: IoCapable<u32>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 32-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 64-bit read with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_read64(&self, _offset: usize) -> Result<u64>
|
||||||
|
where
|
||||||
|
Self: IoCapable<u64>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 64-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 8-bit write with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_write8(&self, _value: u8, _offset: usize) -> Result
|
||||||
|
where
|
||||||
|
Self: IoCapable<u8>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 8-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 16-bit write with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_write16(&self, _value: u16, _offset: usize) -> Result
|
||||||
|
where
|
||||||
|
Self: IoCapable<u16>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 16-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 32-bit write with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_write32(&self, _value: u32, _offset: usize) -> Result
|
||||||
|
where
|
||||||
|
Self: IoCapable<u32>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 32-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fallible 64-bit write with runtime bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_write64(&self, _value: u64, _offset: usize) -> Result
|
||||||
|
where
|
||||||
|
Self: IoCapable<u64>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support fallible 64-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 8-bit read with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn read8(&self, _offset: usize) -> u8
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u8>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 8-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 16-bit read with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn read16(&self, _offset: usize) -> u16
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u16>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 16-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 32-bit read with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn read32(&self, _offset: usize) -> u32
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u32>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 32-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 64-bit read with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn read64(&self, _offset: usize) -> u64
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u64>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 64-bit read")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 8-bit write with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn write8(&self, _value: u8, _offset: usize)
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u8>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 8-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 16-bit write with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn write16(&self, _value: u16, _offset: usize)
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u16>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 16-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 32-bit write with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn write32(&self, _value: u32, _offset: usize)
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u32>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 32-bit write")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Infallible 64-bit write with compile-time bounds check.
|
||||||
|
#[inline(always)]
|
||||||
|
fn write64(&self, _value: u64, _offset: usize)
|
||||||
|
where
|
||||||
|
Self: IoKnownSize + IoCapable<u64>,
|
||||||
|
{
|
||||||
|
build_error!("Backend does not support infallible 64-bit write")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marker trait for types with a known size at compile time.
|
||||||
|
///
|
||||||
|
/// This trait is implemented by I/O backends that have a compile-time known size,
|
||||||
|
/// enabling the use of infallible I/O accessors with compile-time bounds checking.
|
||||||
|
///
|
||||||
|
/// Types implementing this trait can use the infallible methods in [`Io`] trait
|
||||||
|
/// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound.
|
||||||
|
pub trait IoKnownSize: Io {}
|
||||||
|
|
||||||
|
// MMIO regions support 8, 16, and 32-bit accesses.
|
||||||
|
impl<const SIZE: usize> IoCapable<u8> for Mmio<SIZE> {}
|
||||||
|
impl<const SIZE: usize> IoCapable<u16> for Mmio<SIZE> {}
|
||||||
|
impl<const SIZE: usize> IoCapable<u32> for Mmio<SIZE> {}
|
||||||
|
|
||||||
|
// MMIO regions on 64-bit systems also support 64-bit accesses.
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
impl<const SIZE: usize> IoCapable<u64> for Mmio<SIZE> {}
|
||||||
|
|
||||||
|
impl<const SIZE: usize> Io for Mmio<SIZE> {
|
||||||
|
const MIN_SIZE: usize = SIZE;
|
||||||
|
|
||||||
|
/// Returns the base address of this mapping.
|
||||||
|
#[inline]
|
||||||
|
fn addr(&self) -> usize {
|
||||||
|
self.0.addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the maximum size of this mapping.
|
||||||
|
#[inline]
|
||||||
|
fn maxsize(&self) -> usize {
|
||||||
|
self.0.maxsize()
|
||||||
|
}
|
||||||
|
|
||||||
|
define_read!(fallible, try_read8, readb -> u8);
|
||||||
|
define_read!(fallible, try_read16, readw -> u16);
|
||||||
|
define_read!(fallible, try_read32, readl -> u32);
|
||||||
define_read!(
|
define_read!(
|
||||||
|
fallible,
|
||||||
#[cfg(CONFIG_64BIT)]
|
#[cfg(CONFIG_64BIT)]
|
||||||
read64,
|
|
||||||
try_read64,
|
try_read64,
|
||||||
readq -> u64
|
readq -> u64
|
||||||
);
|
);
|
||||||
|
|
||||||
define_read!(read8_relaxed, try_read8_relaxed, readb_relaxed -> u8);
|
define_write!(fallible, try_write8, writeb <- u8);
|
||||||
define_read!(read16_relaxed, try_read16_relaxed, readw_relaxed -> u16);
|
define_write!(fallible, try_write16, writew <- u16);
|
||||||
define_read!(read32_relaxed, try_read32_relaxed, readl_relaxed -> u32);
|
define_write!(fallible, try_write32, writel <- u32);
|
||||||
define_read!(
|
|
||||||
#[cfg(CONFIG_64BIT)]
|
|
||||||
read64_relaxed,
|
|
||||||
try_read64_relaxed,
|
|
||||||
readq_relaxed -> u64
|
|
||||||
);
|
|
||||||
|
|
||||||
define_write!(write8, try_write8, writeb <- u8);
|
|
||||||
define_write!(write16, try_write16, writew <- u16);
|
|
||||||
define_write!(write32, try_write32, writel <- u32);
|
|
||||||
define_write!(
|
define_write!(
|
||||||
|
fallible,
|
||||||
#[cfg(CONFIG_64BIT)]
|
#[cfg(CONFIG_64BIT)]
|
||||||
write64,
|
|
||||||
try_write64,
|
try_write64,
|
||||||
writeq <- u64
|
writeq <- u64
|
||||||
);
|
);
|
||||||
|
|
||||||
define_write!(write8_relaxed, try_write8_relaxed, writeb_relaxed <- u8);
|
define_read!(infallible, read8, readb -> u8);
|
||||||
define_write!(write16_relaxed, try_write16_relaxed, writew_relaxed <- u16);
|
define_read!(infallible, read16, readw -> u16);
|
||||||
define_write!(write32_relaxed, try_write32_relaxed, writel_relaxed <- u32);
|
define_read!(infallible, read32, readl -> u32);
|
||||||
define_write!(
|
define_read!(
|
||||||
|
infallible,
|
||||||
#[cfg(CONFIG_64BIT)]
|
#[cfg(CONFIG_64BIT)]
|
||||||
write64_relaxed,
|
read64,
|
||||||
try_write64_relaxed,
|
readq -> u64
|
||||||
|
);
|
||||||
|
|
||||||
|
define_write!(infallible, write8, writeb <- u8);
|
||||||
|
define_write!(infallible, write16, writew <- u16);
|
||||||
|
define_write!(infallible, write32, writel <- u32);
|
||||||
|
define_write!(
|
||||||
|
infallible,
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
write64,
|
||||||
|
writeq <- u64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {}
|
||||||
|
|
||||||
|
impl<const SIZE: usize> Mmio<SIZE> {
|
||||||
|
/// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
|
||||||
|
/// `maxsize`.
|
||||||
|
pub unsafe fn from_raw(raw: &MmioRaw<SIZE>) -> &Self {
|
||||||
|
// SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`.
|
||||||
|
unsafe { &*core::ptr::from_ref(raw).cast() }
|
||||||
|
}
|
||||||
|
|
||||||
|
define_read!(infallible, pub read8_relaxed, readb_relaxed -> u8);
|
||||||
|
define_read!(infallible, pub read16_relaxed, readw_relaxed -> u16);
|
||||||
|
define_read!(infallible, pub read32_relaxed, readl_relaxed -> u32);
|
||||||
|
define_read!(
|
||||||
|
infallible,
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
pub read64_relaxed,
|
||||||
|
readq_relaxed -> u64
|
||||||
|
);
|
||||||
|
|
||||||
|
define_read!(fallible, pub try_read8_relaxed, readb_relaxed -> u8);
|
||||||
|
define_read!(fallible, pub try_read16_relaxed, readw_relaxed -> u16);
|
||||||
|
define_read!(fallible, pub try_read32_relaxed, readl_relaxed -> u32);
|
||||||
|
define_read!(
|
||||||
|
fallible,
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
pub try_read64_relaxed,
|
||||||
|
readq_relaxed -> u64
|
||||||
|
);
|
||||||
|
|
||||||
|
define_write!(infallible, pub write8_relaxed, writeb_relaxed <- u8);
|
||||||
|
define_write!(infallible, pub write16_relaxed, writew_relaxed <- u16);
|
||||||
|
define_write!(infallible, pub write32_relaxed, writel_relaxed <- u32);
|
||||||
|
define_write!(
|
||||||
|
infallible,
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
pub write64_relaxed,
|
||||||
|
writeq_relaxed <- u64
|
||||||
|
);
|
||||||
|
|
||||||
|
define_write!(fallible, pub try_write8_relaxed, writeb_relaxed <- u8);
|
||||||
|
define_write!(fallible, pub try_write16_relaxed, writew_relaxed <- u16);
|
||||||
|
define_write!(fallible, pub try_write32_relaxed, writel_relaxed <- u32);
|
||||||
|
define_write!(
|
||||||
|
fallible,
|
||||||
|
#[cfg(CONFIG_64BIT)]
|
||||||
|
pub try_write64_relaxed,
|
||||||
writeq_relaxed <- u64
|
writeq_relaxed <- u64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ use crate::{
|
||||||
Region,
|
Region,
|
||||||
Resource, //
|
Resource, //
|
||||||
},
|
},
|
||||||
Io,
|
Mmio,
|
||||||
IoRaw, //
|
MmioRaw, //
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
@ -212,7 +212,7 @@ impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
|
impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
|
||||||
type Target = Io<SIZE>;
|
type Target = Mmio<SIZE>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.iomem
|
&self.iomem
|
||||||
|
|
@ -226,10 +226,10 @@ impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
|
||||||
///
|
///
|
||||||
/// # Invariants
|
/// # Invariants
|
||||||
///
|
///
|
||||||
/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the
|
/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
|
||||||
/// start of the I/O memory mapped region.
|
/// start of the I/O memory mapped region.
|
||||||
pub struct IoMem<const SIZE: usize = 0> {
|
pub struct IoMem<const SIZE: usize = 0> {
|
||||||
io: IoRaw<SIZE>,
|
io: MmioRaw<SIZE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SIZE: usize> IoMem<SIZE> {
|
impl<const SIZE: usize> IoMem<SIZE> {
|
||||||
|
|
@ -264,7 +264,7 @@ impl<const SIZE: usize> IoMem<SIZE> {
|
||||||
return Err(ENOMEM);
|
return Err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
let io = IoRaw::new(addr as usize, size)?;
|
let io = MmioRaw::new(addr as usize, size)?;
|
||||||
let io = IoMem { io };
|
let io = IoMem { io };
|
||||||
|
|
||||||
Ok(io)
|
Ok(io)
|
||||||
|
|
@ -287,10 +287,10 @@ impl<const SIZE: usize> Drop for IoMem<SIZE> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
impl<const SIZE: usize> Deref for IoMem<SIZE> {
|
||||||
type Target = Io<SIZE>;
|
type Target = Mmio<SIZE>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
// SAFETY: Safe as by the invariant of `IoMem`.
|
// SAFETY: Safe as by the invariant of `IoMem`.
|
||||||
unsafe { Io::from_raw(&self.io) }
|
unsafe { Mmio::from_raw(&self.io) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,16 @@ use crate::{
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use kernel::io::{Io, poll::read_poll_timeout};
|
/// use kernel::io::{
|
||||||
|
/// Io,
|
||||||
|
/// Mmio,
|
||||||
|
/// poll::read_poll_timeout, //
|
||||||
|
/// };
|
||||||
/// use kernel::time::Delta;
|
/// use kernel::time::Delta;
|
||||||
///
|
///
|
||||||
/// const HW_READY: u16 = 0x01;
|
/// const HW_READY: u16 = 0x01;
|
||||||
///
|
///
|
||||||
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
|
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
|
||||||
/// read_poll_timeout(
|
/// read_poll_timeout(
|
||||||
/// // The `op` closure reads the value of a specific status register.
|
/// // The `op` closure reads the value of a specific status register.
|
||||||
/// || io.try_read16(0x1000),
|
/// || io.try_read16(0x1000),
|
||||||
|
|
@ -128,12 +132,16 @@ where
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use kernel::io::{poll::read_poll_timeout_atomic, Io};
|
/// use kernel::io::{
|
||||||
|
/// Io,
|
||||||
|
/// Mmio,
|
||||||
|
/// poll::read_poll_timeout_atomic, //
|
||||||
|
/// };
|
||||||
/// use kernel::time::Delta;
|
/// use kernel::time::Delta;
|
||||||
///
|
///
|
||||||
/// const HW_READY: u16 = 0x01;
|
/// const HW_READY: u16 = 0x01;
|
||||||
///
|
///
|
||||||
/// fn wait_for_hardware<const SIZE: usize>(io: &Io<SIZE>) -> Result {
|
/// fn wait_for_hardware<const SIZE: usize>(io: &Mmio<SIZE>) -> Result {
|
||||||
/// read_poll_timeout_atomic(
|
/// read_poll_timeout_atomic(
|
||||||
/// // The `op` closure reads the value of a specific status register.
|
/// // The `op` closure reads the value of a specific status register.
|
||||||
/// || io.try_read16(0x1000),
|
/// || io.try_read16(0x1000),
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ use crate::{
|
||||||
device,
|
device,
|
||||||
devres::Devres,
|
devres::Devres,
|
||||||
io::{
|
io::{
|
||||||
Io,
|
Mmio,
|
||||||
IoRaw, //
|
MmioRaw, //
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
sync::aref::ARef, //
|
sync::aref::ARef, //
|
||||||
|
|
@ -27,7 +27,7 @@ use core::ops::Deref;
|
||||||
/// memory mapped PCI BAR and its size.
|
/// memory mapped PCI BAR and its size.
|
||||||
pub struct Bar<const SIZE: usize = 0> {
|
pub struct Bar<const SIZE: usize = 0> {
|
||||||
pdev: ARef<Device>,
|
pdev: ARef<Device>,
|
||||||
io: IoRaw<SIZE>,
|
io: MmioRaw<SIZE>,
|
||||||
num: i32,
|
num: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl<const SIZE: usize> Bar<SIZE> {
|
||||||
return Err(ENOMEM);
|
return Err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
let io = match IoRaw::new(ioptr, len as usize) {
|
let io = match MmioRaw::new(ioptr, len as usize) {
|
||||||
Ok(io) => io,
|
Ok(io) => io,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
|
|
@ -117,11 +117,11 @@ impl<const SIZE: usize> Drop for Bar<SIZE> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SIZE: usize> Deref for Bar<SIZE> {
|
impl<const SIZE: usize> Deref for Bar<SIZE> {
|
||||||
type Target = Io<SIZE>;
|
type Target = Mmio<SIZE>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
|
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
|
||||||
unsafe { Io::from_raw(&self.io) }
|
unsafe { Mmio::from_raw(&self.io) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
use kernel::{
|
use kernel::{
|
||||||
device::Core,
|
device::Core,
|
||||||
devres::Devres,
|
devres::Devres,
|
||||||
|
io::Io,
|
||||||
pci,
|
pci,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
sync::aref::ARef, //
|
sync::aref::ARef, //
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue