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