esp_hal/interrupt/
xtensa.rs

1//! Interrupt handling
2
3use xtensa_lx::interrupt;
4#[cfg(esp32)]
5pub(crate) use xtensa_lx::interrupt::free;
6use xtensa_lx_rt::exception::Context;
7
8pub use self::vectored::*;
9use super::InterruptStatus;
10use crate::{pac, peripherals::Interrupt, system::Cpu};
11
12/// Interrupt Error
13#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub enum Error {
16    /// The given interrupt is not a valid interrupt
17    InvalidInterrupt,
18    /// The CPU interrupt is a reserved interrupt
19    CpuInterruptReserved,
20}
21
22/// Enumeration of available CPU interrupts
23///
24/// It's possible to create one handler per priority level. (e.g
25/// `level1_interrupt`)
26#[derive(Debug, Copy, Clone)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28#[repr(u32)]
29pub enum CpuInterrupt {
30    /// Level-triggered interrupt with priority 1.
31    Interrupt0LevelPriority1 = 0,
32    /// Level-triggered interrupt with priority 1.
33    Interrupt1LevelPriority1,
34    /// Level-triggered interrupt with priority 1.
35    Interrupt2LevelPriority1,
36    /// Level-triggered interrupt with priority 1.
37    Interrupt3LevelPriority1,
38    /// Level-triggered interrupt with priority 1.
39    Interrupt4LevelPriority1,
40    /// Level-triggered interrupt with priority 1.
41    Interrupt5LevelPriority1,
42    /// Timer 0 interrupt with priority 1.
43    Interrupt6Timer0Priority1,
44    /// Software-triggered interrupt with priority 1.
45    Interrupt7SoftwarePriority1,
46    /// Level-triggered interrupt with priority 1.
47    Interrupt8LevelPriority1,
48    /// Level-triggered interrupt with priority 1.
49    Interrupt9LevelPriority1,
50    /// Edge-triggered interrupt with priority 1.
51    Interrupt10EdgePriority1,
52    /// Profiling-related interrupt with priority 3.
53    Interrupt11ProfilingPriority3,
54    /// Level-triggered interrupt with priority 1.
55    Interrupt12LevelPriority1,
56    /// Level-triggered interrupt with priority 1.
57    Interrupt13LevelPriority1,
58    /// Non-maskable interrupt (NMI) with priority 7.
59    Interrupt14NmiPriority7,
60    /// Timer 1 interrupt with priority 3.
61    Interrupt15Timer1Priority3,
62    /// Timer 2 interrupt with priority 5.
63    Interrupt16Timer2Priority5,
64    /// Level-triggered interrupt with priority 1.
65    Interrupt17LevelPriority1,
66    /// Level-triggered interrupt with priority 1.
67    Interrupt18LevelPriority1,
68    /// Level-triggered interrupt with priority 2.
69    Interrupt19LevelPriority2,
70    /// Level-triggered interrupt with priority 2.
71    Interrupt20LevelPriority2,
72    /// Level-triggered interrupt with priority 2.
73    Interrupt21LevelPriority2,
74    /// Edge-triggered interrupt with priority 3.
75    Interrupt22EdgePriority3,
76    /// Level-triggered interrupt with priority 3.
77    Interrupt23LevelPriority3,
78    /// Level-triggered interrupt with priority 4.
79    Interrupt24LevelPriority4,
80    /// Level-triggered interrupt with priority 4.
81    Interrupt25LevelPriority4,
82    /// Level-triggered interrupt with priority 5.
83    Interrupt26LevelPriority5,
84    /// Level-triggered interrupt with priority 3.
85    Interrupt27LevelPriority3,
86    /// Edge-triggered interrupt with priority 4.
87    Interrupt28EdgePriority4,
88    /// Software-triggered interrupt with priority 3.
89    Interrupt29SoftwarePriority3,
90    /// Edge-triggered interrupt with priority 4.
91    Interrupt30EdgePriority4,
92    /// Edge-triggered interrupt with priority 5.
93    Interrupt31EdgePriority5,
94}
95
96impl CpuInterrupt {
97    fn from_u32(n: u32) -> Option<Self> {
98        if n < 32 {
99            Some(unsafe { core::mem::transmute::<u32, Self>(n) })
100        } else {
101            None
102        }
103    }
104
105    fn is_internal(self) -> bool {
106        matches!(
107            self,
108            Self::Interrupt6Timer0Priority1
109                | Self::Interrupt7SoftwarePriority1
110                | Self::Interrupt11ProfilingPriority3
111                | Self::Interrupt15Timer1Priority3
112                | Self::Interrupt16Timer2Priority5
113                | Self::Interrupt29SoftwarePriority3
114        )
115    }
116
117    fn is_peripheral(self) -> bool {
118        !self.is_internal()
119    }
120}
121
122/// The interrupts reserved by the HAL
123#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
124pub static RESERVED_INTERRUPTS: &[usize] = &[
125    CpuInterrupt::Interrupt1LevelPriority1 as _,
126    CpuInterrupt::Interrupt19LevelPriority2 as _,
127    CpuInterrupt::Interrupt23LevelPriority3 as _,
128    CpuInterrupt::Interrupt10EdgePriority1 as _,
129    CpuInterrupt::Interrupt22EdgePriority3 as _,
130];
131
132pub(crate) fn setup_interrupts() {
133    // disable all known interrupts
134    // at least after the 2nd stage bootloader there are some interrupts enabled
135    // (e.g. UART)
136    for peripheral_interrupt in 0..255 {
137        Interrupt::try_from(peripheral_interrupt)
138            .map(|intr| {
139                #[cfg(multi_core)]
140                disable(Cpu::AppCpu, intr);
141                disable(Cpu::ProCpu, intr);
142            })
143            .ok();
144    }
145}
146
147/// Enable an interrupt by directly binding it to a available CPU interrupt
148///
149/// Unless you are sure, you most likely want to use [`enable`] instead.
150///
151/// Trying using a reserved interrupt from [`RESERVED_INTERRUPTS`] will return
152/// an error.
153pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
154    if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
155        return Err(Error::CpuInterruptReserved);
156    }
157    unsafe {
158        map(Cpu::current(), interrupt, cpu_interrupt);
159
160        xtensa_lx::interrupt::enable_mask(
161            xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
162        );
163    }
164    Ok(())
165}
166
167/// Assign a peripheral interrupt to an CPU interrupt
168///
169/// Note: this only maps the interrupt to the CPU interrupt. The CPU interrupt
170/// still needs to be enabled afterwards
171///
172/// # Safety
173///
174/// Do not use CPU interrupts in the [`RESERVED_INTERRUPTS`].
175pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
176    let interrupt_number = interrupt as isize;
177    let cpu_interrupt_number = which as isize;
178    unsafe {
179        let intr_map_base = match core {
180            Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
181            #[cfg(multi_core)]
182            Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
183        };
184        intr_map_base
185            .offset(interrupt_number)
186            .write_volatile(cpu_interrupt_number as u32);
187    }
188}
189
190/// Get cpu interrupt assigned to peripheral interrupt
191pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
192    let interrupt_number = interrupt as isize;
193
194    let intr_map_base = match cpu {
195        Cpu::ProCpu => unsafe { (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr() },
196        #[cfg(multi_core)]
197        Cpu::AppCpu => unsafe { (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr() },
198    };
199    let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
200    let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?;
201
202    if cpu_intr.is_peripheral() {
203        Some(cpu_intr)
204    } else {
205        None
206    }
207}
208
209/// Disable the given peripheral interrupt
210pub fn disable(core: Cpu, interrupt: Interrupt) {
211    unsafe {
212        let interrupt_number = interrupt as isize;
213        let intr_map_base = match core {
214            Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
215            #[cfg(multi_core)]
216            Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
217        };
218        // To disable an interrupt, map it to a CPU peripheral interrupt
219        intr_map_base
220            .offset(interrupt_number)
221            .write_volatile(CpuInterrupt::Interrupt16Timer2Priority5 as _);
222    }
223}
224
225/// Clear the given CPU interrupt
226pub fn clear(_core: Cpu, which: CpuInterrupt) {
227    unsafe {
228        xtensa_lx::interrupt::clear(1 << which as u32);
229    }
230}
231
232/// Get status of peripheral interrupts
233#[cfg(large_intr_status)]
234pub fn status(core: Cpu) -> InterruptStatus {
235    unsafe {
236        match core {
237            Cpu::ProCpu => InterruptStatus::from(
238                (*core0_interrupt_peripheral())
239                    .pro_intr_status_0()
240                    .read()
241                    .bits(),
242                (*core0_interrupt_peripheral())
243                    .pro_intr_status_1()
244                    .read()
245                    .bits(),
246                (*core0_interrupt_peripheral())
247                    .pro_intr_status_2()
248                    .read()
249                    .bits(),
250            ),
251            #[cfg(multi_core)]
252            Cpu::AppCpu => InterruptStatus::from(
253                (*core1_interrupt_peripheral())
254                    .app_intr_status_0()
255                    .read()
256                    .bits(),
257                (*core1_interrupt_peripheral())
258                    .app_intr_status_1()
259                    .read()
260                    .bits(),
261                (*core1_interrupt_peripheral())
262                    .app_intr_status_2()
263                    .read()
264                    .bits(),
265            ),
266        }
267    }
268}
269
270/// Get status of peripheral interrupts
271#[cfg(very_large_intr_status)]
272pub fn status(core: Cpu) -> InterruptStatus {
273    unsafe {
274        match core {
275            Cpu::ProCpu => InterruptStatus::from(
276                (*core0_interrupt_peripheral())
277                    .pro_intr_status_0()
278                    .read()
279                    .bits(),
280                (*core0_interrupt_peripheral())
281                    .pro_intr_status_1()
282                    .read()
283                    .bits(),
284                (*core0_interrupt_peripheral())
285                    .pro_intr_status_2()
286                    .read()
287                    .bits(),
288                (*core0_interrupt_peripheral())
289                    .pro_intr_status_3()
290                    .read()
291                    .bits(),
292            ),
293            #[cfg(multi_core)]
294            Cpu::AppCpu => InterruptStatus::from(
295                (*core1_interrupt_peripheral())
296                    .app_intr_status_0()
297                    .read()
298                    .bits(),
299                (*core1_interrupt_peripheral())
300                    .app_intr_status_1()
301                    .read()
302                    .bits(),
303                (*core1_interrupt_peripheral())
304                    .app_intr_status_2()
305                    .read()
306                    .bits(),
307                (*core1_interrupt_peripheral())
308                    .app_intr_status_3()
309                    .read()
310                    .bits(),
311            ),
312        }
313    }
314}
315
316#[cfg(esp32)]
317unsafe fn core0_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
318    pac::DPORT::PTR
319}
320
321#[cfg(esp32)]
322unsafe fn core1_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
323    pac::DPORT::PTR
324}
325
326#[cfg(any(esp32s2, esp32s3))]
327unsafe fn core0_interrupt_peripheral() -> *const crate::pac::interrupt_core0::RegisterBlock {
328    pac::INTERRUPT_CORE0::PTR
329}
330
331#[cfg(esp32s3)]
332unsafe fn core1_interrupt_peripheral() -> *const crate::pac::interrupt_core1::RegisterBlock {
333    pac::INTERRUPT_CORE1::PTR
334}
335
336/// Get the current run level (the level below which interrupts are masked).
337pub fn current_runlevel() -> Priority {
338    let ps: u32;
339    unsafe { core::arch::asm!("rsr.ps {0}", out(reg) ps) };
340
341    let prev_interrupt_priority = ps as u8 & 0x0F;
342
343    unwrap!(Priority::try_from(prev_interrupt_priority))
344}
345
346/// Changes the current run level (the level below which interrupts are
347/// masked), and returns the previous run level.
348///
349/// # Safety
350///
351/// This function must only be used to raise the runlevel and to restore it
352/// to a previous value. It must not be used to arbitrarily lower the
353/// runlevel.
354pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
355    let token: u32;
356    unsafe {
357        match level {
358            Priority::None => core::arch::asm!("rsil {0}, 0", out(reg) token),
359            Priority::Priority1 => core::arch::asm!("rsil {0}, 1", out(reg) token),
360            Priority::Priority2 => core::arch::asm!("rsil {0}, 2", out(reg) token),
361            Priority::Priority3 => core::arch::asm!("rsil {0}, 3", out(reg) token),
362        };
363    }
364
365    let prev_interrupt_priority = token as u8 & 0x0F;
366
367    unwrap!(Priority::try_from(prev_interrupt_priority))
368}
369
370mod vectored {
371    use procmacros::ram;
372
373    use super::*;
374
375    /// Interrupt priority levels.
376    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
377    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
378    #[repr(u8)]
379    pub enum Priority {
380        /// No priority.
381        None = 0,
382        /// Priority level 1.
383        Priority1,
384        /// Priority level 2.
385        Priority2,
386        /// Priority level 3.
387        Priority3,
388    }
389
390    impl Priority {
391        /// Maximum interrupt priority
392        pub const fn max() -> Priority {
393            Priority::Priority3
394        }
395
396        /// Minimum interrupt priority
397        pub const fn min() -> Priority {
398            Priority::Priority1
399        }
400    }
401
402    impl TryFrom<u32> for Priority {
403        type Error = Error;
404
405        fn try_from(value: u32) -> Result<Self, Self::Error> {
406            match value {
407                0 => Ok(Priority::None),
408                1 => Ok(Priority::Priority1),
409                2 => Ok(Priority::Priority2),
410                3 => Ok(Priority::Priority3),
411                _ => Err(Error::InvalidInterrupt),
412            }
413        }
414    }
415
416    impl TryFrom<u8> for Priority {
417        type Error = Error;
418
419        fn try_from(value: u8) -> Result<Self, Self::Error> {
420            Self::try_from(value as u32)
421        }
422    }
423
424    impl CpuInterrupt {
425        #[inline]
426        fn level(&self) -> Priority {
427            match self {
428                CpuInterrupt::Interrupt0LevelPriority1
429                | CpuInterrupt::Interrupt1LevelPriority1
430                | CpuInterrupt::Interrupt2LevelPriority1
431                | CpuInterrupt::Interrupt3LevelPriority1
432                | CpuInterrupt::Interrupt4LevelPriority1
433                | CpuInterrupt::Interrupt5LevelPriority1
434                | CpuInterrupt::Interrupt6Timer0Priority1
435                | CpuInterrupt::Interrupt7SoftwarePriority1
436                | CpuInterrupt::Interrupt8LevelPriority1
437                | CpuInterrupt::Interrupt9LevelPriority1
438                | CpuInterrupt::Interrupt10EdgePriority1
439                | CpuInterrupt::Interrupt12LevelPriority1
440                | CpuInterrupt::Interrupt13LevelPriority1
441                | CpuInterrupt::Interrupt17LevelPriority1
442                | CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
443
444                CpuInterrupt::Interrupt19LevelPriority2
445                | CpuInterrupt::Interrupt20LevelPriority2
446                | CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
447
448                CpuInterrupt::Interrupt11ProfilingPriority3
449                | CpuInterrupt::Interrupt15Timer1Priority3
450                | CpuInterrupt::Interrupt22EdgePriority3
451                | CpuInterrupt::Interrupt27LevelPriority3
452                | CpuInterrupt::Interrupt29SoftwarePriority3
453                | CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
454
455                // we direct these to None because we do not support interrupts at this level
456                // through Rust
457                CpuInterrupt::Interrupt24LevelPriority4
458                | CpuInterrupt::Interrupt25LevelPriority4
459                | CpuInterrupt::Interrupt28EdgePriority4
460                | CpuInterrupt::Interrupt30EdgePriority4
461                | CpuInterrupt::Interrupt31EdgePriority5
462                | CpuInterrupt::Interrupt16Timer2Priority5
463                | CpuInterrupt::Interrupt26LevelPriority5
464                | CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
465            }
466        }
467    }
468
469    /// Get the interrupts configured for the core
470    #[inline(always)]
471    fn configured_interrupts(core: Cpu, status: InterruptStatus, level: u32) -> InterruptStatus {
472        unsafe {
473            let intr_map_base = match core {
474                Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(),
475                #[cfg(multi_core)]
476                Cpu::AppCpu => (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr(),
477            };
478
479            let mut res = InterruptStatus::empty();
480
481            for interrupt_nr in status.iterator() {
482                let i = interrupt_nr as isize;
483                let cpu_interrupt = intr_map_base.offset(i).read_volatile();
484                // safety: cast is safe because of repr(u32)
485                let cpu_interrupt: CpuInterrupt =
486                    core::mem::transmute::<u32, CpuInterrupt>(cpu_interrupt);
487                let int_level = cpu_interrupt.level() as u8 as u32;
488
489                if int_level == level {
490                    res.set(interrupt_nr);
491                }
492            }
493
494            res
495        }
496    }
497
498    /// Enable the given peripheral interrupt
499    pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
500        enable_on_cpu(Cpu::current(), interrupt, level)
501    }
502
503    pub(crate) fn enable_on_cpu(
504        cpu: Cpu,
505        interrupt: Interrupt,
506        level: Priority,
507    ) -> Result<(), Error> {
508        let cpu_interrupt =
509            interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
510
511        unsafe {
512            map(cpu, interrupt, cpu_interrupt);
513
514            xtensa_lx::interrupt::enable_mask(
515                xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
516            );
517        }
518        Ok(())
519    }
520
521    /// Binds the given interrupt to the given handler.
522    ///
523    /// # Safety
524    ///
525    /// This will replace any previously bound interrupt handler
526    pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
527        let ptr = unsafe {
528            &pac::__INTERRUPTS[interrupt as usize]._handler as *const _
529                as *mut unsafe extern "C" fn()
530        };
531        unsafe {
532            ptr.write_volatile(handler);
533        }
534    }
535
536    /// Returns the currently bound interrupt handler.
537    pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
538        unsafe {
539            let addr = pac::__INTERRUPTS[interrupt as usize]._handler;
540            if addr as usize == 0 {
541                return None;
542            }
543            Some(addr)
544        }
545    }
546
547    fn interrupt_level_to_cpu_interrupt(
548        level: Priority,
549        is_edge: bool,
550    ) -> Result<CpuInterrupt, Error> {
551        Ok(if is_edge {
552            match level {
553                Priority::None => return Err(Error::InvalidInterrupt),
554                Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
555                Priority::Priority2 => return Err(Error::InvalidInterrupt),
556                Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
557            }
558        } else {
559            match level {
560                Priority::None => return Err(Error::InvalidInterrupt),
561                Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
562                Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
563                Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
564            }
565        })
566    }
567
568    // TODO use CpuInterrupt::LevelX.mask() // TODO make it const
569    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
570    static CPU_INTERRUPT_LEVELS: [u32; 8] = [
571        0b_0000_0000_0000_0000_0000_0000_0000_0000, // Dummy level 0
572        0b_0000_0000_0000_0110_0011_0111_1111_1111, // Level_1
573        0b_0000_0000_0011_1000_0000_0000_0000_0000, // Level 2
574        0b_0010_1000_1100_0000_1000_1000_0000_0000, // Level 3
575        0b_0101_0011_0000_0000_0000_0000_0000_0000, // Level 4
576        0b_1000_0100_0000_0001_0000_0000_0000_0000, // Level 5
577        0b_0000_0000_0000_0000_0000_0000_0000_0000, // Level 6
578        0b_0000_0000_0000_0000_0100_0000_0000_0000, // Level 7
579    ];
580    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
581    static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
582    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
583    static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
584
585    #[inline]
586    fn cpu_interrupt_nr_to_cpu_interrupt_handler(
587        number: u32,
588    ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
589        use xtensa_lx_rt::*;
590        // we're fortunate that all esp variants use the same CPU interrupt layout
591        Some(match number {
592            6 => Timer0,
593            7 => Software0,
594            11 => Profiling,
595            14 => NMI,
596            15 => Timer1,
597            16 => Timer2,
598            29 => Software1,
599            _ => return None,
600        })
601    }
602
603    #[unsafe(no_mangle)]
604    #[ram]
605    unsafe fn __level_1_interrupt(save_frame: &mut Context) {
606        unsafe {
607            handle_interrupts::<1>(save_frame);
608        }
609    }
610
611    #[unsafe(no_mangle)]
612    #[ram]
613    unsafe fn __level_2_interrupt(save_frame: &mut Context) {
614        unsafe {
615            handle_interrupts::<2>(save_frame);
616        }
617    }
618
619    #[unsafe(no_mangle)]
620    #[ram]
621    unsafe fn __level_3_interrupt(save_frame: &mut Context) {
622        unsafe {
623            handle_interrupts::<3>(save_frame);
624        }
625    }
626
627    #[inline(always)]
628    unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
629        let core = Cpu::current();
630
631        let cpu_interrupt_mask =
632            interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[LEVEL as usize];
633
634        if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
635            // Let's handle CPU-internal interrupts (NMI, Timer, Software, Profiling).
636            // These are rarely used by the HAL.
637
638            // Mask the relevant bits
639            let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
640
641            // Pick one
642            let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
643
644            // If the interrupt is edge triggered, we need to clear the request on the CPU's
645            // side.
646            if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
647                unsafe {
648                    interrupt::clear(1 << cpu_interrupt_nr);
649                }
650            }
651
652            if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
653                unsafe { handler(save_frame) };
654            }
655        } else {
656            let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
657                // Next, handle edge triggered peripheral interrupts. Note that on the S3 all
658                // peripheral interrupts are level-triggered.
659
660                // If the interrupt is edge triggered, we need to clear the
661                // request on the CPU's side
662                unsafe { interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
663
664                // For edge interrupts we cannot rely on the peripherals' interrupt status
665                // registers, therefore call all registered handlers for current level.
666                chip_specific::INTERRUPT_EDGE
667            } else {
668                // Finally, check level-triggered peripheral sources.
669                // These interrupts are cleared by the peripheral.
670                status(core)
671            };
672
673            let configured_interrupts = configured_interrupts(core, status, LEVEL);
674            for interrupt_nr in configured_interrupts.iterator() {
675                // Don't use `Interrupt::try_from`. It's slower and placed in flash
676                let interrupt: Interrupt = unsafe { core::mem::transmute(interrupt_nr as u16) };
677
678                unsafe extern "C" {
679                    // defined in each hal
680                    fn EspDefaultHandler(interrupt: Interrupt);
681                }
682
683                let handler = unsafe { pac::__INTERRUPTS[interrupt as usize]._handler };
684                if core::ptr::eq(
685                    handler as *const _,
686                    EspDefaultHandler as *const unsafe extern "C" fn(),
687                ) {
688                    unsafe { EspDefaultHandler(interrupt) };
689                } else {
690                    let handler: fn(&mut Context) = unsafe {
691                        core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
692                    };
693                    handler(save_frame);
694                }
695            }
696        }
697    }
698
699    #[cfg(esp32)]
700    mod chip_specific {
701        use super::*;
702        #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
703        pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
704            0b0000_0000_0000_0000_0000_0000_0000_0000,
705            0b1111_1100_0000_0000_0000_0000_0000_0000,
706            0b0000_0000_0000_0000_0000_0000_0000_0011,
707        );
708        #[inline]
709        pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
710            [
711                Interrupt::TG0_T0_EDGE,
712                Interrupt::TG0_T1_EDGE,
713                Interrupt::TG0_WDT_EDGE,
714                Interrupt::TG0_LACT_EDGE,
715                Interrupt::TG1_T0_EDGE,
716                Interrupt::TG1_T1_EDGE,
717                Interrupt::TG1_WDT_EDGE,
718                Interrupt::TG1_LACT_EDGE,
719            ]
720            .contains(&interrupt)
721        }
722    }
723
724    #[cfg(esp32s2)]
725    mod chip_specific {
726        use super::*;
727        #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
728        pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
729            0b0000_0000_0000_0000_0000_0000_0000_0000,
730            0b1100_0000_0000_0000_0000_0000_0000_0000,
731            0b0000_0000_0000_0000_0000_0011_1011_1111,
732        );
733        #[inline]
734        pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
735            [
736                Interrupt::TG0_T0_EDGE,
737                Interrupt::TG0_T1_EDGE,
738                Interrupt::TG0_WDT_EDGE,
739                Interrupt::TG0_LACT_EDGE,
740                Interrupt::TG1_T0_EDGE,
741                Interrupt::TG1_T1_EDGE,
742                Interrupt::TG1_WDT_EDGE,
743                Interrupt::TG1_LACT_EDGE,
744                Interrupt::SYSTIMER_TARGET0,
745                Interrupt::SYSTIMER_TARGET1,
746                Interrupt::SYSTIMER_TARGET2,
747            ]
748            .contains(&interrupt)
749        }
750    }
751
752    #[cfg(esp32s3)]
753    mod chip_specific {
754        use super::*;
755        #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
756        pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
757        #[inline]
758        pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
759            false
760        }
761    }
762}
763
764mod raw {
765    use super::*;
766
767    unsafe extern "C" {
768        fn level4_interrupt(save_frame: &mut Context);
769        fn level5_interrupt(save_frame: &mut Context);
770        fn level6_interrupt(save_frame: &mut Context);
771        fn level7_interrupt(save_frame: &mut Context);
772    }
773
774    #[unsafe(no_mangle)]
775    #[unsafe(link_section = ".rwtext")]
776    unsafe fn __level_4_interrupt(save_frame: &mut Context) {
777        unsafe { level4_interrupt(save_frame) }
778    }
779
780    #[unsafe(no_mangle)]
781    #[unsafe(link_section = ".rwtext")]
782    unsafe fn __level_5_interrupt(save_frame: &mut Context) {
783        unsafe { level5_interrupt(save_frame) }
784    }
785
786    #[unsafe(no_mangle)]
787    #[unsafe(link_section = ".rwtext")]
788    unsafe fn __level_6_interrupt(save_frame: &mut Context) {
789        unsafe { level6_interrupt(save_frame) }
790    }
791
792    #[unsafe(no_mangle)]
793    #[unsafe(link_section = ".rwtext")]
794    unsafe fn __level_7_interrupt(save_frame: &mut Context) {
795        unsafe { level7_interrupt(save_frame) }
796    }
797}