Skip to main content

ra_hal/
event_link.rs

1//! Event Link Controller (`ELC`) and Interrupt Controller Unit (`ICU`) related shenanigans.
2//!
3//! On the `RA4M1` peripheral interrupts are not directly exposed by the `NVIC`.
4//! Instead the `NVIC` exposes 32 programmable interrupts via the `ICU`.
5//! Each `ICU` interrupt can be mapped to one of ≈100 interrupt sources as defined in [`InterruptEvent`].
6//! Each `ICU` interrupt can also be used to trigger a variety of different events including DMA transfers and power state changes.
7//! See §13, §18 of the reference manual for more information.
8
9use cortex_m::interrupt::InterruptNumber;
10
11use crate::{
12    module_stop::ModuleStop as _,
13    pac::{self, icu::regs::Ielsr},
14    peripherals::ELC,
15};
16
17#[cfg(trust_zone)]
18use pac::{
19    cpscu::vals::SecurityAttribution,
20    system::vals::{Prc4, Prkey},
21};
22
23/// Trait that implements functions allowing inspection and manipulation of the interrupt's `ELC`/`ICU` status.
24///
25/// # Notes
26///
27/// On MCUs with TrustZone™ support this will set the interrupt attribution to non-secure.
28/// This is done with the assumption that we're running in secure mode.
29///
30/// # Safety
31///
32/// Conveniently the `NVIC` only surfaces `ICU` interrupts so each IRQ to IELSR mapping is 1:1.
33pub unsafe trait IcuInterrupt: InterruptNumber + Copy {
34    /// Disables the interrupt in the `ICU`.  Does not modify its status in the `NVIC`.
35    #[inline(always)]
36    fn icu_disable(&self) {
37        let icu = pac::ICU;
38
39        icu.ielsr(self.number() as _).modify(|w| w.set_iels(0));
40        trace!("IEL{}: disable", self.number());
41    }
42
43    /// Enables the interrupt in the `ICU`.  Does not modify its status in the `NVIC`.
44    ///
45    /// # Notes
46    ///
47    /// On MCUs with TrustZone™ support this will set the interrupt attribution to secure.
48    /// This is done with the assumption that we're running in secure mode.
49    ///
50    /// # Safety
51    ///
52    /// Safe so long as there's an interrupt handler in place.
53    #[inline(always)]
54    unsafe fn icu_enable(&self, mask: InterruptEvent) {
55        let icu = pac::ICU;
56
57        trace!("IEL{}: enable={}", self.number(), mask);
58
59        icu.ielsr(self.number() as _).modify(|w| {
60            w.set_iels(mask as _);
61        });
62    }
63
64    /// Returns `true` if the interrupt has been linked to an [`InterruptEvent`].
65    #[inline(always)]
66    fn icu_enabled(&self) -> bool {
67        self.icu_event() != InterruptEvent::None
68    }
69
70    /// Returns the [`InterruptEvent`] that the interrupt is linked to or `InterruptEvent::None` if none is configured.
71    #[inline(always)]
72    fn icu_event(&self) -> InterruptEvent {
73        let icu = pac::ICU;
74
75        let event = icu.ielsr(self.number() as _).read().iels();
76
77        // Yeah we're trusting there won't be gibberish in the register.
78        // TODO: Find a better way to map a bunch of variants to/from primitives.
79        let event: InterruptEvent = unsafe { core::mem::transmute(event) };
80        trace!("IEL{}: event={}", self.number(), event);
81
82        event
83    }
84
85    /// Disables the interrupt in the `ICU` and disables it as a `DTC` activation source.  Does not modify its status in the `NVIC`.
86    ///
87    /// See §17 of the reference manual for more information.
88    #[inline(always)]
89    fn dtc_disable(&self) {
90        let icu = pac::ICU;
91
92        icu.ielsr(self.number() as _).write_value(Ielsr(0));
93    }
94
95    /// Links an interrupt to an event and configures `DTC` vector `n` for activation on interrupt where `n` is the index of this interrupt.
96    ///
97    /// See §17 of the reference manual for more information.
98    #[inline(always)]
99    fn dtc_enable(&self, mask: InterruptEvent) {
100        let icu = pac::ICU;
101
102        trace!("IEL{}: enable={}, dtc=true", self.number(), mask);
103
104        icu.ielsr(self.number() as _).write(|r| {
105            r.set_dtce(true);
106            r.set_iels(mask as _);
107        });
108    }
109
110    /// # Returns
111    ///
112    /// - `true` if the interrupt is configured for `DTC` activation.
113    /// - `false` otherwise
114    #[inline(always)]
115    fn is_dtc(&self) -> bool {
116        let icu = pac::ICU;
117
118        icu.ielsr(self.number() as _).read().dtce()
119    }
120
121    /// Clears the interrupt request flag in the `ICU`.
122    /// Does not modify the `NVIC`.
123    ///
124    /// Similar to [`InterruptExt::unpend`](trait@crate::interrupt::InterruptExt).
125    /// See also §13.2.6.
126    #[inline(always)]
127    fn icu_unpend(&self) {
128        let icu = pac::ICU;
129
130        icu.ielsr(self.number() as _).modify(|w| w.set_ir(false));
131    }
132
133    /// Sets the interrupt request flag in the `ICU`.
134    /// Does not modify the `NVIC`.
135    ///
136    /// Similar to [`InterruptExt::pend`](trait@crate::interrupt::InterruptExt).
137    /// See also §13.2.6.
138    #[inline(always)]
139    fn icu_pend(&self) {
140        let icu = pac::ICU;
141
142        icu.ielsr(self.number() as _).modify(|w| w.set_ir(true));
143    }
144
145    /// Returns true if the interrupt is pending in the `ICU`.
146    fn icu_is_pending(&self) -> bool {
147        let icu = pac::ICU;
148
149        icu.ielsr(self.number() as _).read().ir()
150    }
151
152    /// Logs information about how this interrupt is configured in the `ICU` at the `trace` level.
153    fn icu_status(&self) {
154        let icu = pac::ICU;
155        let number = self.number();
156        let ielsr = icu.ielsr(number as _);
157        let status = ielsr.read();
158        trace!("IEL{}: ir={}, dtce={}", number, status.ir(), status.dtce());
159    }
160
161    /// Set the security attribution of an interrupt.
162    ///
163    /// # Notes
164    /// This must match the security attribution in the NVIC.
165    ///
166    /// # TODO
167    /// Update security attribution in the NVIC.
168    #[cfg(trust_zone)]
169    fn set_security_attribution(&self, attribution: SecurityAttribution) {
170        let cpscu = pac::CPSCU;
171        let system = pac::SYSTEM;
172
173        let prcr = cfg_select! {
174            all(trust_zone_v2, secure) => system.prcr_s(),
175            _ => system.prcr()
176        };
177
178        prcr.modify(|r| {
179            r.set_prkey(Prkey::ProtectKey);
180            r.set_prc4(Prc4::NotProtected);
181        });
182
183        // TODO G is only for N=0..32
184        cpscu
185            .icusarg()
186            .modify(|r| r.set_saielsr(self.number() as _, attribution));
187
188        info!(
189            "ICUSARG: {}",
190            cpscu.icusarg().read().saielsr(self.number() as _)
191        );
192
193        prcr.modify(|r| {
194            r.set_prkey(Prkey::ProtectKey);
195            r.set_prc4(Prc4::Protected);
196        });
197    }
198}
199
200unsafe impl<T: InterruptNumber + Copy> IcuInterrupt for T {}
201
202/// Trait that restricts the software event generator to one of the two available events.
203#[allow(private_bounds)]
204pub trait SoftwareEventGenerator<const N: u8>: SealedSoftwareEventGenerator<N> {
205    /// Returns the [`InterruptEvent`] associated with this software generated event.
206    fn event(&self) -> InterruptEvent;
207
208    /// Trigger a software generated event.
209    #[inline(always)]
210    fn fire(&self) {
211        let elc = crate::pac::ELC;
212        elc.elsegr(self.index() as _).write(|r| r.set_wi(false));
213        elc.elsegr(self.index() as _).write(|r| r.set_we(true));
214        elc.elsegr(self.index() as _).write(|r| r.set_seg(true));
215    }
216}
217
218trait SealedSoftwareEventGenerator<const N: u8> {
219    fn index(&self) -> u8;
220}
221
222/// A software event generator.
223/// Use this to trigger a software event for the ICU.
224/// On the `RA4M1` valid values for `N` are 0 or 1.
225pub struct SoftwareEvent<const N: u8> {}
226
227impl<const N: u8> SoftwareEvent<N> {
228    /// Constructs a new software event generator.
229    pub const fn new() -> Self {
230        Self {}
231    }
232}
233
234impl SoftwareEventGenerator<0> for SoftwareEvent<0> {
235    #[inline(always)]
236    fn event(&self) -> InterruptEvent {
237        InterruptEvent::ElcSoftwareEvent0
238    }
239}
240
241impl SealedSoftwareEventGenerator<0> for SoftwareEvent<0> {
242    #[inline(always)]
243    fn index(&self) -> u8 {
244        0
245    }
246}
247
248impl SoftwareEventGenerator<1> for SoftwareEvent<1> {
249    #[inline(always)]
250    fn event(&self) -> InterruptEvent {
251        InterruptEvent::ElcSoftwareEvent1
252    }
253}
254impl SealedSoftwareEventGenerator<1> for SoftwareEvent<1> {
255    #[inline(always)]
256    fn index(&self) -> u8 {
257        1
258    }
259}
260
261/// `ELC` event signal numbers.
262/// These correspond to Table 18.3 in the reference manual.
263/// Used in `ELC.ELSRn.ELS`.
264#[allow(unused, missing_docs)]
265#[cfg_attr(feature = "defmt", derive(defmt::Format))]
266#[repr(u8)]
267pub enum EventSignal {
268    PortIrq0 = 0x01,
269    PortIrq1 = 0x02,
270    PortIrq2 = 0x03,
271    PortIrq3 = 0x04,
272    PortIrq4 = 0x05,
273    PortIrq5 = 0x06,
274    PortIrq6 = 0x07,
275    PortIrq7 = 0x08,
276    PortIrq8 = 0x09,
277    PortIrq9 = 0x0a,
278    PortIrq10 = 0x0b,
279    PortIrq11 = 0x0c,
280    PortIrq12 = 0x0d,
281    PortIrq13 = 0x0e,
282    PortIrq14 = 0x0f,
283    PortIrq15 = 0x10,
284
285    Dmac0Int = 0x11,
286    Dmac1Int = 0x12,
287    Dmac2Int = 0x13,
288    Dmac3Int = 0x14,
289
290    DtcEnd = 0x16,
291
292    Lvd1 = 0x19,
293    Lvd2 = 0x1A,
294
295    MoscStop = 0x1C,
296
297    SnoozeRequest = 0x1D,
298
299    Agt0Agti = 0x1E,
300    Agt0AgtCmAi = 0x1F,
301    Agt0AgtCmBi = 0x20,
302
303    Agt1Agti = 0x21,
304    Agt1AgtCmAi = 0x22,
305    Agt1AgtCmBi = 0x23,
306
307    IwdtNmi = 0x24,
308    WdtNmi = 0x25,
309
310    RtcPrd = 0x27,
311
312    AdcAdi = 0x29,
313    AdcWcmpM = 0x2D,
314    AdcWcmpUm = 0x2E,
315
316    AcmpLp0 = 0x2F,
317    AcmpLp1 = 0x30,
318
319    Iic0Rxi = 0x35,
320    Iic0Txi = 0x36,
321    Iic0Tei = 0x37,
322    Iic0Eei = 0x38,
323
324    Iic1Rxi = 0x3A,
325    Iic1Txi = 0x3B,
326    Iic1Tei = 0x3C,
327    Iic1Eei = 0x3D,
328
329    DocDopci = 0x46,
330
331    IoportGroup1 = 0x4F,
332    IoportGroup2 = 0x50,
333    IoportGroup3 = 0x51,
334    IoportGroup4 = 0x52,
335
336    ElcSwEvt0 = 0x53,
337    ElcSwEvt1 = 0x54,
338
339    Gpt0CcmpA = 0x57,
340    Gpt0CcmpB = 0x58,
341    Gpt0CmpC = 0x59,
342    Gpt0CmpD = 0x5A,
343    Gpt0CmpE = 0x5B,
344    Gpt0CmpF = 0x5C,
345    Gpt0Ovf = 0x5D,
346    Gpt0Udf = 0x5E,
347
348    Gpt1CcmpA = 0x5F,
349    Gpt1CcmpB = 0x60,
350    Gpt1CmpC = 0x61,
351    Gpt1CmpD = 0x62,
352    Gpt1CmpE = 0x63,
353    Gpt1CmpF = 0x64,
354    Gpt1Ovf = 0x65,
355    Gpt1Udf = 0x66,
356
357    Gpt2CcmpA = 0x67,
358    Gpt2CcmpB = 0x68,
359    Gpt2CmpC = 0x69,
360    Gpt2CmpD = 0x6A,
361    Gpt2CmpE = 0x6B,
362    Gpt2CmpF = 0x6C,
363    Gpt2Ovf = 0x6D,
364    Gpt2Udf = 0x6E,
365
366    Gpt3CcmpA = 0x6F,
367    Gpt3CcmpB = 0x70,
368    Gpt3CmpC = 0x71,
369    Gpt3CmpD = 0x72,
370    Gpt3CmpE = 0x73,
371    Gpt3CmpF = 0x74,
372    Gpt3Ovf = 0x75,
373    Gpt3Udf = 0x76,
374
375    Gpt4CcmpA = 0x77,
376    Gpt4CcmpB = 0x78,
377    Gpt4CmpC = 0x79,
378    Gpt4CmpD = 0x7A,
379    Gpt4CmpE = 0x7B,
380    Gpt4CmpF = 0x7C,
381    Gpt4Ovf = 0x7D,
382    Gpt4Udf = 0x7E,
383
384    Gpt5CcmpA = 0x7F,
385    Gpt5CcmpB = 0x80,
386    Gpt5CmpC = 0x81,
387    Gpt5CmpD = 0x82,
388    Gpt5CmpE = 0x83,
389    Gpt5CmpF = 0x84,
390    Gpt5Ovf = 0x85,
391    Gpt5Udf = 0x86,
392
393    Gpt6CcmpA = 0x87,
394    Gpt6CcmpB = 0x88,
395    Gpt6CmpC = 0x89,
396    Gpt6CmpD = 0x8A,
397    Gpt6CmpE = 0x8B,
398    Gpt6CmpF = 0x8C,
399    Gpt6Ovf = 0x8D,
400    Gpt6Udf = 0x8E,
401
402    Gpt7CcmpA = 0x8F,
403    Gpt7CcmpB = 0x90,
404    Gpt7CmpC = 0x91,
405    Gpt7CmpD = 0x92,
406    Gpt7CmpE = 0x93,
407    Gpt7CmpF = 0x94,
408    Gpt7Ovf = 0x95,
409    Gpt7Udf = 0x96,
410
411    GptUvwEdge = 0x97,
412
413    Sci0Rxi = 0x98,
414    Sci0Txi = 0x99,
415    Sci0Tei = 0x9A,
416    Sci0Eri = 0x9B,
417    Sci0Am = 0x9C,
418
419    Sci1Rxi = 0x9E,
420    Sci1Txi = 0x9F,
421    Sci1Tei = 0xA0,
422    Sci1Eri = 0xA1,
423    Sci1Am = 0xA2,
424
425    Sci2Rxi = 0xA3,
426    Sci2Txi = 0xA4,
427    Sci2Tei = 0xA5,
428    Sci2Eri = 0xA6,
429    Sci2Am = 0xA7,
430
431    Sci9Rxi = 0xA8,
432    Sci9Txi = 0xA9,
433    Sci9Tei = 0xAA,
434    Sci9Eri = 0xAB,
435    Sci9Am = 0xAC,
436
437    Spi0SpRi = 0xAD,
438    Spi0SpTi = 0xAE,
439    Spi0SpIi = 0xAF,
440    Spi0SpEi = 0xB0,
441    Spi0SpTend = 0xB1,
442
443    Spi1SpRi = 0xB2,
444    Spi1SpTi = 0xB3,
445    Spi1SpIi = 0xB4,
446    Spi1SpEi = 0xB5,
447    Spi1SpTend = 0xB6,
448}
449
450include!(concat!(env!("OUT_DIR"), "/event_link.rs"));
451
452/// Turn on the `ELC` clock.
453pub(crate) fn init() {
454    ELC::start_module();
455}