1//! APIs for async DMAC operations.
23use atsamd_hal_macros::hal_cfg;
45use crate::{
6 async_hal::interrupts::{Handler, DMAC},
7 dmac::{waker::WAKERS, TriggerSource},
8 util::BitIter,
9};
1011/// Interrupt handler for the DMAC peripheral.
12pub struct InterruptHandler {
13 _private: (),
14}
1516impl crate::typelevel::Sealed for InterruptHandler {}
1718#[hal_cfg(any("dmac-d11", "dmac-d21"))]
19impl Handler<DMAC> for InterruptHandler {
20unsafe fn on_interrupt() {
21// SAFETY: Here we can't go through the `with_chid` method to safely access
22 // the different channel interrupt flags. Instead, we read the ID in a short
23 // critical section, and make sure to RESET the CHID field to whatever
24 // it was before this function ran.
25let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
2627 critical_section::with(|_| {
28let old_id = dmac.chid().read().id().bits();
29let pending_interrupts = BitIter(dmac.intstatus().read().bits());
3031// Iterate over channels and check their interrupt status
32for pend_channel in pending_interrupts {
33unsafe { dmac.chid().modify(|_, w| w.id().bits(pend_channel as u8)) };
3435let wake = if dmac.chintflag().read().tcmpl().bit_is_set() {
36// Transfer complete. Don't clear the flag, but
37 // disable the interrupt. Flag will be cleared when polled
38dmac.chintenclr().modify(|_, w| w.tcmpl().set_bit());
39true
40} else if dmac.chintflag().read().terr().bit_is_set() {
41// Transfer error
42dmac.chintenclr().modify(|_, w| w.terr().set_bit());
43true
44} else {
45false
46};
4748if wake {
49 dmac.chctrla().modify(|_, w| w.enable().clear_bit());
50 dmac.chctrlb()
51 .modify(|_, w| w.trigsrc().variant(TriggerSource::Disable));
52 WAKERS[pend_channel as usize].wake();
53 }
54 }
5556// Reset the CHID.ID register
57unsafe {
58 dmac.chid().write(|w| w.id().bits(old_id));
59 }
60 });
61 }
62}
6364#[hal_cfg("dmac-d5x")]
65impl Handler<DMAC> for InterruptHandler {
66unsafe fn on_interrupt() {
67let dmac = unsafe { crate::pac::Peripherals::steal().dmac };
6869let pending_channels = BitIter(dmac.intstatus().read().bits());
70for channel in pending_channels.map(|c| c as usize) {
71let wake = if dmac
72 .channel(channel)
73 .chintflag()
74 .read()
75 .tcmpl()
76 .bit_is_set()
77 {
78// Transfer complete. Don't clear the flag, but
79 // disable the interrupt. Flag will be cleared when polled
80dmac.channel(channel)
81 .chintenclr()
82 .modify(|_, w| w.tcmpl().set_bit());
83true
84} else if dmac.channel(channel).chintflag().read().terr().bit_is_set() {
85// Transfer error
86dmac.channel(channel)
87 .chintenclr()
88 .modify(|_, w| w.terr().set_bit());
89true
90} else {
91false
92};
9394if wake {
95 dmac.channel(channel).chctrla().modify(|_, w| {
96 w.enable().clear_bit();
97 w.trigsrc().variant(TriggerSource::Disable)
98 });
99 WAKERS[channel].wake();
100 }
101 }
102 }
103}