#![no_std]
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr;
use zerocopy::{FromBytes, IntoBytes};
#[doc(inline)]
pub use regio_macro::offset;
pub trait IoBackend {
type Base;
type Addr;
type Mode: IoMode;
fn read_at(&self, addr: Self::Addr) -> Self::Base
where
Self::Mode: Readable;
fn write_at(&self, value: Self::Base, addr: Self::Addr)
where
Self::Mode: Writable;
}
pub trait IoMode {}
pub trait Readable: IoMode {}
pub trait Writable: IoMode {}
pub struct ReadOnly {}
impl IoMode for ReadOnly {}
impl Readable for ReadOnly {}
pub struct ReadWrite {}
impl IoMode for ReadWrite {}
impl Readable for ReadWrite {}
impl Writable for ReadWrite {}
pub struct WriteOnly {}
impl IoMode for WriteOnly {}
impl Writable for WriteOnly {}
pub trait Register: From<Self::Base> + Deref<Target = Self::Base> + Addr {
type Base: Copy;
#[inline]
fn read_from_at<Io>(io: &Io, addr: Self::Addr) -> Self
where
Self: UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Readable,
{
Self::from(io.read_at(addr))
}
#[inline]
fn read_from<Io>(io: &Io) -> Self
where
Self: FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Readable,
{
Self::from(io.read_at(Self::ADDR))
}
#[inline]
fn read_at(addr: Self::Addr) -> Self
where
Self: UnfixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Readable,
{
Self::from(Self::Io::default().read_at(addr))
}
#[inline]
fn read() -> Self
where
Self: FixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Readable,
{
Self::from(Self::Io::default().read_at(Self::ADDR))
}
#[inline]
fn write_to_at<Io>(self, io: &Io, addr: Self::Addr)
where
Self: UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Writable,
{
io.write_at(*self, addr)
}
#[inline]
fn write_to<Io>(self, io: &Io)
where
Self: FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Writable,
{
io.write_at(*self, Self::ADDR)
}
#[inline]
fn write_at(self, addr: Self::Addr)
where
Self: UnfixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Writable,
{
Self::Io::default().write_at(*self, addr)
}
#[inline]
fn write(self)
where
Self: FixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Writable,
{
Self::Io::default().write_at(*self, Self::ADDR)
}
#[inline]
fn modify_with_at<Io, F>(io: &Io, mutate: F, addr: Self::Addr)
where
Self: UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Readable + Writable,
F: FnOnce(&mut Self),
{
let mut value = Register::read_from_at(io, addr);
mutate(&mut value);
value.write_to_at(io, addr)
}
#[inline]
fn modify_with<Io, F>(io: &Io, mutate: F)
where
Self: FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
Io::Mode: Readable + Writable,
F: FnOnce(&mut Self),
{
let mut value = Register::read_from(io);
mutate(&mut value);
value.write_to(io)
}
#[inline]
fn modify_at<F>(mutate: F, addr: Self::Addr)
where
Self: UnfixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Readable + Writable,
F: FnOnce(&mut Self),
{
let mut value = Register::read_at(addr);
mutate(&mut value);
value.write_at(addr)
}
#[inline]
fn modify<F>(mutate: F)
where
Self: FixedAddr,
Self: FixedIo<Self::Base, Self::Addr>,
<<Self as FixedIo<Self::Base, Self::Addr>>::Io as IoBackend>::Mode: Readable + Writable,
F: FnOnce(&mut Self),
{
let mut value = Register::read();
mutate(&mut value);
value.write()
}
}
impl<T, Base> Register for T
where
T: From<Base> + Deref<Target = Base> + Addr,
Base: Copy,
{
type Base = Base;
}
pub trait Addr {
type Addr: Copy;
}
pub trait FixedAddr: Addr {
const ADDR: Self::Addr;
}
pub trait UnfixedAddr: Addr {}
pub trait FixedIo<Base, Addr> {
type Io: Default + IoBackend<Base = Base, Addr = Addr>;
}
#[derive(Clone, Copy, Debug)]
pub struct Offset(pub usize);
impl Deref for Offset {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
mod sealed {
pub trait Sealed {}
}
pub trait FitsIn<Access>: sealed::Sealed {
fn widen(self) -> Access;
fn truncate(access: Access) -> Self;
}
impl sealed::Sealed for u8 {}
impl sealed::Sealed for u16 {}
impl sealed::Sealed for u32 {}
impl sealed::Sealed for u64 {}
macro_rules! fits_in {
($narrow:ty, $wide:ty) => {
impl FitsIn<$wide> for $narrow {
fn widen(self) -> $wide {
self as $wide
}
fn truncate(wide: $wide) -> Self {
wide as Self
}
}
};
}
fits_in!(u8, u8);
fits_in!(u8, u16);
fits_in!(u8, u32);
fits_in!(u8, u64);
fits_in!(u16, u16);
fits_in!(u16, u32);
fits_in!(u16, u64);
fits_in!(u32, u32);
fits_in!(u32, u64);
fits_in!(u64, u64);
pub struct Mmio<Reg, Access = Reg>
where
Reg: FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
base: *mut Access,
size: usize,
phantom: PhantomData<Reg>,
}
impl<Reg, Access> Mmio<Reg, Access>
where
Reg: FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
pub fn new(base: usize, size: usize) -> Self {
Self {
base: ptr::without_provenance_mut(base),
size,
phantom: PhantomData {},
}
}
}
impl<Reg, Access> IoBackend for Mmio<Reg, Access>
where
Reg: FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
type Addr = Offset;
type Base = Reg;
type Mode = ReadWrite;
fn read_at(&self, offset: Offset) -> Reg {
debug_assert!(*offset < self.size);
unsafe {
let ptr = self.base.add(*offset);
Reg::truncate(ptr::read_volatile(ptr))
}
}
fn write_at(&self, value: Reg, offset: Offset) {
debug_assert!(*offset < self.size);
unsafe {
let ptr = self.base.add(*offset);
ptr::write_volatile(ptr, value.widen())
}
}
}