1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! Interrupts

use core::arch::asm;

pub use bare_metal::CriticalSection;

/// Trait for enums of external interrupt numbers.
///
/// This trait should be implemented by a peripheral access crate (PAC)
/// on its enum of available external interrupts for a specific device.
/// Each variant must convert to a u16 of its interrupt number,
/// which is its exception number - 16.
///
/// # Safety
///
/// This trait must only be implemented on enums of device interrupts. Each
/// enum variant must represent a distinct value (no duplicates are permitted),
/// and must always return the same value (do not change at runtime).
///
/// These requirements ensure safe nesting of critical sections.
pub unsafe trait InterruptNumber: Copy {
    /// Return the interrupt number associated with this variant.
    ///
    /// See trait documentation for safety requirements.
    fn number(self) -> u16;
}

/// Disables all interrupts and return the previous settings
#[inline]
pub fn disable() -> u32 {
    unsafe { set_mask(0) }
}

/// Enables all the interrupts
///
/// # Safety
///
/// - Do not call this function inside an `interrupt::free` critical section
#[inline]
pub unsafe fn enable() -> u32 {
    set_mask(!0)
}

/// Enables specific interrupts and returns the previous setting
///
/// # Safety
///
/// - Do not call this function inside an `interrupt::free` critical section
#[inline]
pub unsafe fn set_mask(mut mask: u32) -> u32 {
    asm!("
        xsr {0}, intenable
        rsync
        ",
        inout(reg) mask, options(nostack)
    );
    mask
}

/// Disables specific interrupts and returns the previous settings
#[inline]
pub fn disable_mask(mask: u32) -> u32 {
    let mut prev: u32 = 0;
    let _dummy: u32;
    unsafe {
        asm!("
        xsr.intenable {0}  // get mask and temporarily disable interrupts 
        and {1}, {1}, {0}
        rsync
        wsr.intenable {1}
        rsync
        ", inout(reg) prev, inout(reg) !mask => _dummy, options(nostack)
        );
    }
    prev
}

/// Enables specific interrupts and returns the previous setting
///
/// # Safety
///
/// - Do not call this function inside an `interrupt::free` critical section
#[inline]
pub unsafe fn enable_mask(mask: u32) -> u32 {
    let mut prev: u32 = 0;
    let _dummy: u32;
    asm!("
        xsr.intenable {0} // get mask and temporarily disable interrupts
        or {1}, {1}, {0}
        rsync
        wsr.intenable {1}
        rsync
    ", inout(reg) prev, inout(reg) mask => _dummy, options(nostack));
    prev
}

/// Get current interrupt mask
#[inline]
pub fn get_mask() -> u32 {
    let mask: u32;
    unsafe { asm!("rsr.intenable {0}", out(reg) mask) };
    mask
}

/// Get currently active interrupts
#[inline]
pub fn get() -> u32 {
    let mask: u32;
    unsafe {
        asm!("rsr.interrupt {0}", out(reg) mask, options(nostack));
    }
    mask
}

/// Set interrupt
///
/// Only valid for software interrupts
#[inline]
pub unsafe fn set(mask: u32) {
    asm!("
         wsr.intset {0}
         rsync
         ",
         in(reg) mask, options(nostack)
    );
}

/// Clear interrupt
///
/// Only valid for software and edge-triggered interrupts
#[inline]
pub unsafe fn clear(mask: u32) {
    asm!("
         wsr.intclear {0}
         rsync
         ",
         in(reg) mask, options(nostack)
    );
}

/// Get current interrupt level
#[inline]
pub fn get_level() -> u32 {
    let ps: u32;
    unsafe { asm!("rsr.ps {0}", out(reg) ps, options(nostack)); };
    ps & 0xf
}

/// Execute closure `f` in an interrupt-free context.
///
/// This as also known as a "critical section".
#[inline]
pub fn free<F, R>(f: F) -> R
where
    F: FnOnce(&CriticalSection) -> R,
{
    // disable interrupts and store old mask
    let old_mask = disable();

    let r = f(unsafe { &CriticalSection::new() });

    // enable previously disable interrupts
    unsafe {
        enable_mask(old_mask);
    }

    r
}