Skip to main content

strat9_bus_drivers/
mmio.rs

1use core::sync::atomic::Ordering;
2
3pub struct MmioRegion {
4    base: usize,
5    size: usize,
6}
7
8impl MmioRegion {
9    /// Creates a new instance.
10    pub const fn new() -> Self {
11        Self { base: 0, size: 0 }
12    }
13
14    /// Performs the init operation.
15    pub fn init(&mut self, base: usize, size: usize) {
16        self.base = base;
17        self.size = size;
18    }
19
20    /// Performs the base operation.
21    pub fn base(&self) -> usize {
22        self.base
23    }
24
25    /// Returns whether valid.
26    pub fn is_valid(&self) -> bool {
27        self.base != 0
28    }
29
30    /// Performs the checked addr operation.
31    fn checked_addr(&self, offset: usize, width: usize) -> usize {
32        let base = self.base();
33        assert!(base != 0);
34        let end = offset.checked_add(width).expect("mmio offset overflow");
35        assert!(end <= self.size);
36        base.checked_add(offset).expect("mmio address overflow")
37    }
38
39    /// Performs the read8 operation.
40    pub fn read8(&self, offset: usize) -> u8 {
41        let addr = self.checked_addr(offset, core::mem::size_of::<u8>());
42        // SAFETY: caller guarantees this address is a valid MMIO region
43        unsafe { core::ptr::read_volatile(addr as *const u8) }
44    }
45
46    /// Performs the read16 operation.
47    pub fn read16(&self, offset: usize) -> u16 {
48        let addr = self.checked_addr(offset, core::mem::size_of::<u16>());
49        // SAFETY: caller guarantees this address is a valid MMIO region
50        unsafe { core::ptr::read_volatile(addr as *const u16) }
51    }
52
53    /// Performs the read32 operation.
54    pub fn read32(&self, offset: usize) -> u32 {
55        let addr = self.checked_addr(offset, core::mem::size_of::<u32>());
56        // SAFETY: caller guarantees this address is a valid MMIO region
57        unsafe { core::ptr::read_volatile(addr as *const u32) }
58    }
59
60    /// Performs the read64 operation.
61    pub fn read64(&self, offset: usize) -> u64 {
62        let addr = self.checked_addr(offset, core::mem::size_of::<u64>());
63        // SAFETY: caller guarantees this address is a valid MMIO region
64        unsafe { core::ptr::read_volatile(addr as *const u64) }
65    }
66
67    /// Performs the write8 operation.
68    pub fn write8(&self, offset: usize, val: u8) {
69        let addr = self.checked_addr(offset, core::mem::size_of::<u8>());
70        // SAFETY: caller guarantees this address is a valid MMIO region
71        unsafe { core::ptr::write_volatile(addr as *mut u8, val) }
72    }
73
74    /// Performs the write16 operation.
75    pub fn write16(&self, offset: usize, val: u16) {
76        let addr = self.checked_addr(offset, core::mem::size_of::<u16>());
77        // SAFETY: caller guarantees this address is a valid MMIO region
78        unsafe { core::ptr::write_volatile(addr as *mut u16, val) }
79    }
80
81    /// Performs the write32 operation.
82    pub fn write32(&self, offset: usize, val: u32) {
83        let addr = self.checked_addr(offset, core::mem::size_of::<u32>());
84        // SAFETY: caller guarantees this address is a valid MMIO region
85        unsafe { core::ptr::write_volatile(addr as *mut u32, val) }
86    }
87
88    /// Performs the write64 operation.
89    pub fn write64(&self, offset: usize, val: u64) {
90        let addr = self.checked_addr(offset, core::mem::size_of::<u64>());
91        // SAFETY: caller guarantees this address is a valid MMIO region
92        unsafe { core::ptr::write_volatile(addr as *mut u64, val) }
93    }
94
95    /// Sets bits32.
96    pub fn set_bits32(&self, offset: usize, bits: u32) {
97        let val = self.read32(offset);
98        self.write32(offset, val | bits);
99    }
100
101    /// Performs the clear bits32 operation.
102    pub fn clear_bits32(&self, offset: usize, bits: u32) {
103        let val = self.read32(offset);
104        self.write32(offset, val & !bits);
105    }
106
107    /// Performs the modify32 operation.
108    pub fn modify32(&self, offset: usize, clear: u32, set: u32) {
109        let val = self.read32(offset);
110        self.write32(offset, (val & !clear) | set);
111    }
112
113    /// Reads field32.
114    pub fn read_field32(&self, offset: usize, mask: u32, shift: u32) -> u32 {
115        (self.read32(offset) & mask) >> shift
116    }
117
118    /// Writes field32.
119    pub fn write_field32(&self, offset: usize, mask: u32, shift: u32, value: u32) {
120        self.modify32(offset, mask, (value << shift) & mask);
121    }
122}
123
124// SAFETY: MmioRegion contains only an atomic base address and a size.
125// Access to the MMIO region itself requires the caller to ensure
126// the mapping is valid and not concurrently mutated.
127unsafe impl Send for MmioRegion {}
128unsafe impl Sync for MmioRegion {}
129
130/// Performs the memory barrier operation.
131pub fn memory_barrier() {
132    core::sync::atomic::fence(Ordering::SeqCst);
133}