#![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;
fn read_at(&self, addr: Self::Addr) -> Self::Base;
fn write_at(&self, value: Self::Base, addr: Self::Addr);
}
pub trait Readable: AccessMode {}
pub trait Writable: AccessMode {}
pub struct ReadOnly {}
impl AccessMode for ReadOnly {}
impl Readable for ReadOnly {}
pub struct ReadWrite {}
impl AccessMode for ReadWrite {}
impl Readable for ReadWrite {}
impl Writable for ReadWrite {}
pub struct WriteOnly {}
impl AccessMode for WriteOnly {}
impl Writable for WriteOnly {}
pub trait Spec {
type Base: Copy;
type Addr: Copy;
type Access: AccessMode;
}
pub trait FixedAddr: Spec {
const ADDR: Self::Addr;
}
pub trait UnfixedAddr: Spec {}
pub trait DefaultIo: Spec {
type Io: Default + IoBackend<Base = Self::Base, Addr = Self::Addr>;
}
pub trait Register: Spec + From<Self::Base> + Deref<Target = Self::Base> {
#[inline]
fn read_from_at<Io>(io: &Io, addr: Self::Addr) -> Self
where
Self: UnfixedAddr + Spec<Access: Readable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
Self::from(io.read_at(addr))
}
#[inline]
fn read_from<Io>(io: &Io) -> Self
where
Self: FixedAddr + Spec<Access: Readable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
Self::from(io.read_at(Self::ADDR))
}
#[inline]
fn read_at(addr: Self::Addr) -> Self
where
Self: UnfixedAddr + DefaultIo + Spec<Access: Readable>,
{
Self::from(Self::Io::default().read_at(addr))
}
#[inline]
fn read() -> Self
where
Self: FixedAddr + DefaultIo + Spec<Access: 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 + Spec<Access: Writable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
io.write_at(*self, addr)
}
#[inline]
fn write_to<Io>(self, io: &Io)
where
Self: FixedAddr + Spec<Access: Writable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
io.write_at(*self, Self::ADDR)
}
#[inline]
fn write_at(self, addr: Self::Addr)
where
Self: UnfixedAddr + DefaultIo + Spec<Access: Writable>,
{
Self::Io::default().write_at(*self, addr)
}
#[inline]
fn write(self)
where
Self: FixedAddr + DefaultIo + Spec<Access: 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 + Spec<Access: Readable + Writable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
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 + Spec<Access: Readable + Writable>,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
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 + DefaultIo + Spec<Access: 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 + DefaultIo + Spec<Access: Readable + Writable>,
F: FnOnce(&mut Self),
{
let mut value = Register::read();
mutate(&mut value);
value.write()
}
}
impl<T> Register for T where T: Spec + From<Self::Base> + Deref<Target = Self::Base> {}
#[derive(Clone, Copy, Debug)]
pub struct Offset(pub usize);
impl Deref for Offset {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
mod internal {
pub trait AccessMode {}
pub trait FitsIn<Access> {
fn widen(self) -> Access;
fn truncate(access: Access) -> Self;
}
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);
}
use internal::AccessMode;
pub struct Mmio<Reg, AccessType = Reg>
where
Reg: internal::FitsIn<AccessType>,
AccessType: FromBytes + IntoBytes,
{
base: *mut AccessType,
size: usize,
phantom: PhantomData<Reg>,
}
impl<Reg, AccessType> Mmio<Reg, AccessType>
where
Reg: internal::FitsIn<AccessType>,
AccessType: FromBytes + IntoBytes,
{
pub fn new(base: usize, size: usize) -> Self {
Self {
base: ptr::without_provenance_mut(base),
size,
phantom: PhantomData {},
}
}
}
impl<Reg, AccessType> IoBackend for Mmio<Reg, AccessType>
where
Reg: internal::FitsIn<AccessType>,
AccessType: FromBytes + IntoBytes,
{
type Addr = Offset;
type Base = Reg;
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())
}
}
}