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