imxrt_hal/chip/
dma.rs

1//! Chip-specific DMA APIs.
2
3use crate::ral;
4
5use crate::common::dma::channel::Channel;
6
7/// The total number of DMA channels.
8///
9/// This is 16 the minumum number of DMA channels available for all
10/// i.MX RT processors. However, if you've enabled a chip family feature
11/// and that chip family has more than 16 DMA channels, this value may
12/// increase.
13pub const CHANNEL_COUNT: usize = crate::chip::config::DMA_CHANNEL_COUNT;
14
15/// The DMA driver.
16///
17/// This DMA driver is configured for your chip. You could use it to allocate
18/// channels; however, it's safer to use [`channels()`] to acquire your DMA
19/// channels.
20///
21/// This driver provides access to the wakers that are provided to DMA futures.
22/// If you're implementing an async runtime, you should use this object to wake
23/// DMA channel wakers on interrupt.
24// Safety: pointers come from RAL, and are correct for the selected chip.
25// DMA channel count is also valid for the chip selection.
26pub static DMA: crate::common::dma::Dma<{ CHANNEL_COUNT }> = unsafe {
27    crate::common::dma::Dma::new(
28        crate::ral::dma::DMA.cast(),
29        crate::ral::dmamux::DMAMUX.cast(),
30    )
31};
32
33/// Allocate all DMA channels.
34///
35/// The number of channels depends on [`CHANNEL_COUNT`], which may change
36/// depending on feature selection.
37///
38/// When `channels` returns, each element is guaranteed to hold `Some` channel.
39/// You may then `take()` the channel, leaving `None` in its place.
40pub fn channels(_: ral::dma::DMA, _: ral::dmamux::DMAMUX) -> [Option<Channel>; CHANNEL_COUNT] {
41    const NO_CHANNEL: Option<Channel> = None;
42    let mut channels: [Option<Channel>; CHANNEL_COUNT] = [NO_CHANNEL; CHANNEL_COUNT];
43
44    for (idx, channel) in channels.iter_mut().enumerate() {
45        // Safety: we own the DMA instances, so we're OK to fabricate the channels.
46        // It would be unsafe for the user to subsequently access the DMA instances.
47        let mut chan = unsafe { DMA.channel(idx) };
48        chan.reset();
49        *channel = Some(chan);
50    }
51    channels
52}
53
54//
55// Peripheral implementations.
56//
57// These depend on DMA MUX peripheral mappings, which are chip (family) specific.
58//
59use crate::dma::peripheral;
60
61#[cfg(family = "imxrt10xx")]
62mod mappings {
63    pub(super) const LPUART_DMA_RX_MAPPING: [u32; 8] = [3, 67, 5, 69, 7, 71, 9, 73];
64    pub(super) const LPUART_DMA_TX_MAPPING: [u32; 8] = [2, 66, 4, 68, 6, 70, 8, 72];
65
66    pub(super) const LPSPI_DMA_RX_MAPPING: [u32; 4] = [13, 77, 15, 79];
67    pub(super) const LPSPI_DMA_TX_MAPPING: [u32; 4] = [14, 78, 16, 80];
68
69    pub(super) const ADC_DMA_RX_MAPPING: [u32; 2] = [24, 88];
70}
71#[cfg(family = "imxrt11xx")]
72mod mappings {
73    pub(super) const LPUART_DMA_RX_MAPPING: [u32; 12] =
74        [9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31];
75    pub(super) const LPUART_DMA_TX_MAPPING: [u32; 12] =
76        [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30];
77
78    pub(super) const LPSPI_DMA_RX_MAPPING: [u32; 6] = [36, 38, 40, 42, 44, 46];
79    pub(super) const LPSPI_DMA_TX_MAPPING: [u32; 6] = [37, 39, 41, 43, 45, 47];
80}
81use mappings::*;
82
83// LPUART
84use crate::lpuart;
85
86// Safety: a LPUART can support writes from a DMA engine into its data register.
87// The peripheral is static, so it's always a valid target for memory writes.
88unsafe impl<P, const N: u8> peripheral::Destination<u8> for lpuart::Lpuart<P, N> {
89    fn destination_signal(&self) -> u32 {
90        LPUART_DMA_TX_MAPPING[N as usize - 1]
91    }
92    fn destination_address(&self) -> *const u8 {
93        self.data().cast()
94    }
95    fn enable_destination(&mut self) {
96        self.enable_dma_transmit();
97    }
98    fn disable_destination(&mut self) {
99        self.disable_dma_transmit();
100    }
101}
102
103// Safety: a LPUART can support reads performed by a DMA engine from its data
104// register. The peripheral is static and always valid for reading.
105unsafe impl<P, const N: u8> peripheral::Source<u8> for lpuart::Lpuart<P, N> {
106    fn source_signal(&self) -> u32 {
107        LPUART_DMA_RX_MAPPING[N as usize - 1]
108    }
109    fn source_address(&self) -> *const u8 {
110        self.data().cast()
111    }
112    fn enable_source(&mut self) {
113        self.enable_dma_receive();
114    }
115    fn disable_source(&mut self) {
116        self.disable_dma_receive();
117    }
118}
119
120impl<P, const N: u8> lpuart::Lpuart<P, N> {
121    /// Use a DMA channel to write data to the UART peripheral
122    ///
123    /// Completes when all data in `buffer` has been written to the UART
124    /// peripheral.
125    pub fn dma_write<'a>(
126        &'a mut self,
127        channel: &'a mut Channel,
128        buffer: &'a [u8],
129    ) -> peripheral::Write<'a, Self, u8> {
130        peripheral::write(channel, buffer, self)
131    }
132
133    /// Use a DMA channel to read data from the UART peripheral
134    ///
135    /// Completes when `buffer` is filled.
136    pub fn dma_read<'a>(
137        &'a mut self,
138        channel: &'a mut Channel,
139        buffer: &'a mut [u8],
140    ) -> peripheral::Read<'a, Self, u8> {
141        peripheral::read(channel, self, buffer)
142    }
143}
144
145// LPSPI
146use crate::lpspi;
147
148// Safety: a LPSPI can provide data for a DMA transfer. Its receive data register
149// points to static memory.
150unsafe impl<P, const N: u8> peripheral::Source<u32> for lpspi::Lpspi<P, N> {
151    fn source_signal(&self) -> u32 {
152        LPSPI_DMA_RX_MAPPING[N as usize - 1]
153    }
154    fn source_address(&self) -> *const u32 {
155        self.rdr().cast()
156    }
157    fn enable_source(&mut self) {
158        self.enable_dma_receive()
159    }
160    fn disable_source(&mut self) {
161        self.disable_dma_receive();
162    }
163}
164
165// Safety: a LPSPI can receive data for a DMA transfer. Its transmit data register
166// points to static memory.
167unsafe impl<P, const N: u8> peripheral::Destination<u32> for lpspi::Lpspi<P, N> {
168    fn destination_signal(&self) -> u32 {
169        LPSPI_DMA_TX_MAPPING[N as usize - 1]
170    }
171    fn destination_address(&self) -> *const u32 {
172        self.tdr().cast()
173    }
174    fn enable_destination(&mut self) {
175        self.enable_dma_transmit();
176    }
177    fn disable_destination(&mut self) {
178        self.disable_dma_transmit();
179    }
180}
181
182// Safety: a LPSPI can perform bi-directional I/O from a single buffer. Reads from
183// the buffer are always performed before writes.
184unsafe impl<P, const N: u8> peripheral::Bidirectional<u32> for lpspi::Lpspi<P, N> {}
185
186impl<P, const N: u8> lpspi::Lpspi<P, N> {
187    /// Use a DMA channel to write data to the LPSPI peripheral.
188    ///
189    /// The future completes when all data in `buffer` has been written to the
190    /// peripheral. This call may block until space is available in the
191    /// command queue. An error indicates that there was an issue preparing the
192    /// transaction, or there was an issue while waiting for space in the command
193    /// queue.
194    pub fn dma_write<'a>(
195        &'a mut self,
196        channel: &'a mut Channel,
197        buffer: &'a [u32],
198    ) -> Result<peripheral::Write<'a, Self, u32>, lpspi::LpspiError> {
199        let mut transaction = lpspi::Transaction::new_u32s(buffer)?;
200        transaction.bit_order = self.bit_order();
201
202        transaction.receive_data_mask = true;
203        self.wait_for_transmit_fifo_space()?;
204        self.enqueue_transaction(&transaction);
205        Ok(peripheral::write(channel, buffer, self))
206    }
207
208    /// Use a DMA channel to read data from the LPSPI peripheral.
209    ///
210    /// The future completes when `buffer` is filled. This call may block until
211    /// space is available in the command queue. An error indicates that there was
212    /// an issue preparing the transaction, or there was an issue waiting for space
213    /// in the command queue.
214    pub fn dma_read<'a>(
215        &'a mut self,
216        channel: &'a mut Channel,
217        buffer: &'a mut [u32],
218    ) -> Result<peripheral::Read<'a, Self, u32>, lpspi::LpspiError> {
219        let mut transaction = lpspi::Transaction::new_u32s(buffer)?;
220        transaction.bit_order = self.bit_order();
221
222        transaction.transmit_data_mask = true;
223        self.wait_for_transmit_fifo_space()?;
224        self.enqueue_transaction(&transaction);
225        Ok(peripheral::read(channel, self, buffer))
226    }
227
228    /// Use a DMA channel to simultaneously read and write from a buffer
229    /// and the LPSPI peripheral.
230    ///
231    /// The future completes when `buffer` is filled and after sending `buffer` elements.
232    /// This call may block until space is available in the command queue. An error
233    /// indicates that there was an issue preparing the transaction, or there was an
234    /// issue waiting for space in the command queue.
235    pub fn dma_full_duplex<'a>(
236        &'a mut self,
237        rx: &'a mut Channel,
238        tx: &'a mut Channel,
239        buffer: &'a mut [u32],
240    ) -> Result<peripheral::FullDuplex<'a, Self, u32>, lpspi::LpspiError> {
241        let mut transaction = lpspi::Transaction::new_u32s(buffer)?;
242        transaction.bit_order = self.bit_order();
243
244        self.wait_for_transmit_fifo_space()?;
245        self.enqueue_transaction(&transaction);
246        Ok(peripheral::full_duplex(rx, tx, self, buffer))
247    }
248}
249
250// ADC
251#[cfg(family = "imxrt10xx")]
252use crate::adc;
253
254#[cfg(family = "imxrt10xx")]
255// Safety: an ADC source adapter points to a static register that's always valid
256// for reads.
257unsafe impl<P, const N: u8> peripheral::Source<u16> for adc::DmaSource<P, N> {
258    fn source_signal(&self) -> u32 {
259        ADC_DMA_RX_MAPPING[if N == ral::SOLE_INSTANCE {
260            N as usize
261        } else {
262            N as usize - 1
263        }]
264    }
265    fn source_address(&self) -> *const u16 {
266        self.r0().cast()
267    }
268    fn enable_source(&mut self) {
269        self.enable_dma();
270    }
271    fn disable_source(&mut self) {
272        self.disable_dma();
273    }
274}