imxrt_dma/interrupt.rs
1//! DMA interrupt support
2
3use crate::{channel::Channel, Error};
4use core::{
5 cell::RefCell,
6 future::Future,
7 marker::PhantomPinned,
8 pin::Pin,
9 sync::atomic,
10 task::{Context, Poll, Waker},
11};
12
13use cortex_m::interrupt::{self, Mutex};
14
15impl<const CHANNELS: usize> super::Dma<CHANNELS> {
16 /// Handle a DMA interrupt
17 ///
18 /// Checks the interrupt status for the channel identified by `channel`.
19 /// If the channel completed its transfer, `on_interrupt` wakes the channel's
20 /// waker.
21 ///
22 /// Consider calling `on_interrupt` in a DMA channel's interrupt handler:
23 ///
24 /// ```
25 /// use imxrt_dma::Dma;
26 /// static DMA: Dma<32> = // Handle to DMA driver.
27 /// # unsafe { Dma::new(core::ptr::null(), core::ptr::null()) };
28 ///
29 /// // #[cortex_m_rt::interrupt]
30 /// fn DMA7_DMA23() {
31 /// // Safety: only checking channels 7 and 23, which
32 /// // are both valid on an i.MX RT 1060 chip.
33 /// unsafe {
34 /// DMA.on_interrupt(7);
35 /// DMA.on_interrupt(23);
36 /// }
37 /// }
38 /// ```
39 ///
40 /// # Safety
41 ///
42 /// This should only be used when the associated DMA channel is exclusively referenced
43 /// by a DMA transfer future. Caller must ensure that `on_interrupt` is called in
44 /// the correct interrupt handler.
45 ///
46 /// # Panics
47 ///
48 /// Panics if `channel` is greater than or equal to the maximum number of channels.
49 #[inline(always)]
50 pub unsafe fn on_interrupt(&'static self, channel: usize) {
51 let channel = self.channel(channel);
52 if channel.is_interrupt() {
53 channel.clear_interrupt();
54 }
55
56 if channel.is_complete() | channel.is_error() {
57 interrupt::free(|cs| {
58 let waker = self.wakers[channel.channel()].borrow(cs);
59 let mut waker = waker.borrow_mut();
60 if let Some(waker) = waker.take() {
61 waker.wake();
62 }
63 });
64 }
65 }
66}
67
68pub(crate) type SharedWaker = Mutex<RefCell<Option<Waker>>>;
69#[allow(clippy::declare_interior_mutable_const)] // Very convenient, and usage for static init deemed OK in clippy docs
70pub(crate) const NO_WAKER: SharedWaker = Mutex::new(RefCell::new(None));
71
72/// The core DMA transfer future
73///
74/// `Transfer` is a future that drives the DMA transfer. `Transfer` will
75/// initiate a DMA transfer when it is first polled. You may then poll it
76/// to understand when the transfer completes.
77///
78/// To cancel a transfer, drop the `Transfer`.
79///
80/// If you've enabled DMA interrupts, consider using [`on_interrupt`](crate::Dma::on_interrupt)
81/// to wake an executor when the DMA transfer completes, The interrupt interface assumes that you've
82///
83/// - configured your channel to generate interrupts
84/// - registered a DMA ISR with your embedded runtime
85/// - unmasked the DMA interrupt in the NVIC
86///
87/// `Transfer` calls the unsafe [`enable`](crate::channel::Channel::enable) method to enable a
88/// DMA transfer. To properly use `Transfer`, make sure that you've configured your DMA
89/// channel for a valid transfer.
90///
91/// `Transfer` is the core DMA future used in `imxrt_dma`. For safe DMA transfers, consider
92/// using
93///
94/// - [`Memcpy`](crate::memcpy::Memcpy) for buffer-to-buffer DMA transfers
95/// - [`Read`](crate::peripheral::Read) for peripheral-to-memory DMA transfers
96/// - [`Write`](crate::peripheral::Write) for memory-to-peripheral DMA transfers
97///
98/// `Transfer` is designed to the DMA `Channel` public interface. If you need to implement
99/// your own transfer future, you may do so.
100///
101/// ```no_run
102/// use imxrt_dma::{channel::Channel, Transfer};
103///
104/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
105/// # async fn f() -> imxrt_dma::Result<()> {
106/// let my_channel: Channel = // Acquire your channel...
107/// # unsafe { DMA.channel(0) };
108/// // Properly prepare your transfer...
109/// // Safety: transfer properly prepared
110/// unsafe { Transfer::new(&my_channel) }.await?;
111/// # Ok(()) }
112/// ```
113pub struct Transfer<'a> {
114 channel: &'a Channel,
115 _pinned: PhantomPinned,
116}
117
118impl<'a> Transfer<'a> {
119 /// Create a new `Transfer` that performs the DMA transfer described by `channel`
120 ///
121 /// # Safety
122 ///
123 /// Assumes that the transfer is correctly defined in the DMA channel memory.
124 /// The transfer enables after the first call to `poll()`.
125 pub unsafe fn new(channel: &'a Channel) -> Self {
126 Transfer {
127 channel,
128 _pinned: PhantomPinned,
129 }
130 }
131}
132
133impl Future for Transfer<'_> {
134 type Output = Result<(), Error>;
135 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
136 interrupt::free(|cs| {
137 let waker = self.channel.waker.borrow(cs);
138 let mut waker = waker.borrow_mut();
139 *waker = Some(cx.waker().clone());
140 });
141
142 loop {
143 // This driver is only expecting to catch synchronous errors
144 // (those that manifest once we enable the transfer). If there
145 // is a misconfiguration that only the hardware detects, we expect
146 // to see it as soon as we loop back around after the enable.
147 if self.channel.is_error() {
148 let es = self.channel.error_status();
149 self.channel.clear_error();
150 return Poll::Ready(Err(es));
151 } else if self.channel.is_complete() {
152 self.channel.clear_complete();
153 return Poll::Ready(Ok(()));
154 } else if self.channel.is_enabled() {
155 return Poll::Pending;
156 } else {
157 atomic::fence(atomic::Ordering::SeqCst);
158 unsafe { self.channel.enable() };
159 }
160 }
161 }
162}
163
164impl Drop for Transfer<'_> {
165 fn drop(&mut self) {
166 self.channel.disable();
167 self.channel.clear_complete();
168 self.channel.clear_error();
169 interrupt::free(|cs| {
170 let waker = self.channel.waker.borrow(cs);
171 let mut waker = waker.borrow_mut();
172 *waker = None;
173 });
174 }
175}