aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/interrupt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/interrupt.rs')
-rw-r--r--embassy-mcxa/src/interrupt.rs537
1 files changed, 537 insertions, 0 deletions
diff --git a/embassy-mcxa/src/interrupt.rs b/embassy-mcxa/src/interrupt.rs
new file mode 100644
index 000000000..b662f7ee0
--- /dev/null
+++ b/embassy-mcxa/src/interrupt.rs
@@ -0,0 +1,537 @@
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
5// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
6// are complete prior to release.
7#![allow(clippy::missing_safety_doc)]
8
9mod generated {
10 #[rustfmt::skip]
11 embassy_hal_internal::interrupt_mod!(
12 ADC0,
13 ADC1,
14 ADC2,
15 ADC3,
16 DMA_CH0,
17 DMA_CH1,
18 DMA_CH2,
19 DMA_CH3,
20 DMA_CH4,
21 DMA_CH5,
22 DMA_CH6,
23 DMA_CH7,
24 GPIO0,
25 GPIO1,
26 GPIO2,
27 GPIO3,
28 GPIO4,
29 LPI2C0,
30 LPI2C1,
31 LPI2C2,
32 LPI2C3,
33 LPUART0,
34 LPUART1,
35 LPUART2,
36 LPUART3,
37 LPUART4,
38 LPUART5,
39 OS_EVENT,
40 RTC,
41 TRNG0
42 );
43}
44
45use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
46
47pub use generated::interrupt::{Priority, typelevel};
48
49use crate::pac::Interrupt;
50
51/// Trait for configuring and controlling interrupts.
52///
53/// This trait provides a consistent interface for interrupt management across
54/// different interrupt sources, similar to embassy-imxrt's InterruptExt.
55pub trait InterruptExt {
56 /// Clear any pending interrupt in NVIC.
57 fn unpend(&self);
58
59 /// Set NVIC priority for this interrupt.
60 fn set_priority(&self, priority: Priority);
61
62 /// Enable this interrupt in NVIC.
63 ///
64 /// # Safety
65 /// This function is unsafe because it can enable interrupts that may not be
66 /// properly configured, potentially leading to undefined behavior.
67 unsafe fn enable(&self);
68
69 /// Disable this interrupt in NVIC.
70 ///
71 /// # Safety
72 /// This function is unsafe because disabling interrupts may leave the system
73 /// in an inconsistent state if the interrupt was expected to fire.
74 unsafe fn disable(&self);
75
76 /// Check if the interrupt is pending in NVIC.
77 fn is_pending(&self) -> bool;
78}
79
80#[derive(Clone, Copy, Debug, Default)]
81pub struct DefaultHandlerSnapshot {
82 pub vector: u16,
83 pub count: u32,
84 pub cfsr: u32,
85 pub hfsr: u32,
86 pub stacked_pc: u32,
87 pub stacked_lr: u32,
88 pub stacked_sp: u32,
89}
90
91static LAST_DEFAULT_VECTOR: AtomicU16 = AtomicU16::new(0);
92static LAST_DEFAULT_COUNT: AtomicU32 = AtomicU32::new(0);
93static LAST_DEFAULT_CFSR: AtomicU32 = AtomicU32::new(0);
94static LAST_DEFAULT_HFSR: AtomicU32 = AtomicU32::new(0);
95static LAST_DEFAULT_PC: AtomicU32 = AtomicU32::new(0);
96static LAST_DEFAULT_LR: AtomicU32 = AtomicU32::new(0);
97static LAST_DEFAULT_SP: AtomicU32 = AtomicU32::new(0);
98
99#[inline]
100pub fn default_handler_snapshot() -> DefaultHandlerSnapshot {
101 DefaultHandlerSnapshot {
102 vector: LAST_DEFAULT_VECTOR.load(Ordering::Relaxed),
103 count: LAST_DEFAULT_COUNT.load(Ordering::Relaxed),
104 cfsr: LAST_DEFAULT_CFSR.load(Ordering::Relaxed),
105 hfsr: LAST_DEFAULT_HFSR.load(Ordering::Relaxed),
106 stacked_pc: LAST_DEFAULT_PC.load(Ordering::Relaxed),
107 stacked_lr: LAST_DEFAULT_LR.load(Ordering::Relaxed),
108 stacked_sp: LAST_DEFAULT_SP.load(Ordering::Relaxed),
109 }
110}
111
112#[inline]
113pub fn clear_default_handler_snapshot() {
114 LAST_DEFAULT_VECTOR.store(0, Ordering::Relaxed);
115 LAST_DEFAULT_COUNT.store(0, Ordering::Relaxed);
116 LAST_DEFAULT_CFSR.store(0, Ordering::Relaxed);
117 LAST_DEFAULT_HFSR.store(0, Ordering::Relaxed);
118 LAST_DEFAULT_PC.store(0, Ordering::Relaxed);
119 LAST_DEFAULT_LR.store(0, Ordering::Relaxed);
120 LAST_DEFAULT_SP.store(0, Ordering::Relaxed);
121}
122
123/// OS_EVENT interrupt helper with methods similar to embassy-imxrt's InterruptExt.
124pub struct OsEvent;
125pub const OS_EVENT: OsEvent = OsEvent;
126
127impl InterruptExt for OsEvent {
128 /// Clear any pending OS_EVENT in NVIC.
129 #[inline]
130 fn unpend(&self) {
131 cortex_m::peripheral::NVIC::unpend(Interrupt::OS_EVENT);
132 }
133
134 /// Set NVIC priority for OS_EVENT.
135 #[inline]
136 fn set_priority(&self, priority: Priority) {
137 unsafe {
138 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
139 nvic.set_priority(Interrupt::OS_EVENT, u8::from(priority));
140 }
141 }
142
143 /// Enable OS_EVENT in NVIC.
144 #[inline]
145 unsafe fn enable(&self) {
146 cortex_m::peripheral::NVIC::unmask(Interrupt::OS_EVENT);
147 }
148
149 /// Disable OS_EVENT in NVIC.
150 #[inline]
151 unsafe fn disable(&self) {
152 cortex_m::peripheral::NVIC::mask(Interrupt::OS_EVENT);
153 }
154
155 /// Check if OS_EVENT is pending in NVIC.
156 #[inline]
157 fn is_pending(&self) -> bool {
158 cortex_m::peripheral::NVIC::is_pending(Interrupt::OS_EVENT)
159 }
160}
161
162impl OsEvent {
163 /// Configure OS_EVENT interrupt for timer operation.
164 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
165 /// Also performs a software event to wake any pending WFE.
166 pub fn configure_for_timer(&self, priority: Priority) {
167 // Configure NVIC
168 self.unpend();
169 self.set_priority(priority);
170 unsafe {
171 self.enable();
172 }
173
174 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
175 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
176 unsafe {
177 cortex_m::interrupt::enable();
178 }
179
180 // Wake any executor WFE that might be sleeping when we armed the first deadline
181 cortex_m::asm::sev();
182 }
183}
184
185/// LPUART2 interrupt helper with methods similar to embassy-imxrt's InterruptExt.
186pub struct Lpuart2;
187pub const LPUART2: Lpuart2 = Lpuart2;
188
189impl InterruptExt for Lpuart2 {
190 /// Clear any pending LPUART2 in NVIC.
191 #[inline]
192 fn unpend(&self) {
193 cortex_m::peripheral::NVIC::unpend(Interrupt::LPUART2);
194 }
195
196 /// Set NVIC priority for LPUART2.
197 #[inline]
198 fn set_priority(&self, priority: Priority) {
199 unsafe {
200 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
201 nvic.set_priority(Interrupt::LPUART2, u8::from(priority));
202 }
203 }
204
205 /// Enable LPUART2 in NVIC.
206 #[inline]
207 unsafe fn enable(&self) {
208 cortex_m::peripheral::NVIC::unmask(Interrupt::LPUART2);
209 }
210
211 /// Disable LPUART2 in NVIC.
212 #[inline]
213 unsafe fn disable(&self) {
214 cortex_m::peripheral::NVIC::mask(Interrupt::LPUART2);
215 }
216
217 /// Check if LPUART2 is pending in NVIC.
218 #[inline]
219 fn is_pending(&self) -> bool {
220 cortex_m::peripheral::NVIC::is_pending(Interrupt::LPUART2)
221 }
222}
223
224impl Lpuart2 {
225 /// Configure LPUART2 interrupt for UART operation.
226 /// This sets up the NVIC priority, enables the interrupt, and ensures global interrupts are enabled.
227 pub fn configure_for_uart(&self, priority: Priority) {
228 // Configure NVIC
229 self.unpend();
230 self.set_priority(priority);
231 unsafe {
232 self.enable();
233 }
234
235 // Ensure global interrupts are enabled in no-reset scenarios (e.g., cargo run)
236 // Debuggers typically perform a reset which leaves PRIMASK=0; cargo run may not.
237 unsafe {
238 cortex_m::interrupt::enable();
239 }
240 }
241
242 /// Install LPUART2 handler into the RAM vector table.
243 /// Safety: See `install_irq_handler`.
244 pub unsafe fn install_handler(&self, handler: unsafe extern "C" fn()) {
245 install_irq_handler(Interrupt::LPUART2, handler);
246 }
247}
248
249pub struct Rtc;
250pub const RTC: Rtc = Rtc;
251
252impl InterruptExt for Rtc {
253 /// Clear any pending RTC in NVIC.
254 #[inline]
255 fn unpend(&self) {
256 cortex_m::peripheral::NVIC::unpend(Interrupt::RTC);
257 }
258
259 /// Set NVIC priority for RTC.
260 #[inline]
261 fn set_priority(&self, priority: Priority) {
262 unsafe {
263 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
264 nvic.set_priority(Interrupt::RTC, u8::from(priority));
265 }
266 }
267
268 /// Enable RTC in NVIC.
269 #[inline]
270 unsafe fn enable(&self) {
271 cortex_m::peripheral::NVIC::unmask(Interrupt::RTC);
272 }
273
274 /// Disable RTC in NVIC.
275 #[inline]
276 unsafe fn disable(&self) {
277 cortex_m::peripheral::NVIC::mask(Interrupt::RTC);
278 }
279
280 /// Check if RTC is pending in NVIC.
281 #[inline]
282 fn is_pending(&self) -> bool {
283 cortex_m::peripheral::NVIC::is_pending(Interrupt::RTC)
284 }
285}
286
287pub struct Gpio0;
288pub const GPIO0: Gpio0 = Gpio0;
289
290impl InterruptExt for Gpio0 {
291 /// Clear any pending GPIO0 in NVIC.
292 #[inline]
293 fn unpend(&self) {
294 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO0);
295 }
296
297 /// Set NVIC priority for GPIO0.
298 #[inline]
299 fn set_priority(&self, priority: Priority) {
300 unsafe {
301 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
302 nvic.set_priority(Interrupt::GPIO0, u8::from(priority));
303 }
304 }
305
306 /// Enable GPIO0 in NVIC.
307 #[inline]
308 unsafe fn enable(&self) {
309 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO0);
310 }
311
312 /// Disable GPIO0 in NVIC.
313 #[inline]
314 unsafe fn disable(&self) {
315 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO0);
316 }
317
318 /// Check if GPIO0 is pending in NVIC.
319 #[inline]
320 fn is_pending(&self) -> bool {
321 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO0)
322 }
323}
324
325pub struct Gpio1;
326pub const GPIO1: Gpio1 = Gpio1;
327
328impl InterruptExt for Gpio1 {
329 /// Clear any pending GPIO1 in NVIC.
330 #[inline]
331 fn unpend(&self) {
332 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO1);
333 }
334
335 /// Set NVIC priority for GPIO1.
336 #[inline]
337 fn set_priority(&self, priority: Priority) {
338 unsafe {
339 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
340 nvic.set_priority(Interrupt::GPIO1, u8::from(priority));
341 }
342 }
343
344 /// Enable GPIO1 in NVIC.
345 #[inline]
346 unsafe fn enable(&self) {
347 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO1);
348 }
349
350 /// Disable GPIO1 in NVIC.
351 #[inline]
352 unsafe fn disable(&self) {
353 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO1);
354 }
355
356 /// Check if GPIO1 is pending in NVIC.
357 #[inline]
358 fn is_pending(&self) -> bool {
359 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO1)
360 }
361}
362
363pub struct Gpio2;
364pub const GPIO2: Gpio2 = Gpio2;
365
366impl InterruptExt for Gpio2 {
367 /// Clear any pending GPIO2 in NVIC.
368 #[inline]
369 fn unpend(&self) {
370 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO2);
371 }
372
373 /// Set NVIC priority for GPIO2.
374 #[inline]
375 fn set_priority(&self, priority: Priority) {
376 unsafe {
377 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
378 nvic.set_priority(Interrupt::GPIO2, u8::from(priority));
379 }
380 }
381
382 /// Enable GPIO2 in NVIC.
383 #[inline]
384 unsafe fn enable(&self) {
385 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO2);
386 }
387
388 /// Disable GPIO2 in NVIC.
389 #[inline]
390 unsafe fn disable(&self) {
391 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO2);
392 }
393
394 /// Check if GPIO2 is pending in NVIC.
395 #[inline]
396 fn is_pending(&self) -> bool {
397 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO2)
398 }
399}
400
401pub struct Gpio3;
402pub const GPIO3: Gpio3 = Gpio3;
403
404impl InterruptExt for Gpio3 {
405 /// Clear any pending GPIO3 in NVIC.
406 #[inline]
407 fn unpend(&self) {
408 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO3);
409 }
410
411 /// Set NVIC priority for GPIO3.
412 #[inline]
413 fn set_priority(&self, priority: Priority) {
414 unsafe {
415 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
416 nvic.set_priority(Interrupt::GPIO3, u8::from(priority));
417 }
418 }
419
420 /// Enable GPIO3 in NVIC.
421 #[inline]
422 unsafe fn enable(&self) {
423 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO3);
424 }
425
426 /// Disable GPIO3 in NVIC.
427 #[inline]
428 unsafe fn disable(&self) {
429 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO3);
430 }
431
432 /// Check if GPIO3 is pending in NVIC.
433 #[inline]
434 fn is_pending(&self) -> bool {
435 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO3)
436 }
437}
438
439pub struct Gpio4;
440pub const GPIO4: Gpio4 = Gpio4;
441
442impl InterruptExt for Gpio4 {
443 /// Clear any pending GPIO4 in NVIC.
444 #[inline]
445 fn unpend(&self) {
446 cortex_m::peripheral::NVIC::unpend(Interrupt::GPIO4);
447 }
448
449 /// Set NVIC priority for GPIO4.
450 #[inline]
451 fn set_priority(&self, priority: Priority) {
452 unsafe {
453 let mut nvic = cortex_m::peripheral::Peripherals::steal().NVIC;
454 nvic.set_priority(Interrupt::GPIO4, u8::from(priority));
455 }
456 }
457
458 /// Enable GPIO4 in NVIC.
459 #[inline]
460 unsafe fn enable(&self) {
461 cortex_m::peripheral::NVIC::unmask(Interrupt::GPIO4);
462 }
463
464 /// Disable GPIO4 in NVIC.
465 #[inline]
466 unsafe fn disable(&self) {
467 cortex_m::peripheral::NVIC::mask(Interrupt::GPIO4);
468 }
469
470 /// Check if GPIO4 is pending in NVIC.
471 #[inline]
472 fn is_pending(&self) -> bool {
473 cortex_m::peripheral::NVIC::is_pending(Interrupt::GPIO4)
474 }
475}
476
477/// Set VTOR (Vector Table Offset) to a RAM-based vector table.
478/// Pass a pointer to the first word in the RAM table (stack pointer slot 0).
479/// Safety: Caller must ensure the RAM table is valid and aligned as required by the core.
480pub unsafe fn vtor_set_ram_vector_base(base: *const u32) {
481 core::ptr::write_volatile(0xE000_ED08 as *mut u32, base as u32);
482}
483
484/// Install an interrupt handler into the current VTOR-based vector table.
485/// This writes the function pointer at index 16 + irq number.
486/// Safety: Caller must ensure VTOR points at a writable RAM table and that `handler`
487/// has the correct ABI and lifetime.
488pub unsafe fn install_irq_handler(irq: Interrupt, handler: unsafe extern "C" fn()) {
489 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
490 let idx = 16 + (irq as isize as usize);
491 core::ptr::write_volatile(vtor_base.add(idx), handler as usize as u32);
492}
493
494impl OsEvent {
495 /// Convenience to install the OS_EVENT handler into the RAM vector table.
496 /// Safety: See `install_irq_handler`.
497 pub unsafe fn install_handler(&self, handler: extern "C" fn()) {
498 install_irq_handler(Interrupt::OS_EVENT, handler);
499 }
500}
501
502/// Install OS_EVENT handler by raw address. Useful to avoid fn pointer type mismatches.
503/// Safety: Caller must ensure the address is a valid `extern "C" fn()` handler.
504pub unsafe fn os_event_install_handler_raw(handler_addr: usize) {
505 let vtor_base = core::ptr::read_volatile(0xE000_ED08 as *const u32) as *mut u32;
506 let idx = 16 + (Interrupt::OS_EVENT as isize as usize);
507 core::ptr::write_volatile(vtor_base.add(idx), handler_addr as u32);
508}
509
510/// Provide a conservative default IRQ handler that avoids wedging the system.
511/// It clears all NVIC pending bits and returns, so spurious or reserved IRQs
512/// don’t trap the core in an infinite WFI loop during bring-up.
513#[no_mangle]
514pub unsafe extern "C" fn DefaultHandler() {
515 let active = core::ptr::read_volatile(0xE000_ED04 as *const u32) & 0x1FF;
516 let cfsr = core::ptr::read_volatile(0xE000_ED28 as *const u32);
517 let hfsr = core::ptr::read_volatile(0xE000_ED2C as *const u32);
518
519 let sp = cortex_m::register::msp::read();
520 let stacked = sp as *const u32;
521 // Stacked registers follow ARMv8-M procedure call standard order
522 let stacked_pc = unsafe { stacked.add(6).read() };
523 let stacked_lr = unsafe { stacked.add(5).read() };
524
525 LAST_DEFAULT_VECTOR.store(active as u16, Ordering::Relaxed);
526 LAST_DEFAULT_CFSR.store(cfsr, Ordering::Relaxed);
527 LAST_DEFAULT_HFSR.store(hfsr, Ordering::Relaxed);
528 LAST_DEFAULT_COUNT.fetch_add(1, Ordering::Relaxed);
529 LAST_DEFAULT_PC.store(stacked_pc, Ordering::Relaxed);
530 LAST_DEFAULT_LR.store(stacked_lr, Ordering::Relaxed);
531 LAST_DEFAULT_SP.store(sp, Ordering::Relaxed);
532
533 // Do nothing here: on some MCUs/TrustZone setups, writing NVIC from a spurious
534 // handler can fault if targeting the Secure bank. Just return.
535 cortex_m::asm::dsb();
536 cortex_m::asm::isb();
537}