1#![cfg_attr(target_os = "none", no_std)]
2
3use core::{fmt::Display, ops::Deref, ptr::NonNull, sync::atomic::Ordering};
4
5#[derive(thiserror::Error, Debug)]
6pub enum MapError {
7 #[error("Invalid MMIO address or size")]
8 Invalid,
9 #[error("Failed to allocate memory for MMIO mapping")]
10 NoMemory,
11 #[error("MMIO address is already in use")]
12 Busy,
13}
14
15pub trait MmioOp: Sync + Send + 'static {
16 fn ioremap(&self, addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError>;
17 fn iounmap(&self, mmio: &MmioRaw);
18}
19
20static mut MMIO_OP: Option<&'static dyn MmioOp> = None;
21static INIT: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
22
23pub fn init(mmio_op: &'static dyn MmioOp) {
24 if INIT
25 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
26 .is_err()
27 {
28 return;
29 }
30
31 unsafe {
32 MMIO_OP = Some(mmio_op);
33 }
34}
35
36pub unsafe fn ioremap_raw(addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError> {
40 let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
41 mmio_op.ioremap(addr, size)
42}
43
44pub unsafe fn iounmap(mmio: &MmioRaw) {
48 let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
49 mmio_op.iounmap(mmio);
50}
51
52pub fn ioremap(addr: MmioAddr, size: usize) -> Result<Mmio, MapError> {
53 let mmio = unsafe { ioremap_raw(addr, size)? };
54 Ok(Mmio(mmio))
55}
56
57#[derive(
59 Default,
60 derive_more::From,
61 derive_more::Into,
62 Clone,
63 Copy,
64 derive_more::Debug,
65 derive_more::Display,
66 PartialEq,
67 Eq,
68 PartialOrd,
69 Ord,
70 Hash,
71)]
72#[repr(transparent)]
73#[debug("PhysAddr({_0:#x})")]
74#[display("{_0:#x}")]
75pub struct MmioAddr(usize);
76
77impl MmioAddr {
78 pub fn as_usize(&self) -> usize {
79 self.0
80 }
81}
82
83impl From<u64> for MmioAddr {
84 fn from(value: u64) -> Self {
85 MmioAddr(value as usize)
86 }
87}
88
89#[derive(Debug, Clone)]
90pub struct MmioRaw {
91 phys: MmioAddr,
92 virt: NonNull<u8>,
93 size: usize,
94}
95
96impl MmioRaw {
97 pub unsafe fn new(phys: MmioAddr, virt: NonNull<u8>, size: usize) -> Self {
101 MmioRaw { phys, virt, size }
102 }
103
104 pub fn phys_addr(&self) -> MmioAddr {
105 self.phys
106 }
107
108 pub fn as_slice(&self) -> &[u8] {
109 unsafe { core::slice::from_raw_parts(self.virt.as_ptr(), self.size) }
110 }
111
112 pub fn as_ptr(&self) -> *mut u8 {
113 self.virt.as_ptr()
114 }
115
116 pub fn size(&self) -> usize {
117 self.size
118 }
119
120 pub fn read<T>(&self, offset: usize) -> T {
121 assert!(offset < self.size);
122 unsafe { self.virt.add(offset).cast::<T>().read_volatile() }
123 }
124
125 pub fn write<T>(&self, offset: usize, value: T) {
126 assert!(offset < self.size);
127 unsafe { self.virt.add(offset).cast::<T>().write_volatile(value) }
128 }
129}
130
131pub struct Mmio(MmioRaw);
132
133impl Deref for Mmio {
134 type Target = MmioRaw;
135
136 fn deref(&self) -> &Self::Target {
137 &self.0
138 }
139}
140
141impl Drop for Mmio {
142 fn drop(&mut self) {
143 let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
144 mmio_op.iounmap(self);
145 }
146}
147
148unsafe impl Send for MmioRaw {}
149unsafe impl Sync for MmioRaw {}
150
151impl Display for MmioRaw {
152 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 write!(
154 f,
155 "Mmio [{}, {:#x}) -> virt: {:#p}",
156 self.phys,
157 self.phys.0 + self.size,
158 self.virt
159 )
160 }
161}
162
163#[cfg(all(test, not(target_os = "none")))]
164mod tests {
165 use super::MmioRaw;
166
167 struct DummyMmioOp;
168 impl super::MmioOp for DummyMmioOp {
169 fn ioremap(&self, addr: super::MmioAddr, size: usize) -> Result<MmioRaw, super::Error> {
170 Ok(MmioRaw {
171 phys: addr,
172 virt: core::ptr::NonNull::dangling(),
173 size,
174 })
175 }
176
177 fn iounmap(&self, _mmio: &MmioRaw) {}
178 }
179
180 #[test]
181 fn test_mmio_new() {
182 super::init(&DummyMmioOp);
183
184 let addr = MmioRaw {
185 phys: super::MmioAddr(0x1000),
186 virt: core::ptr::NonNull::dangling(),
187 size: 0x100,
188 };
189 println!("Mmio address: {:?}", addr);
190 println!("Mmio address display: {}", addr);
191 }
192}