esp_hal/interrupt/
riscv.rs

1//! Interrupt handling
2//!
3//! CPU interrupts 1 through 15 are reserved for each of the possible interrupt
4//! priorities.
5//!
6//! On chips with a PLIC CPU interrupts 1,2,5,6,9 .. 19 are used.
7//!
8//! ```rust, ignore
9//! interrupt1() => Priority::Priority1
10//! interrupt2() => Priority::Priority2
11//! ...
12//! interrupt15() => Priority::Priority15
13//! ```
14
15pub use esp_riscv_rt::TrapFrame;
16use riscv::register::{mcause, mtvec};
17
18#[cfg(not(plic))]
19pub use self::classic::*;
20#[cfg(plic)]
21pub use self::plic::*;
22pub use self::vectored::*;
23use super::InterruptStatus;
24use crate::{
25    pac,
26    peripherals::{INTERRUPT_CORE0, Interrupt},
27    system::Cpu,
28};
29
30/// Interrupt Error
31#[derive(Copy, Clone, Debug, PartialEq, Eq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33pub enum Error {
34    /// The priority is not valid
35    InvalidInterruptPriority,
36    /// The CPU interrupt is a reserved interrupt
37    CpuInterruptReserved,
38}
39
40/// Interrupt kind
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum InterruptKind {
43    /// Level interrupt
44    Level,
45    /// Edge interrupt
46    Edge,
47}
48
49/// Enumeration of available CPU interrupts.
50/// It is possible to create a handler for each of the interrupts. (e.g.
51/// `interrupt3`)
52#[repr(u32)]
53#[derive(Debug, Copy, Clone)]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
55pub enum CpuInterrupt {
56    /// Interrupt number 1.
57    Interrupt1 = 1,
58    /// Interrupt number 2.
59    Interrupt2,
60    /// Interrupt number 3.
61    Interrupt3,
62    /// Interrupt number 4.
63    Interrupt4,
64    /// Interrupt number 5.
65    Interrupt5,
66    /// Interrupt number 6.
67    Interrupt6,
68    /// Interrupt number 7.
69    Interrupt7,
70    /// Interrupt number 8.
71    Interrupt8,
72    /// Interrupt number 9.
73    Interrupt9,
74    /// Interrupt number 10.
75    Interrupt10,
76    /// Interrupt number 11.
77    Interrupt11,
78    /// Interrupt number 12.
79    Interrupt12,
80    /// Interrupt number 13.
81    Interrupt13,
82    /// Interrupt number 14.
83    Interrupt14,
84    /// Interrupt number 15.
85    Interrupt15,
86    /// Interrupt number 16.
87    Interrupt16,
88    /// Interrupt number 17.
89    Interrupt17,
90    /// Interrupt number 18.
91    Interrupt18,
92    /// Interrupt number 19.
93    Interrupt19,
94    /// Interrupt number 20.
95    Interrupt20,
96    /// Interrupt number 21.
97    Interrupt21,
98    /// Interrupt number 22.
99    Interrupt22,
100    /// Interrupt number 23.
101    Interrupt23,
102    /// Interrupt number 24.
103    Interrupt24,
104    /// Interrupt number 25.
105    Interrupt25,
106    /// Interrupt number 26.
107    Interrupt26,
108    /// Interrupt number 27.
109    Interrupt27,
110    /// Interrupt number 28.
111    Interrupt28,
112    /// Interrupt number 29.
113    Interrupt29,
114    /// Interrupt number 30.
115    Interrupt30,
116    /// Interrupt number 31.
117    Interrupt31,
118}
119
120/// Interrupt priority levels.
121#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
122#[cfg_attr(feature = "defmt", derive(defmt::Format))]
123#[repr(u8)]
124pub enum Priority {
125    /// No priority.
126    None = 0,
127    /// Priority level 1.
128    Priority1,
129    /// Priority level 2.
130    Priority2,
131    /// Priority level 3.
132    Priority3,
133    /// Priority level 4.
134    Priority4,
135    /// Priority level 5.
136    Priority5,
137    /// Priority level 6.
138    Priority6,
139    /// Priority level 7.
140    Priority7,
141    /// Priority level 8.
142    Priority8,
143    /// Priority level 9.
144    Priority9,
145    /// Priority level 10.
146    Priority10,
147    /// Priority level 11.
148    Priority11,
149    /// Priority level 12.
150    Priority12,
151    /// Priority level 13.
152    Priority13,
153    /// Priority level 14.
154    Priority14,
155    /// Priority level 15.
156    Priority15,
157}
158
159impl Priority {
160    /// Maximum interrupt priority
161    pub const fn max() -> Priority {
162        Priority::Priority15
163    }
164
165    /// Minimum interrupt priority
166    pub const fn min() -> Priority {
167        Priority::Priority1
168    }
169}
170
171impl TryFrom<u32> for Priority {
172    type Error = Error;
173
174    fn try_from(value: u32) -> Result<Self, Self::Error> {
175        match value {
176            0 => Ok(Priority::None),
177            1 => Ok(Priority::Priority1),
178            2 => Ok(Priority::Priority2),
179            3 => Ok(Priority::Priority3),
180            4 => Ok(Priority::Priority4),
181            5 => Ok(Priority::Priority5),
182            6 => Ok(Priority::Priority6),
183            7 => Ok(Priority::Priority7),
184            8 => Ok(Priority::Priority8),
185            9 => Ok(Priority::Priority9),
186            10 => Ok(Priority::Priority10),
187            11 => Ok(Priority::Priority11),
188            12 => Ok(Priority::Priority12),
189            13 => Ok(Priority::Priority13),
190            14 => Ok(Priority::Priority14),
191            15 => Ok(Priority::Priority15),
192            _ => Err(Error::InvalidInterruptPriority),
193        }
194    }
195}
196
197impl TryFrom<u8> for Priority {
198    type Error = Error;
199
200    fn try_from(value: u8) -> Result<Self, Self::Error> {
201        Priority::try_from(value as u32)
202    }
203}
204
205/// The interrupts reserved by the HAL
206#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
207pub static RESERVED_INTERRUPTS: &[usize] = PRIORITY_TO_INTERRUPT;
208
209/// # Safety
210///
211/// This function is called from an assembly trap handler.
212#[doc(hidden)]
213#[unsafe(link_section = ".trap.rust")]
214#[unsafe(export_name = "_start_trap_rust_hal")]
215pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
216    assert!(
217        mcause::read().is_exception(),
218        "Arrived into _start_trap_rust_hal but mcause is not an exception!"
219    );
220    unsafe extern "C" {
221        fn ExceptionHandler(tf: *mut TrapFrame);
222    }
223    unsafe {
224        ExceptionHandler(trap_frame);
225    }
226}
227
228#[doc(hidden)]
229#[unsafe(no_mangle)]
230pub fn _setup_interrupts() {
231    unsafe extern "C" {
232        static _vector_table: *const u32;
233    }
234
235    unsafe {
236        // disable all known interrupts
237        // at least after the 2nd stage bootloader there are some interrupts enabled
238        // (e.g. UART)
239        for peripheral_interrupt in 0..255 {
240            crate::peripherals::Interrupt::try_from(peripheral_interrupt)
241                .map(|intr| {
242                    #[cfg(multi_core)]
243                    disable(Cpu::AppCpu, intr);
244                    disable(Cpu::ProCpu, intr);
245                })
246                .ok();
247        }
248
249        let vec_table = &_vector_table as *const _ as usize;
250        mtvec::write(vec_table, mtvec::TrapMode::Vectored);
251
252        crate::interrupt::init_vectoring();
253    };
254
255    #[cfg(plic)]
256    unsafe {
257        core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
258    }
259}
260
261/// Enable an interrupt by directly binding it to a available CPU interrupt
262///
263/// Unless you are sure, you most likely want to use [`enable`] instead.
264///
265/// Trying using a reserved interrupt from [`RESERVED_INTERRUPTS`] will return
266/// an error.
267pub fn enable_direct(
268    interrupt: Interrupt,
269    level: Priority,
270    cpu_interrupt: CpuInterrupt,
271) -> Result<(), Error> {
272    if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
273        return Err(Error::CpuInterruptReserved);
274    }
275    if matches!(level, Priority::None) {
276        return Err(Error::InvalidInterruptPriority);
277    }
278    unsafe {
279        map(Cpu::current(), interrupt, cpu_interrupt);
280        set_priority(Cpu::current(), cpu_interrupt, level);
281        enable_cpu_interrupt(cpu_interrupt);
282    }
283    Ok(())
284}
285
286/// Disable the given peripheral interrupt.
287pub fn disable(_core: Cpu, interrupt: Interrupt) {
288    unsafe {
289        let interrupt_number = interrupt as isize;
290        let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
291
292        // set to 0 to disable the peripheral interrupt on chips with an interrupt
293        // controller other than PLIC use the disabled interrupt 31 otherwise
294        intr_map_base
295            .offset(interrupt_number)
296            .write_volatile(DISABLED_CPU_INTERRUPT);
297    }
298}
299
300/// Get status of peripheral interrupts
301#[inline]
302pub fn status(_core: Cpu) -> InterruptStatus {
303    cfg_if::cfg_if! {
304        if #[cfg(large_intr_status)] {
305            InterruptStatus::from(
306                INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
307                INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
308                INTERRUPT_CORE0::regs().int_status_reg_2().read().bits(),
309            )
310        } else if #[cfg(very_large_intr_status)] {
311            InterruptStatus::from(
312                INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
313                INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
314                INTERRUPT_CORE0::regs().intr_status_reg_2().read().bits(),
315                INTERRUPT_CORE0::regs().intr_status_reg_3().read().bits(),
316            )
317        } else {
318            InterruptStatus::from(
319                INTERRUPT_CORE0::regs().intr_status_reg_0().read().bits(),
320                INTERRUPT_CORE0::regs().intr_status_reg_1().read().bits(),
321            )
322        }
323    }
324}
325
326/// Assign a peripheral interrupt to an CPU interrupt.
327///
328/// # Safety
329///
330/// Do not use CPU interrupts in the [`RESERVED_INTERRUPTS`].
331pub unsafe fn map(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
332    let interrupt_number = interrupt as isize;
333    let cpu_interrupt_number = which as isize;
334    #[cfg(not(multi_core))]
335    let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
336    #[cfg(multi_core)]
337    let intr_map_base = match _core {
338        Cpu::ProCpu => crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32,
339        Cpu::AppCpu => crate::soc::registers::INTERRUPT_MAP_BASE_APP_CPU as *mut u32,
340    };
341
342    unsafe {
343        intr_map_base
344            .offset(interrupt_number)
345            .write_volatile(cpu_interrupt_number as u32 + EXTERNAL_INTERRUPT_OFFSET);
346    }
347}
348
349/// Get cpu interrupt assigned to peripheral interrupt
350#[inline]
351unsafe fn assigned_cpu_interrupt(interrupt: Interrupt) -> Option<CpuInterrupt> {
352    let interrupt_number = interrupt as isize;
353    let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32;
354
355    let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() };
356    if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT {
357        Some(unsafe {
358            core::mem::transmute::<u32, CpuInterrupt>(cpu_intr - EXTERNAL_INTERRUPT_OFFSET)
359        })
360    } else {
361        None
362    }
363}
364
365pub(crate) fn bound_cpu_interrupt_for(_cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
366    unsafe { assigned_cpu_interrupt(interrupt) }
367}
368
369mod vectored {
370    use procmacros::ram;
371
372    use super::*;
373
374    // Setup interrupts ready for vectoring
375    #[doc(hidden)]
376    pub(crate) unsafe fn init_vectoring() {
377        for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() {
378            unsafe {
379                set_kind(
380                    Cpu::current(),
381                    core::mem::transmute::<u32, CpuInterrupt>(*num as u32),
382                    InterruptKind::Level,
383                );
384                set_priority(
385                    Cpu::current(),
386                    core::mem::transmute::<u32, CpuInterrupt>(*num as u32),
387                    core::mem::transmute::<u8, Priority>((prio as u8) + 1),
388                );
389                enable_cpu_interrupt(core::mem::transmute::<u32, CpuInterrupt>(*num as u32));
390            }
391        }
392    }
393
394    /// Get the interrupts configured for the core at the given priority
395    /// matching the given status
396    #[inline]
397    fn configured_interrupts(
398        core: Cpu,
399        status: InterruptStatus,
400        priority: Priority,
401    ) -> InterruptStatus {
402        unsafe {
403            let mut res = InterruptStatus::empty();
404
405            for interrupt_nr in status.iterator() {
406                // safety: cast is safe because of repr(u16)
407                if let Some(cpu_interrupt) =
408                    assigned_cpu_interrupt(core::mem::transmute::<u16, Interrupt>(
409                        interrupt_nr as u16,
410                    ))
411                {
412                    if priority_by_core(core, cpu_interrupt) == priority {
413                        res.set(interrupt_nr);
414                    }
415                }
416            }
417            res
418        }
419    }
420
421    /// Enables a interrupt at a given priority
422    ///
423    /// Note that interrupts still need to be enabled globally for interrupts
424    /// to be serviced.
425    pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
426        enable_on_cpu(Cpu::current(), interrupt, level)
427    }
428
429    pub(crate) fn enable_on_cpu(
430        cpu: Cpu,
431        interrupt: Interrupt,
432        level: Priority,
433    ) -> Result<(), Error> {
434        if matches!(level, Priority::None) {
435            return Err(Error::InvalidInterruptPriority);
436        }
437        unsafe {
438            let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(
439                PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32,
440            );
441            map(cpu, interrupt, cpu_interrupt);
442            enable_cpu_interrupt(cpu_interrupt);
443        }
444        Ok(())
445    }
446
447    /// Binds the given interrupt to the given handler.
448    ///
449    /// # Safety
450    ///
451    /// This will replace any previously bound interrupt handler
452    pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
453        unsafe {
454            let ptr = &pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _
455                as *mut unsafe extern "C" fn();
456            ptr.write_volatile(handler);
457        }
458    }
459
460    /// Returns the currently bound interrupt handler.
461    pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
462        unsafe {
463            let addr = pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler;
464            if addr as usize == 0 {
465                return None;
466            }
467
468            Some(addr)
469        }
470    }
471
472    #[unsafe(no_mangle)]
473    #[ram]
474    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) {
475        let core = Cpu::current();
476        let status = status(core);
477
478        // this has no effect on level interrupts, but the interrupt may be an edge one
479        // so we clear it anyway
480        clear(core, cpu_intr);
481
482        let priority = INTERRUPT_TO_PRIORITY[cpu_intr as usize];
483        let prio: Priority = unsafe { core::mem::transmute(priority) };
484        let configured_interrupts = configured_interrupts(core, status, prio);
485
486        for interrupt_nr in configured_interrupts.iterator() {
487            // Don't use `Interrupt::try_from`. It's slower and placed in flash
488            let interrupt: Interrupt = unsafe { core::mem::transmute(interrupt_nr as u16) };
489            unsafe {
490                handle_interrupt(interrupt, context);
491            }
492        }
493    }
494
495    #[inline(always)]
496    unsafe fn handle_interrupt(interrupt: Interrupt, save_frame: &mut TrapFrame) {
497        unsafe extern "C" {
498            // defined in each hal
499            fn EspDefaultHandler(interrupt: Interrupt);
500        }
501
502        let handler = unsafe { pac::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler };
503
504        if core::ptr::eq(
505            handler as *const _,
506            EspDefaultHandler as *const unsafe extern "C" fn(),
507        ) {
508            unsafe { EspDefaultHandler(interrupt) };
509        } else {
510            let handler: fn(&mut TrapFrame) = unsafe {
511                core::mem::transmute::<unsafe extern "C" fn(), fn(&mut TrapFrame)>(handler)
512            };
513            handler(save_frame);
514        }
515    }
516
517    // The compiler generates quite unfortunate code for
518    // ```rust,ignore
519    // #[no_mangle]
520    // #[ram]
521    // unsafe fn interrupt1(context: &mut TrapFrame) {
522    //    handle_interrupts(CpuInterrupt::Interrupt1, context)
523    // }
524    // ```
525    //
526    // Resulting in
527    // ```asm,ignore
528    // interrupt1:
529    // add	sp,sp,-16
530    // sw	ra,12(sp)
531    // sw	s0,8(sp)
532    // add	s0,sp,16
533    // mv	a1,a0
534    // li	a0,1
535    // lw	ra,12(sp)
536    // lw	s0,8(sp)
537    // add	sp,sp,16
538    // auipc	t1,0x0
539    // jr	handle_interrupts
540    // ```
541    //
542    // We can do better manually - use Rust again once/if that changes
543    macro_rules! interrupt_handler {
544        ($num:literal) => {
545            core::arch::global_asm! {
546                concat!(
547                r#"
548                    .section .rwtext, "ax"
549                    .global interrupt"#,$num,r#"
550
551                interrupt"#,$num,r#":
552                    mv a1, a0
553                    li a0,"#,$num,r#"
554                    j handle_interrupts
555                "#
556            )
557            }
558        };
559    }
560
561    interrupt_handler!(1);
562    interrupt_handler!(2);
563    interrupt_handler!(3);
564    interrupt_handler!(4);
565    interrupt_handler!(5);
566    interrupt_handler!(6);
567    interrupt_handler!(7);
568    interrupt_handler!(8);
569    interrupt_handler!(9);
570    interrupt_handler!(10);
571    interrupt_handler!(11);
572    interrupt_handler!(12);
573    interrupt_handler!(13);
574    interrupt_handler!(14);
575    interrupt_handler!(15);
576
577    #[cfg(plic)]
578    interrupt_handler!(16);
579    #[cfg(plic)]
580    interrupt_handler!(17);
581    #[cfg(plic)]
582    interrupt_handler!(18);
583    #[cfg(plic)]
584    interrupt_handler!(19);
585}
586
587#[cfg(not(plic))]
588mod classic {
589    use super::{CpuInterrupt, InterruptKind, Priority};
590    use crate::{peripherals::INTERRUPT_CORE0, system::Cpu};
591
592    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
593    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 0;
594
595    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
596    pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
597
598    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
599    pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
600        &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
601
602    // First element is not used, just there to avoid a -1 in the interrupt handler.
603    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
604    pub(super) static INTERRUPT_TO_PRIORITY: [u8; 16] =
605        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
606
607    /// Enable a CPU interrupt
608    ///
609    /// # Safety
610    ///
611    /// Make sure there is an interrupt handler registered.
612    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
613        let cpu_interrupt_number = which as isize;
614        let intr = INTERRUPT_CORE0::regs();
615        intr.cpu_int_enable()
616            .modify(|r, w| unsafe { w.bits((1 << cpu_interrupt_number) | r.bits()) });
617    }
618
619    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
620    ///
621    /// The vectored interrupt handler will take care of clearing edge interrupt
622    /// bits.
623    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
624        unsafe {
625            let intr = INTERRUPT_CORE0::regs();
626            let cpu_interrupt_number = which as isize;
627
628            let interrupt_type = match kind {
629                InterruptKind::Level => 0,
630                InterruptKind::Edge => 1,
631            };
632            intr.cpu_int_type().modify(|r, w| {
633                w.bits(
634                    r.bits() & !(1 << cpu_interrupt_number)
635                        | (interrupt_type << cpu_interrupt_number),
636                )
637            });
638        }
639    }
640
641    /// Set the priority level of an CPU interrupt
642    ///
643    /// # Safety
644    ///
645    /// Great care must be taken when using this function; avoid changing the
646    /// priority of interrupts 1 - 15.
647    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
648        let intr = INTERRUPT_CORE0::regs();
649        intr.cpu_int_pri(which as usize)
650            .write(|w| unsafe { w.map().bits(priority as u8) });
651    }
652
653    /// Clear a CPU interrupt
654    #[inline]
655    pub fn clear(_core: Cpu, which: CpuInterrupt) {
656        unsafe {
657            let cpu_interrupt_number = which as usize;
658            let intr = INTERRUPT_CORE0::regs();
659            intr.cpu_int_clear()
660                .write(|w| w.bits(1 << cpu_interrupt_number));
661        }
662    }
663
664    /// Get interrupt priority
665    #[inline]
666    pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority {
667        unsafe { priority(cpu_interrupt) }
668    }
669
670    /// Get interrupt priority - called by assembly code
671    #[inline]
672    pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
673        let intr = INTERRUPT_CORE0::regs();
674        unsafe {
675            core::mem::transmute::<u8, Priority>(
676                intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(),
677            )
678        }
679    }
680    #[unsafe(no_mangle)]
681    #[unsafe(link_section = ".trap")]
682    pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
683        use super::mcause;
684        // Both C6 and H2 have 5 bits of code. The riscv crate masks 31 bits, which then
685        // causes a bounds check to be present.
686        let interrupt_id: usize = mcause::read().bits() & 0x1f;
687        let intr = INTERRUPT_CORE0::regs();
688        let interrupt_priority = unsafe {
689            intr.cpu_int_pri(0)
690                .as_ptr()
691                .add(interrupt_id)
692                .read_volatile()
693        };
694
695        let prev_interrupt_priority = intr.cpu_int_thresh().read().bits();
696        if interrupt_priority < 15 {
697            // leave interrupts disabled if interrupt is of max priority.
698            intr.cpu_int_thresh()
699                .write(|w| unsafe { w.bits(interrupt_priority + 1) }); // set the prio threshold to 1 more than current interrupt prio
700            unsafe { riscv::interrupt::enable() };
701        }
702        prev_interrupt_priority
703    }
704    #[unsafe(no_mangle)]
705    #[unsafe(link_section = ".trap")]
706    pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
707        riscv::interrupt::disable();
708        let intr = INTERRUPT_CORE0::regs();
709        intr.cpu_int_thresh()
710            .write(|w| unsafe { w.bits(stored_prio) });
711    }
712
713    /// Get the current run level (the level below which interrupts are masked).
714    pub fn current_runlevel() -> Priority {
715        let intr = INTERRUPT_CORE0::regs();
716        let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;
717
718        unwrap!(Priority::try_from(prev_interrupt_priority))
719    }
720
721    /// Changes the current run level (the level below which interrupts are
722    /// masked), and returns the previous run level.
723    ///
724    /// # Safety
725    ///
726    /// This function must only be used to raise the runlevel and to restore it
727    /// to a previous value. It must not be used to arbitrarily lower the
728    /// runlevel.
729    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
730        let prev_interrupt_priority = current_runlevel();
731
732        // The CPU responds to interrupts `>= level`, but we want to also disable
733        // interrupts at `level` so we set the threshold to `level + 1`.
734        INTERRUPT_CORE0::regs()
735            .cpu_int_thresh()
736            .write(|w| unsafe { w.bits(level as u32 + 1) });
737
738        prev_interrupt_priority
739    }
740}
741
742#[cfg(plic)]
743mod plic {
744    use super::{CpuInterrupt, InterruptKind, Priority};
745    use crate::{peripherals::PLIC_MX, system::Cpu};
746
747    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
748    pub(super) static DISABLED_CPU_INTERRUPT: u32 = 31;
749
750    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
751    pub(super) static EXTERNAL_INTERRUPT_OFFSET: u32 = 0;
752
753    // don't use interrupts reserved for CLIC (0,3,4,7)
754    // for some reason also CPU interrupt 8 doesn't work by default since it's
755    // disabled after reset - so don't use that, too
756    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
757    pub(super) static PRIORITY_TO_INTERRUPT: &[usize] =
758        &[1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
759
760    // First element is not used, just there to avoid a -1 in the interrupt handler.
761    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
762    pub(super) static INTERRUPT_TO_PRIORITY: [u8; 20] = [
763        0, 1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
764    ];
765
766    /// Enable a CPU interrupt
767    ///
768    /// # Safety
769    ///
770    /// Make sure there is an interrupt handler registered.
771    pub unsafe fn enable_cpu_interrupt(which: CpuInterrupt) {
772        unsafe {
773            PLIC_MX::regs().mxint_enable().modify(|r, w| {
774                let old = r.cpu_mxint_enable().bits();
775                let new = old | (1 << (which as isize));
776                w.cpu_mxint_enable().bits(new)
777            });
778        }
779    }
780
781    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
782    ///
783    /// The vectored interrupt handler will take care of clearing edge interrupt
784    /// bits.
785    pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
786        let interrupt_type = match kind {
787            InterruptKind::Level => 0,
788            InterruptKind::Edge => 1,
789        };
790
791        unsafe {
792            PLIC_MX::regs().mxint_type().modify(|r, w| {
793                let old = r.cpu_mxint_type().bits();
794                let new = old & !(1 << (which as isize)) | (interrupt_type << (which as isize));
795                w.cpu_mxint_type().bits(new)
796            });
797        }
798    }
799
800    /// Set the priority level of an CPU interrupt
801    ///
802    /// # Safety
803    ///
804    /// Great care must be taken when using this function; avoid changing the
805    /// priority of interrupts 1 - 15.
806    pub unsafe fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
807        unsafe {
808            PLIC_MX::regs()
809                .mxint_pri(which as usize)
810                .modify(|_, w| w.cpu_mxint_pri().bits(priority as u8));
811        }
812    }
813
814    /// Clear a CPU interrupt
815    #[inline]
816    pub fn clear(_core: Cpu, which: CpuInterrupt) {
817        unsafe {
818            PLIC_MX::regs().mxint_clear().modify(|r, w| {
819                let old = r.cpu_mxint_clear().bits();
820                let new = old | (1 << (which as isize));
821                w.cpu_mxint_clear().bits(new)
822            });
823        }
824    }
825
826    /// Get interrupt priority
827    #[inline]
828    pub(super) unsafe extern "C" fn priority_by_core(
829        _core: Cpu,
830        cpu_interrupt: CpuInterrupt,
831    ) -> Priority {
832        unsafe { priority(cpu_interrupt) }
833    }
834
835    /// Get interrupt priority - called by assembly code
836    #[inline]
837    pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority {
838        let prio = PLIC_MX::regs()
839            .mxint_pri(cpu_interrupt as usize)
840            .read()
841            .cpu_mxint_pri()
842            .bits();
843        unsafe { core::mem::transmute::<u8, Priority>(prio) }
844    }
845    #[unsafe(no_mangle)]
846    #[unsafe(link_section = ".trap")]
847    pub(super) unsafe extern "C" fn _handle_priority() -> u32 {
848        let interrupt_id: usize = super::mcause::read().code(); // MSB is whether its exception or interrupt.
849        let interrupt_priority = PLIC_MX::regs()
850            .mxint_pri(interrupt_id)
851            .read()
852            .cpu_mxint_pri()
853            .bits();
854
855        let prev_interrupt_priority = PLIC_MX::regs()
856            .mxint_thresh()
857            .read()
858            .cpu_mxint_thresh()
859            .bits();
860        if interrupt_priority < 15 {
861            // leave interrupts disabled if interrupt is of max priority.
862            PLIC_MX::regs()
863                .mxint_thresh()
864                .write(|w| unsafe { w.cpu_mxint_thresh().bits(interrupt_priority + 1) });
865
866            unsafe {
867                riscv::interrupt::enable();
868            }
869        }
870        prev_interrupt_priority as u32
871    }
872    #[unsafe(no_mangle)]
873    #[unsafe(link_section = ".trap")]
874    pub(super) unsafe extern "C" fn _restore_priority(stored_prio: u32) {
875        riscv::interrupt::disable();
876        PLIC_MX::regs()
877            .mxint_thresh()
878            .write(|w| unsafe { w.cpu_mxint_thresh().bits(stored_prio as u8) });
879    }
880
881    /// Get the current run level (the level below which interrupts are masked).
882    pub fn current_runlevel() -> Priority {
883        let prev_interrupt_priority = PLIC_MX::regs()
884            .mxint_thresh()
885            .read()
886            .cpu_mxint_thresh()
887            .bits()
888            .saturating_sub(1);
889
890        unwrap!(Priority::try_from(prev_interrupt_priority))
891    }
892
893    /// Changes the current run level (the level below which interrupts are
894    /// masked), and returns the previous run level.
895    ///
896    /// # Safety
897    ///
898    /// This function must only be used to raise the runlevel and to restore it
899    /// to a previous value. It must not be used to arbitrarily lower the
900    /// runlevel.
901    pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
902        let prev_interrupt_priority = current_runlevel();
903
904        // The CPU responds to interrupts `>= level`, but we want to also disable
905        // interrupts at `level` so we set the threshold to `level + 1`.
906        PLIC_MX::regs()
907            .mxint_thresh()
908            .write(|w| unsafe { w.cpu_mxint_thresh().bits(level as u8 + 1) });
909
910        prev_interrupt_priority
911    }
912}