aboutsummaryrefslogtreecommitdiff
path: root/src/interrupt.rs
diff options
context:
space:
mode:
authorFelipe Balbi <[email protected]>2025-11-07 11:00:15 -0800
committerGitHub <[email protected]>2025-11-07 11:00:15 -0800
commit5632acec18cc5906b1625a8facf530db56c73300 (patch)
treeabf2897f4b2f9814069c64611896be2d42cf2ce8 /src/interrupt.rs
parent47e383545f4aac3bfaec0563429cc721540e665a (diff)
parent9590d94ee9ba016f65a13100c429fc56ffe58e40 (diff)
Merge pull request #1 from bogdan-petru/import/mcxa276-initial
feat(mcxa276): initial HAL import
Diffstat (limited to 'src/interrupt.rs')
-rw-r--r--src/interrupt.rs349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/interrupt.rs b/src/interrupt.rs
new file mode 100644
index 000000000..09d7acbef
--- /dev/null
+++ b/src/interrupt.rs
@@ -0,0 +1,349 @@
1//! Minimal interrupt helpers mirroring embassy-imxrt style for OS_EVENT and LPUART2.
2//! Type-level interrupt traits and bindings are provided by the
3//! `embassy_hal_internal::interrupt_mod!` macro via the generated module below.
4
5mod generated {
6 embassy_hal_internal::interrupt_mod!(OS_EVENT, LPUART2, RTC, ADC1);
7}
8
9use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
10
11pub use generated::interrupt::{typelevel, Priority};
12
13use crate::pac::Interrupt;
14
15/// Trait for configuring and controlling interrupts.
16///
17/// This trait provides a consistent interface for interrupt management across
18/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
19pub trait InterruptExt {
20 /// Clear any pending interrupt in NVIC.
21 fn unpend(&self);
22
23 /// Set NVIC priority for this interrupt.
24 fn set_priority(&self, priority: Priority);
25
26 /// Enable this interrupt in NVIC.
27 ///
28 /// # Safety
29 /// This function is unsafe because it can enable interrupts that may not be
30 /// properly configured, potentially leading to undefined behavior.
31 unsafe fn enable(&self);
32
33 /// Disable this interrupt in NVIC.
34 ///
35 /// # Safety
36 /// This function is unsafe because disabling interrupts may leave the system
37 /// in an inconsistent state if the interrupt was expected to fire.
38 unsafe fn disable(&self);
39
40 /// Check if the interrupt is pending in NVIC.
41 fn is_pending(&self) -> bool;
42}
43
44#[derive(Clone, Copy, Debug, Default)]
45pub struct DefaultHandlerSnapshot {
46 pub vector: u16,
47 pub count: u32,
48 pub cfsr: u32,
49 pub hfsr: u32,
50 pub stacked_pc: u32,
51 pub stacked_lr: u32,
52 pub stacked_sp: u32,
53}
54
55static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
56static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
57static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
58static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
59static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
60static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
61static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
62
63#[inline]
64pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
65 DefaultHandlerSnapshot {
66 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
67 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
68 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
69 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
70 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
71 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
72 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
73 }
74}
75
76#[inline]
77pub fn clear_default_handler_snapshot() {
78 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
79 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
80 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
81 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
82 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
83 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
84 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
85}
86
87/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
88pub struct OsEvent;
89pub const OS_EVENT: OsEvent = OsEvent;
90
91impl InterruptExt for OsEvent {
92 /// Clear any pending OS_EVENT in NVIC.
93 #[inline]
94 fn unpend(&self) {
95 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
96 }
97
98 /// Set NVIC priority for OS_EVENT.
99 #[inline]
100 fn set_priority(&self, priority: Priority) {
101 unsafe {
102 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
103 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
104 }
105 }
106
107 /// Enable OS_EVENT in NVIC.
108 #[inline]
109 unsafe fn enable(&self) {
110 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
111 }
112
113 /// Disable OS_EVENT in NVIC.
114 #[inline]
115 unsafe fn disable(&self) {
116 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
117 }
118
119 /// Check if OS_EVENT is pending in NVIC.
120 #[inline]
121 fn is_pending(&self) -> bool {
122 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
123 }
124}
125
126impl OsEvent {
127 /// Configure OS_EVENT interrupt for timer operation.
128 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
129 /// Also performs a software event to wake any pending WFE.
130 pub fn configure_for_timer(&self, priority: Priority) {
131 // Configure NVIC
132 self.unpend();
133 self.set_priority(priority);
134 unsafe {
135 self.enable();
136 }
137
138 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
139 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
140 unsafe {
141 cortex_m::interrupt::enable();
142 }
143
144 // Wake any executor WFE that might be sleeping when we armed the first deadline
145 cortex_m::asm::sev();
146 }
147}
148
149/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
150pub struct Lpuart2;
151pub const LPUART2: Lpuart2 = Lpuart2;
152
153impl InterruptExt for Lpuart2 {
154 /// Clear any pending LPUART2 in NVIC.
155 #[inline]
156 fn unpend(&self) {
157 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
158 }
159
160 /// Set NVIC priority for LPUART2.
161 #[inline]
162 fn set_priority(&self, priority: Priority) {
163 unsafe {
164 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
165 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
166 }
167 }
168
169 /// Enable LPUART2 in NVIC.
170 #[inline]
171 unsafe fn enable(&self) {
172 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
173 }
174
175 /// Disable LPUART2 in NVIC.
176 #[inline]
177 unsafe fn disable(&self) {
178 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
179 }
180
181 /// Check if LPUART2 is pending in NVIC.
182 #[inline]
183 fn is_pending(&self) -> bool {
184 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
185 }
186}
187
188impl Lpuart2 {
189 /// Configure LPUART2 interrupt for UART operation.
190 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
191 pub fn configure_for_uart(&self, priority: Priority) {
192 // Configure NVIC
193 self.unpend();
194 self.set_priority(priority);
195 unsafe {
196 self.enable();
197 }
198
199 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
200 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
201 unsafe {
202 cortex_m::interrupt::enable();
203 }
204 }
205
206 /// Install LPUART2 handler into the RAM vector table.
207 /// Safety: See `install_irq_handler`.
208 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
209 install_irq_handler(Interrupt::LPUART2, handler);
210 }
211}
212
213pub struct Rtc;
214pub const RTC: Rtc = Rtc;
215
216impl InterruptExt for Rtc {
217 /// Clear any pending RTC in NVIC.
218 #[inline]
219 fn unpend(&self) {
220 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
221 }
222
223 /// Set NVIC priority for RTC.
224 #[inline]
225 fn set_priority(&self, priority: Priority) {
226 unsafe {
227 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
228 nvic.set_priority(Interrupt::RTC, u8::from(priority));
229 }
230 }
231
232 /// Enable RTC in NVIC.
233 #[inline]
234 unsafe fn enable(&self) {
235 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
236 }
237
238 /// Disable RTC in NVIC.
239 #[inline]
240 unsafe fn disable(&self) {
241 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
242 }
243
244 /// Check if RTC is pending in NVIC.
245 #[inline]
246 fn is_pending(&self) -> bool {
247 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
248 }
249}
250
251pub struct Adc;
252pub const ADC1: Adc = Adc;
253
254impl InterruptExt for Adc {
255 /// Clear any pending ADC1 in NVIC.
256 #[inline]
257 fn unpend(&self) {
258 cortex_m::peripheral::NVIC::unpend(Interrupt::ADC1);
259 }
260
261 /// Set NVIC priority for ADC1.
262 #[inline]
263 fn set_priority(&self, priority: Priority) {
264 unsafe {
265 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
266 nvic.set_priority(Interrupt::ADC1, u8::from(priority));
267 }
268 }
269
270 /// Enable ADC1 in NVIC.
271 #[inline]
272 unsafe fn enable(&self) {
273 cortex_m::peripheral::NVIC::unmask(Interrupt::ADC1);
274 }
275
276 /// Disable ADC1 in NVIC.
277 #[inline]
278 unsafe fn disable(&self) {
279 cortex_m::peripheral::NVIC::mask(Interrupt::ADC1);
280 }
281
282 /// Check if ADC1 is pending in NVIC.
283 #[inline]
284 fn is_pending(&self) -> bool {
285 cortex_m::peripheral::NVIC::is_pending(Interrupt::ADC1)
286 }
287}
288
289/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
290/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
291/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
292pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
293 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
294}
295
296/// Install an interrupt handler into the current VTOR-based vector table.
297/// This writes the function pointer at index 16 + irq number.
298/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
299/// has the correct ABI and lifetime.
300pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
301 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
302 let idx = 16 + (irq as isize as usize);
303 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
304}
305
306impl OsEvent {
307 /// Convenience to install the OS_EVENT handler into the RAM vector table.
308 /// Safety: See `install_irq_handler`.
309 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
310 install_irq_handler(Interrupt::OS_EVENT, handler);
311 }
312}
313
314/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
315/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
316pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
317 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
318 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
319 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
320}
321
322/// Provide a conservative default IRQ handler that avoids wedging the system.
323/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
324/// don’t trap the core in an infinite WFI loop during bring-up.
325#[no_mangle]
326pub unsafe extern "C" fn DefaultHandler() {
327 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
328 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
329 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
330
331 let sp = cortex_m::register::msp::read();
332 let stacked = sp as *const u32;
333 // Stacked registers follow ARMv8-M procedure call standard order
334 let stacked_pc = unsafe { stacked.add(6).read() };
335 let stacked_lr = unsafe { stacked.add(5).read() };
336
337 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
338 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
339 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
340 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
341 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
342 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
343 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
344
345 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
346 // handler can fault if targeting the Secure bank. Just return.
347 cortex_m::asm::dsb();
348 cortex_m::asm::isb();
349}