atsamd_hal/dmac/
async_api.rs

1//! APIs for async DMAC operations.
2
3use atsamd_hal_macros::hal_cfg;
4use core::sync::atomic;
5
6use crate::{
7    async_hal::interrupts::{DMAC, Handler},
8    dmac::{TriggerSource, waker::WAKERS},
9    util::BitIter,
10};
11
12/// Interrupt handler for the DMAC peripheral.
13pub struct InterruptHandler {
14    _private: (),
15}
16
17impl crate::typelevel::Sealed for InterruptHandler {}
18
19#[hal_cfg(any("dmac-d11", "dmac-d21"))]
20impl Handler<DMAC> for InterruptHandler {
21    unsafe fn on_interrupt() {
22        // SAFETY: Here we can't go through the `with_chid` method to safely access
23        // the different channel interrupt flags. Instead, we read the ID in a short
24        // critical section, and make sure to RESET the CHID field to whatever
25        // it was before this function ran.
26        let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
27
28        critical_section::with(|_| {
29            let old_id = dmac.chid().read().id().bits();
30            let pending_interrupts = BitIter(dmac.intstatus().read().bits());
31
32            // Iterate over channels and check their interrupt status
33            for pend_channel in pending_interrupts {
34                unsafe { dmac.chid().modify(|_, w| w.id().bits(pend_channel as u8)) };
35
36                let wake = if dmac.chintflag().read().tcmpl().bit_is_set() {
37                    // Transfer complete. Don't clear the flag, but
38                    // disable the interrupt. Flag will be cleared when polled
39                    dmac.chintenclr().modify(|_, w| w.tcmpl().set_bit());
40                    true
41                } else if dmac.chintflag().read().terr().bit_is_set() {
42                    // Transfer error
43                    dmac.chintenclr().modify(|_, w| w.terr().set_bit());
44                    true
45                } else {
46                    false
47                };
48
49                if wake {
50                    dmac.chctrla().modify(|_, w| w.enable().clear_bit());
51                    dmac.chctrlb()
52                        .modify(|_, w| w.trigsrc().variant(TriggerSource::Disable));
53
54                    while dmac.chctrla().read().enable().bit_is_set() {
55                        core::hint::spin_loop();
56                    }
57
58                    // Prevent the compiler from re-ordering read/write
59                    // operations beyond this fence.
60                    // (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
61                    atomic::fence(atomic::Ordering::Acquire); // ▼
62
63                    WAKERS[pend_channel as usize].wake();
64                }
65            }
66
67            // Reset the CHID.ID register
68            unsafe {
69                dmac.chid().write(|w| w.id().bits(old_id));
70            }
71        });
72    }
73}
74
75#[hal_cfg("dmac-d5x")]
76impl Handler<DMAC> for InterruptHandler {
77    unsafe fn on_interrupt() {
78        let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
79
80        let pending_channels = BitIter(dmac.intstatus().read().bits());
81        for channel in pending_channels.map(|c| c as usize) {
82            let wake = if dmac
83                .channel(channel)
84                .chintflag()
85                .read()
86                .tcmpl()
87                .bit_is_set()
88            {
89                // Transfer complete. Don't clear the flag, but
90                // disable the interrupt. Flag will be cleared when polled
91                dmac.channel(channel)
92                    .chintenclr()
93                    .modify(|_, w| w.tcmpl().set_bit());
94                true
95            } else if dmac.channel(channel).chintflag().read().terr().bit_is_set() {
96                // Transfer error
97                dmac.channel(channel)
98                    .chintenclr()
99                    .modify(|_, w| w.terr().set_bit());
100                true
101            } else {
102                false
103            };
104
105            if wake {
106                dmac.channel(channel).chctrla().modify(|_, w| {
107                    w.enable().clear_bit();
108                    w.trigsrc().variant(TriggerSource::Disable)
109                });
110
111                while dmac.channel(channel).chctrla().read().enable().bit_is_set() {
112                    core::hint::spin_loop();
113                }
114
115                // Prevent the compiler from re-ordering read/write
116                // operations beyond this fence.
117                // (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
118                atomic::fence(atomic::Ordering::Acquire); // ▼
119
120                WAKERS[channel].wake();
121            }
122        }
123    }
124}