Skip to main content

imxrt_hal/chip/drivers/
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(any(chip = "imxrt1010", chip = "imxrt1020", chip = "imxrt1060"))]
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    pub(super) const SAI_DMA_RX_MAPPING: [u32; 3] = [19, 21, 83];
72    pub(super) const SAI_DMA_TX_MAPPING: [u32; 3] = [20, 22, 84];
73}
74#[cfg(chip = "imxrt1170")]
75mod mappings {
76    pub(super) const LPUART_DMA_RX_MAPPING: [u32; 12] =
77        [9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31];
78    pub(super) const LPUART_DMA_TX_MAPPING: [u32; 12] =
79        [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30];
80
81    pub(super) const LPSPI_DMA_RX_MAPPING: [u32; 6] = [36, 38, 40, 42, 44, 46];
82    pub(super) const LPSPI_DMA_TX_MAPPING: [u32; 6] = [37, 39, 41, 43, 45, 47];
83
84    pub(super) const SAI_DMA_RX_MAPPING: [u32; 4] = [54, 56, 58, 60];
85    pub(super) const SAI_DMA_TX_MAPPING: [u32; 4] = [55, 57, 59, 61];
86}
87use mappings::*;
88
89// LPUART
90use crate::lpuart;
91
92// Safety: a LPUART can support writes from a DMA engine into its data register.
93// The peripheral is static, so it's always a valid target for memory writes.
94unsafe impl peripheral::Destination<u8> for lpuart::Lpuart {
95    fn destination_signal(&self) -> u32 {
96        LPUART_DMA_TX_MAPPING[self.instance() as usize - 1]
97    }
98    fn destination_address(&self) -> *const u8 {
99        self.data().cast()
100    }
101    fn enable_destination(&mut self) {
102        self.enable_dma_transmit();
103    }
104    fn disable_destination(&mut self) {
105        self.disable_dma_transmit();
106    }
107}
108
109// Safety: a LPUART can support reads performed by a DMA engine from its data
110// register. The peripheral is static and always valid for reading.
111unsafe impl peripheral::Source<u8> for lpuart::Lpuart {
112    fn source_signal(&self) -> u32 {
113        LPUART_DMA_RX_MAPPING[self.instance() as usize - 1]
114    }
115    fn source_address(&self) -> *const u8 {
116        self.data().cast()
117    }
118    fn enable_source(&mut self) {
119        self.enable_dma_receive();
120    }
121    fn disable_source(&mut self) {
122        self.disable_dma_receive();
123    }
124}
125
126impl lpuart::Lpuart {
127    /// Returns the instance number for this LPUART peripheral.
128    ///
129    /// This is used by chip-specific code for DMA signal mapping.
130    fn instance(&self) -> u8 {
131        ral::lpuart::number(&*self.lpuart).unwrap()
132    }
133
134    /// Use a DMA channel to write data to the UART peripheral
135    ///
136    /// Completes when all data in `buffer` has been written to the UART
137    /// peripheral.
138    pub fn dma_write<'a>(
139        &'a mut self,
140        channel: &'a mut Channel,
141        buffer: &'a [u8],
142    ) -> peripheral::Write<'a, Self, u8> {
143        peripheral::write(channel, buffer, self)
144    }
145
146    /// Use a DMA channel to read data from the UART peripheral
147    ///
148    /// Completes when `buffer` is filled.
149    pub fn dma_read<'a>(
150        &'a mut self,
151        channel: &'a mut Channel,
152        buffer: &'a mut [u8],
153    ) -> peripheral::Read<'a, Self, u8> {
154        peripheral::read(channel, self, buffer)
155    }
156}
157
158// LPSPI
159use crate::lpspi;
160
161// Safety: a LPSPI can provide data for a DMA transfer. Its receive data register
162// points to static memory.
163unsafe impl peripheral::Source<u32> for lpspi::Lpspi {
164    fn source_signal(&self) -> u32 {
165        LPSPI_DMA_RX_MAPPING[self.instance() as usize - 1]
166    }
167    fn source_address(&self) -> *const u32 {
168        self.rdr().cast()
169    }
170    fn enable_source(&mut self) {
171        self.enable_dma_receive()
172    }
173    fn disable_source(&mut self) {
174        self.disable_dma_receive();
175    }
176}
177
178// Safety: a LPSPI can receive data for a DMA transfer. Its transmit data register
179// points to static memory.
180unsafe impl peripheral::Destination<u32> for lpspi::Lpspi {
181    fn destination_signal(&self) -> u32 {
182        LPSPI_DMA_TX_MAPPING[self.instance() as usize - 1]
183    }
184    fn destination_address(&self) -> *const u32 {
185        self.tdr().cast()
186    }
187    fn enable_destination(&mut self) {
188        self.enable_dma_transmit();
189    }
190    fn disable_destination(&mut self) {
191        self.disable_dma_transmit();
192    }
193}
194
195// Safety: a LPSPI can perform bi-directional I/O from a single buffer. Reads from
196// the buffer are always performed before writes.
197unsafe impl peripheral::Bidirectional<u32> for lpspi::Lpspi {}
198
199impl lpspi::Lpspi {
200    /// Returns the instance number for this LPSPI peripheral.
201    ///
202    /// This is used by chip-specific code for DMA signal mapping.
203    fn instance(&self) -> u8 {
204        ral::lpspi::number(&*self.lpspi).unwrap()
205    }
206    /// Use a DMA channel to write data to the LPSPI peripheral.
207    ///
208    /// The future completes when all data in `buffer` has been written to the
209    /// peripheral. This call may block until space is available in the
210    /// command queue. An error indicates that there was an issue preparing the
211    /// transaction, or there was an issue while waiting for space in the command
212    /// queue.
213    pub fn dma_write<'a>(
214        &'a mut self,
215        channel: &'a mut Channel,
216        buffer: &'a [u32],
217    ) -> Result<peripheral::Write<'a, Self, u32>, lpspi::LpspiError> {
218        let mut transaction = self.bus_transaction(buffer)?;
219        transaction.set_receive_data_mask(true);
220
221        self.wait_for_transmit_fifo_space()?;
222        self.enqueue_transaction(transaction);
223        Ok(peripheral::write(channel, buffer, self))
224    }
225
226    /// Use a DMA channel to read data from the LPSPI peripheral.
227    ///
228    /// The future completes when `buffer` is filled. This call may block until
229    /// space is available in the command queue. An error indicates that there was
230    /// an issue preparing the transaction, or there was an issue waiting for space
231    /// in the command queue.
232    pub fn dma_read<'a>(
233        &'a mut self,
234        channel: &'a mut Channel,
235        buffer: &'a mut [u32],
236    ) -> Result<peripheral::Read<'a, Self, u32>, lpspi::LpspiError> {
237        let mut transaction = self.bus_transaction(buffer)?;
238        transaction.set_transmit_data_mask(true);
239
240        self.wait_for_transmit_fifo_space()?;
241        self.enqueue_transaction(transaction);
242        Ok(peripheral::read(channel, self, buffer))
243    }
244
245    /// Use a DMA channel to simultaneously read and write from a buffer
246    /// and the LPSPI peripheral.
247    ///
248    /// The future completes when `buffer` is filled and after sending `buffer` elements.
249    /// This call may block until space is available in the command queue. An error
250    /// indicates that there was an issue preparing the transaction, or there was an
251    /// issue waiting for space in the command queue.
252    pub fn dma_full_duplex<'a>(
253        &'a mut self,
254        rx: &'a mut Channel,
255        tx: &'a mut Channel,
256        buffer: &'a mut [u32],
257    ) -> Result<peripheral::FullDuplex<'a, Self, u32>, lpspi::LpspiError> {
258        let transaction = self.bus_transaction(buffer)?;
259
260        self.wait_for_transmit_fifo_space()?;
261        self.enqueue_transaction(transaction);
262        Ok(peripheral::full_duplex(rx, tx, self, buffer))
263    }
264}
265
266// ADC
267#[cfg(any(chip = "imxrt1010", chip = "imxrt1020", chip = "imxrt1060"))]
268use crate::adc;
269
270#[cfg(any(chip = "imxrt1010", chip = "imxrt1020", chip = "imxrt1060"))]
271// Safety: an ADC source adapter points to a static register that's always valid
272// for reads.
273unsafe impl peripheral::Source<u16> for adc::DmaSource {
274    fn source_signal(&self) -> u32 {
275        let n = self.instance();
276        ADC_DMA_RX_MAPPING[if n == ral::SOLE_INSTANCE {
277            n as usize
278        } else {
279            n as usize - 1
280        }]
281    }
282    fn source_address(&self) -> *const u16 {
283        self.r0().cast()
284    }
285    fn enable_source(&mut self) {
286        self.enable_dma();
287    }
288    fn disable_source(&mut self) {
289        self.disable_dma();
290    }
291}
292
293// SAI
294#[cfg(any(
295    chip = "imxrt1010",
296    chip = "imxrt1020",
297    chip = "imxrt1060",
298    chip = "imxrt1170"
299))]
300use crate::sai;
301
302#[cfg(any(
303    chip = "imxrt1010",
304    chip = "imxrt1020",
305    chip = "imxrt1060",
306    chip = "imxrt1170"
307))]
308// Safety: a SAI transmitter can receive data for a DMA transfer. Its transmit
309// data register (TDR) points to static memory that's always valid for writes.
310unsafe impl peripheral::Destination<u32> for sai::Tx {
311    fn destination_signal(&self) -> u32 {
312        let instance = ral::sai::number(&*self.sai).unwrap();
313        SAI_DMA_TX_MAPPING[instance as usize - 1]
314    }
315    fn destination_address(&self) -> *const u32 {
316        self.tdr(self.channel())
317    }
318    fn enable_destination(&mut self) {
319        self.enable_dma_transmit();
320    }
321    fn disable_destination(&mut self) {
322        self.disable_dma_transmit();
323    }
324}
325
326#[cfg(any(
327    chip = "imxrt1010",
328    chip = "imxrt1020",
329    chip = "imxrt1060",
330    chip = "imxrt1170"
331))]
332// Safety: a SAI receiver can provide data for a DMA transfer. Its receive
333// data register (RDR) points to static memory that's always valid for reads.
334unsafe impl peripheral::Source<u32> for sai::Rx {
335    fn source_signal(&self) -> u32 {
336        let instance = ral::sai::number(&*self.sai).unwrap();
337        SAI_DMA_RX_MAPPING[instance as usize - 1]
338    }
339    fn source_address(&self) -> *const u32 {
340        self.rdr(self.channel())
341    }
342    fn enable_source(&mut self) {
343        self.enable_dma_receive();
344    }
345    fn disable_source(&mut self) {
346        self.disable_dma_receive();
347    }
348}