1use core::marker::PhantomData;
6
7use super::{
8 AccessRestrictsTo, Accessible, IoHandle, LayoutOver, NoWrite, ReadHandle, Readable, Register,
9 Writable, WriteHandle,
10};
11
12#[derive(Debug)]
15pub struct Offset<Layout, Access> {
16 pub value: usize,
18 _marker: PhantomData<(Layout, Access)>,
19}
20
21impl<Layout, Access> Offset<Layout, Access> {
22 pub const fn new(value: usize) -> Self {
23 Self { value, _marker: PhantomData }
24 }
25}
26
27impl<Layout, Access> Clone for Offset<Layout, Access> {
33 fn clone(&self) -> Self {
34 Self { value: self.value, _marker: PhantomData }
35 }
36}
37
38impl<Layout, Access> Copy for Offset<Layout, Access> {}
39
40pub type Mmio<Layout, Base, Access> = Register<Layout, Access, MmioPtr<Base, Access>>;
42
43impl<Layout, Base, Access> Mmio<Layout, Base, Access>
44where
45 Layout: LayoutOver<Base>,
46 Base: Copy,
47 Access: Accessible,
48{
49 pub const fn new<InputAccess>(ptr: MmioPtr<Base, InputAccess>) -> Self
51 where
52 InputAccess: AccessRestrictsTo<Access>,
53 {
54 unsafe { Register::from_io(ptr.restrict()) }
58 }
59}
60
61#[derive(Debug)]
64pub struct MmioPtr<T, Access: Accessible>(*const T, PhantomData<Access>);
65
66impl<T, R> MmioPtr<T, (R, NoWrite)>
72where
73 (R, NoWrite): Accessible,
74{
75 pub const unsafe fn new(ptr: *const T) -> Self {
92 Self(ptr, PhantomData)
93 }
94
95 pub const fn as_ptr(self) -> *const T {
97 self.0
98 }
99}
100
101impl<T, Access: Writable> MmioPtr<T, Access> {
102 pub const unsafe fn new(ptr: *mut T) -> Self {
127 Self(ptr.cast(), PhantomData)
128 }
129
130 pub const fn as_ptr(self) -> *mut T {
132 self.0.cast_mut()
135 }
136}
137
138impl<T, Access: Accessible> MmioPtr<T, Access> {
139 pub const fn restrict<NewAccess>(self) -> MmioPtr<T, NewAccess>
141 where
142 NewAccess: Accessible,
143 Access: AccessRestrictsTo<NewAccess>,
144 {
145 MmioPtr(self.0, PhantomData)
146 }
147}
148
149impl<T, Access: Accessible> Clone for MmioPtr<T, Access> {
155 fn clone(&self) -> Self {
156 Self(self.0.clone(), PhantomData)
157 }
158}
159
160impl<T, Access: Accessible> Copy for MmioPtr<T, Access> {}
161
162impl<T, Access> IoHandle for MmioPtr<T, Access>
163where
164 T: Copy,
165 Access: Accessible,
166{
167 type Base = T;
168}
169
170macro_rules! impl_mmio_ptr_handle {
171 ($ty:ty, $read_fn:ident, $write_fn:ident) => {
172 impl<Access> ReadHandle for MmioPtr<$ty, Access>
173 where
174 Access: Readable,
175 {
176 #[inline(always)]
177 unsafe fn read_raw(&self) -> $ty {
178 unsafe { mmio_ptr::$read_fn(self.0) }
179 }
180 }
181
182 impl<Access> WriteHandle for MmioPtr<$ty, Access>
183 where
184 Access: Writable,
185 {
186 #[inline(always)]
187 unsafe fn write_raw(&self, value: $ty) {
188 unsafe { mmio_ptr::$write_fn(value, self.0.cast_mut()) }
191 }
192 }
193 };
194}
195
196impl_mmio_ptr_handle!(u8, read8, write8);
197impl_mmio_ptr_handle!(u16, read16, write16);
198impl_mmio_ptr_handle!(u32, read32, write32);
199
200#[cfg(target_pointer_width = "64")]
201impl_mmio_ptr_handle!(u64, read64, write64);
202
203unsafe impl<T, Access: Accessible> Send for MmioPtr<T, Access> {}
205
206unsafe impl<T, Access: Accessible> Sync for MmioPtr<T, Access> {}
208
209pub struct MmioBank<Base, MaxAccess: Accessible> {
213 base: MmioPtr<Base, MaxAccess>,
214 size_bytes: usize,
215}
216
217impl<Base, MaxAccess> MmioBank<Base, MaxAccess>
218where
219 Base: Copy,
220 MaxAccess: Accessible,
221{
222 pub const fn new(base: MmioPtr<Base, MaxAccess>, size_bytes: usize) -> Self {
224 Self { base, size_bytes }
225 }
226
227 pub const unsafe fn at<Layout, Access>(
239 &self,
240 offset: Offset<Layout, Access>,
241 ) -> Mmio<Layout, Base, Access>
242 where
243 Layout: LayoutOver<Base>,
244 Access: Accessible,
245 MaxAccess: AccessRestrictsTo<Access>,
246 {
247 assert!(offset.value.is_multiple_of(align_of::<Base>()));
248 assert!(size_of::<Base>() <= self.size_bytes);
249 assert!(offset.value <= self.size_bytes - size_of::<Base>());
250
251 let ptr = unsafe {
256 let offset_addr = self.base.0.byte_add(offset.value);
257 MmioPtr::<Base, MaxAccess>(offset_addr, PhantomData)
258 };
259 Mmio::new(ptr)
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use crate::{Ro, RwSafe, RwUnsafe, WoSafe, WoUnsafe};
267
268 #[test]
269 fn mmio_ptr_ro() {
270 let data = [10u32, 20u32];
271
272 let ptr = unsafe { MmioPtr::<u32, Ro>::new(data.as_ptr()) };
274
275 let bank = MmioBank::new(ptr, size_of_val(&data));
279
280 const OFFSET0: Offset<u32, Ro> = Offset::new(0);
281 let reg0 = unsafe { bank.at(OFFSET0) }; assert_eq!(reg0.read(), 10);
283
284 const OFFSET4: Offset<u32, Ro> = Offset::new(4);
285 let reg4 = unsafe { bank.at(OFFSET4) }; assert_eq!(reg4.read(), 20);
287 }
288
289 #[test]
290 fn mmio_ptr_wo_safe() {
291 let mut data = [10u32, 20u32];
292
293 let ptr = unsafe { MmioPtr::<u32, WoSafe>::new(data.as_mut_ptr()) };
295 let bank = MmioBank::new(ptr, size_of_val(&data));
296
297 const OFFSET0: Offset<u32, WoSafe> = Offset::new(0);
298 let reg0 = unsafe { bank.at(OFFSET0) }; assert_eq!(data[0], 10);
300 reg0.write(100u32);
301 assert_eq!(data[0], 100);
302
303 const OFFSET4: Offset<u32, WoSafe> = Offset::new(4);
304 let reg4 = unsafe { bank.at(OFFSET4) }; assert_eq!(data[1], 20);
306 reg4.write(200u32);
307 assert_eq!(data[1], 200);
308 }
309
310 #[test]
311 fn mmio_ptr_wo_unsafe() {
312 let mut data = [10u32, 20u32];
313
314 let ptr = unsafe { MmioPtr::<u32, WoUnsafe>::new(data.as_mut_ptr()) };
316 let bank = MmioBank::new(ptr, size_of_val(&data));
317
318 const OFFSET0: Offset<u32, WoUnsafe> = Offset::new(0);
319 let reg0 = unsafe { bank.at(OFFSET0) }; assert_eq!(data[0], 10);
321 unsafe { reg0.write(100u32) }; assert_eq!(data[0], 100);
323
324 const OFFSET4: Offset<u32, WoUnsafe> = Offset::new(4);
325 let reg4 = unsafe { bank.at(OFFSET4) }; assert_eq!(data[1], 20);
327 unsafe { reg4.write(200u32) }; assert_eq!(data[1], 200);
329 }
330
331 #[test]
332 fn mmio_ptr_rw_safe() {
333 let mut data = [10u32, 20u32];
334
335 let ptr = unsafe { MmioPtr::<u32, RwSafe>::new(data.as_mut_ptr()) };
337 let bank = MmioBank::new(ptr, size_of_val(&data));
338
339 const OFFSET0: Offset<u32, RwSafe> = Offset::new(0);
340 let reg0 = unsafe { bank.at(OFFSET0) }; assert_eq!(reg0.read(), 10);
342 reg0.write(100u32);
343 assert_eq!(reg0.read(), 100);
344 assert_eq!(data[0], 100);
345
346 const OFFSET4: Offset<u32, RwSafe> = Offset::new(4);
347 let reg4 = unsafe { bank.at(OFFSET4) }; assert_eq!(reg4.read(), 20);
349 reg4.write(200u32);
350 assert_eq!(reg4.read(), 200);
351 assert_eq!(data[1], 200);
352
353 reg4.modify(|val| {
354 assert_eq!(*val, 200);
355 *val = 300;
356 });
357 assert_eq!(reg4.read(), 300);
358 assert_eq!(data[1], 300);
359 }
360
361 #[test]
362 fn mmio_ptr_rw_unsafe() {
363 let mut data = [10u32, 20u32];
364
365 let ptr = unsafe { MmioPtr::<u32, RwUnsafe>::new(data.as_mut_ptr()) };
367 let bank = MmioBank::new(ptr, size_of_val(&data));
368
369 const OFFSET0: Offset<u32, RwUnsafe> = Offset::new(0);
370 let reg0 = unsafe { bank.at(OFFSET0) }; assert_eq!(reg0.read(), 10);
372 unsafe { reg0.write(100u32) }; assert_eq!(reg0.read(), 100);
374 assert_eq!(data[0], 100);
375
376 const OFFSET4: Offset<u32, RwUnsafe> = Offset::new(4);
377 let reg4 = unsafe { bank.at(OFFSET4) }; assert_eq!(reg4.read(), 20);
379 unsafe { reg4.write(200u32) }; assert_eq!(reg4.read(), 200);
381 assert_eq!(data[1], 200);
382
383 unsafe {
385 reg4.modify(|val| {
386 assert_eq!(*val, 200);
387 *val = 300;
388 });
389 }
390 assert_eq!(reg4.read(), 300);
391 assert_eq!(data[1], 300);
392 }
393
394 #[test]
395 fn mmio_bank_at_access_restrictions() {
396 let mut data = [0u32; 1];
397 let raw = data.as_mut_ptr();
398 let size = size_of_val(&data);
399
400 {
402 let base = unsafe { MmioPtr::<u32, Ro>::new(raw) };
403 let bank = MmioBank::new(base, size);
404 let _ = unsafe { bank.at(Offset::<u32, Ro>::new(0)) };
405 }
406
407 {
409 let base = unsafe { MmioPtr::<u32, WoSafe>::new(raw) };
410 let bank = MmioBank::new(base, size);
411 let _ = unsafe { bank.at(Offset::<u32, WoSafe>::new(0)) };
412 let _ = unsafe { bank.at(Offset::<u32, WoUnsafe>::new(0)) };
413 }
414
415 {
417 let base = unsafe { MmioPtr::<u32, WoUnsafe>::new(raw) };
418 let bank = MmioBank::new(base, size);
419 let _ = unsafe { bank.at(Offset::<u32, WoUnsafe>::new(0)) };
420 }
421
422 {
424 let base = unsafe { MmioPtr::<u32, RwSafe>::new(raw) };
425 let bank = MmioBank::new(base, size);
426 let _ = unsafe { bank.at(Offset::<u32, RwSafe>::new(0)) };
427 let _ = unsafe { bank.at(Offset::<u32, RwUnsafe>::new(0)) };
428 let _ = unsafe { bank.at(Offset::<u32, WoSafe>::new(0)) };
429 let _ = unsafe { bank.at(Offset::<u32, WoUnsafe>::new(0)) };
430 let _ = unsafe { bank.at(Offset::<u32, Ro>::new(0)) };
431 }
432
433 {
435 let base = unsafe { MmioPtr::<u32, RwUnsafe>::new(raw) };
436 let bank = MmioBank::new(base, size);
437 let _ = unsafe { bank.at(Offset::<u32, RwUnsafe>::new(0)) };
438 let _ = unsafe { bank.at(Offset::<u32, WoUnsafe>::new(0)) };
439 let _ = unsafe { bank.at(Offset::<u32, Ro>::new(0)) };
440 }
441 }
442}