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 169 170 171 172 173 174 175
//! DMA interrupt support
use crate::{channel::Channel, Error};
use core::{
cell::RefCell,
future::Future,
marker::PhantomPinned,
pin::Pin,
sync::atomic,
task::{Context, Poll, Waker},
};
use cortex_m::interrupt::{self, Mutex};
impl<const CHANNELS: usize> super::Dma<CHANNELS> {
/// Handle a DMA interrupt
///
/// Checks the interrupt status for the channel identified by `channel`.
/// If the channel completed its transfer, `on_interrupt` wakes the channel's
/// waker.
///
/// Consider calling `on_interrupt` in a DMA channel's interrupt handler:
///
/// ```
/// use imxrt_dma::Dma;
/// static DMA: Dma<32> = // Handle to DMA driver.
/// # unsafe { Dma::new(core::ptr::null(), core::ptr::null()) };
///
/// // #[cortex_m_rt::interrupt]
/// fn DMA7_DMA23() {
/// // Safety: only checking channels 7 and 23, which
/// // are both valid on an i.MX RT 1060 chip.
/// unsafe {
/// DMA.on_interrupt(7);
/// DMA.on_interrupt(23);
/// }
/// }
/// ```
///
/// # Safety
///
/// This should only be used when the associated DMA channel is exclusively referenced
/// by a DMA transfer future. Caller must ensure that `on_interrupt` is called in
/// the correct interrupt handler.
///
/// # Panics
///
/// Panics if `channel` is greater than or equal to the maximum number of channels.
#[inline(always)]
pub unsafe fn on_interrupt(&'static self, channel: usize) {
let channel = self.channel(channel);
if channel.is_interrupt() {
channel.clear_interrupt();
}
if channel.is_complete() | channel.is_error() {
interrupt::free(|cs| {
let waker = self.wakers[channel.channel()].borrow(cs);
let mut waker = waker.borrow_mut();
if let Some(waker) = waker.take() {
waker.wake();
}
});
}
}
}
pub(crate) type SharedWaker = Mutex<RefCell<Option<Waker>>>;
#[allow(clippy::declare_interior_mutable_const)] // Very convenient, and usage for static init deemed OK in clippy docs
pub(crate) const NO_WAKER: SharedWaker = Mutex::new(RefCell::new(None));
/// The core DMA transfer future
///
/// `Transfer` is a future that drives the DMA transfer. `Transfer` will
/// initiate a DMA transfer when it is first polled. You may then poll it
/// to understand when the transfer completes.
///
/// To cancel a transfer, drop the `Transfer`.
///
/// If you've enabled DMA interrupts, consider using [`on_interrupt`](crate::Dma::on_interrupt)
/// to wake an executor when the DMA transfer completes, The interrupt interface assumes that you've
///
/// - configured your channel to generate interrupts
/// - registered a DMA ISR with your embedded runtime
/// - unmasked the DMA interrupt in the NVIC
///
/// `Transfer` calls the unsafe [`enable`](crate::channel::Channel::enable) method to enable a
/// DMA transfer. To properly use `Transfer`, make sure that you've configured your DMA
/// channel for a valid transfer.
///
/// `Transfer` is the core DMA future used in `imxrt_dma`. For safe DMA transfers, consider
/// using
///
/// - [`Memcpy`](crate::memcpy::Memcpy) for buffer-to-buffer DMA transfers
/// - [`Read`](crate::peripheral::Read) for peripheral-to-memory DMA transfers
/// - [`Write`](crate::peripheral::Write) for memory-to-peripheral DMA transfers
///
/// `Transfer` is designed to the DMA `Channel` public interface. If you need to implement
/// your own transfer future, you may do so.
///
/// ```no_run
/// use imxrt_dma::{channel::Channel, Transfer};
///
/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
/// # async fn f() -> imxrt_dma::Result<()> {
/// let my_channel: Channel = // Acquire your channel...
/// # unsafe { DMA.channel(0) };
/// // Properly prepare your transfer...
/// // Safety: transfer properly prepared
/// unsafe { Transfer::new(&my_channel) }.await?;
/// # Ok(()) }
/// ```
pub struct Transfer<'a> {
channel: &'a Channel,
_pinned: PhantomPinned,
}
impl<'a> Transfer<'a> {
/// Create a new `Transfer` that performs the DMA transfer described by `channel`
///
/// # Safety
///
/// Assumes that the transfer is correctly defined in the DMA channel memory.
/// The transfer enables after the first call to `poll()`.
pub unsafe fn new(channel: &'a Channel) -> Self {
Transfer {
channel,
_pinned: PhantomPinned,
}
}
}
impl Future for Transfer<'_> {
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
interrupt::free(|cs| {
let waker = self.channel.waker.borrow(cs);
let mut waker = waker.borrow_mut();
*waker = Some(cx.waker().clone());
});
loop {
// This driver is only expecting to catch synchronous errors
// (those that manifest once we enable the transfer). If there
// is a misconfiguration that only the hardware detects, we expect
// to see it as soon as we loop back around after the enable.
if self.channel.is_error() {
let es = self.channel.error_status();
self.channel.clear_error();
return Poll::Ready(Err(es));
} else if self.channel.is_complete() {
self.channel.clear_complete();
return Poll::Ready(Ok(()));
} else if self.channel.is_enabled() {
return Poll::Pending;
} else {
atomic::fence(atomic::Ordering::SeqCst);
unsafe { self.channel.enable() };
}
}
}
}
impl Drop for Transfer<'_> {
fn drop(&mut self) {
self.channel.disable();
self.channel.clear_complete();
self.channel.clear_error();
interrupt::free(|cs| {
let waker = self.channel.waker.borrow(cs);
let mut waker = waker.borrow_mut();
*waker = None;
});
}
}