Skip to main content

imxrt10xx/
dma.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Direct Memory Access (DMA) channels and multiplexer
6//!
7//! ## DMAMUX Channel Configuration Options
8//!
9//! | ENBL | TRIG | A_ON | Function                                                | Mode                   |
10//! |------|------|------|---------------------------------------------------------|------------------------|
11//! |   0  |   X  |   X  | DMA channel is disabled                                 | Disabled Mode          |
12//! |   1  |   0  |   0  | DMA channel is enabled with no triggering (transparent) | Normal Mode            |
13//! |   1  |   1  |   0  | DMA channel is enabled with triggering                  | Periodic Trigger Mode  |
14//! |   1  |   0  |   1  | DMA channel is always enabled                           | Always On Mode         |
15//! |   1  |   1  |   1  | DMA channel is always enabled with triggering           | Always On Trigger Mode |
16//!
17//! Implementation assumptions:
18//!
19//! - No minor loop mapping, assuming we don't need to change addresses on minor loop runs.
20//! - The driver exposes 32 DMA channels. This applies for nearly all i.MX RT 10xx chips, except for the 1011.
21//!   Accessing any DMA channel beyond 15 will index into reserved memory.
22//!
23//! When assigning DMA channels to peripherals, consider:
24//!
25//! - How you could use channels that are 16 channel IDs apart, and complete DMA transfers with signaling
26//!   from one DMA interrupt, instead of two separate interrupts.
27//! - The first four DMA channels can be periodically scheduled from the four periodic interrupt timer (PIT)
28//!   channels. Consider reserving those first four channels if you need to regularly schedule DMA transfers
29//!   without CPU intervention.
30//! - Channel priorities may come into play when preferring DMA channels. See the reference manual for more
31//!   information on channel priorities, and how the DMA controller use priorities for scheduling.
32
33use kernel::utilities::{
34    StaticRef,
35    cells::OptionalCell,
36    registers::{
37        self, ReadOnly, ReadWrite, WriteOnly,
38        interfaces::{ReadWriteable, Readable, Writeable},
39    },
40};
41
42use core::cell::Cell;
43use core::mem;
44use core::ops::Index;
45
46use crate::ccm;
47
48/// DMA Multiplexer.
49///
50/// The multiplexer is used for routing between DMA channels and hardware
51/// peripherals. It's a detail of `DmaChannel`.
52#[repr(C)]
53struct DmaMultiplexerRegisters {
54    /// Channel configuration registers, one per channel.
55    chcfg: [ReadWrite<u32, ChannelConfiguration::Register>; 32],
56}
57
58const DMA_MUX_BASE: StaticRef<DmaMultiplexerRegisters> =
59    unsafe { StaticRef::new(0x400E_C000 as *const DmaMultiplexerRegisters) };
60
61registers::register_bitfields![u32,
62    /// Each of the DMA channels can be independently enabled/disabled and associated
63    /// with one of the DMA slots (peripheral slots or always-on slots) in the system.
64    ///
65    /// Note: Setting multiple CHCFG registers with the same source value will result in
66    /// unpredictable behavior. This is true, even if a channel is disabled (ENBL==0).
67    ///
68    /// Note: Before changing the trigger or source settings, a DMA channel must be
69    /// disabled via CHCFGn[ENBL].
70    ChannelConfiguration [
71        /// Enables the channel for DMA Mux. The DMA has separate channel
72        /// enables/disables, which should be used to disable or reconfigure
73        /// a DMA channel.
74        ENBL OFFSET(31) NUMBITS(1) [],
75        /// Enables the periodic trigger capability for the triggered DMA channel.
76        ///
77        /// 0b - Triggering is disabled. If triggering is disabled and ENBL is set,
78        /// the DMA Channel will simply route the specified source to the DMA channel.
79        /// (Normal mode)
80        ///
81        /// 1b - Triggering is enabled. If triggering is enabled and ENBL is set,
82        /// the DMA_CH_MUX is in Periodic Trigger mode.
83        TRIG OFFSET(30) NUMBITS(1) [],
84        /// DMA Channel Always Enable
85        ///
86        /// Enables the DMA Channel to be always ON.
87        /// If TRIG bit is set, the module will assert request on every trigger.
88        ///
89        /// 0b - DMA Channel Always ON function is disabled
90        /// 1b - DMA Channel Always ON function is enabled
91        A_ON OFFSET(29) NUMBITS(1) [],
92        /// DMA Channel Source (Slot Number)
93        ///
94        /// Specifies which DMA source, if any, is routed to a particular DMA channel.
95        /// See the "DMA MUX Mapping" table in the "Interrupts, DMA Events, and XBAR
96        /// Assignments" chapter for details about DMA source and channel information.
97        SOURCE OFFSET(0) NUMBITS(7) []
98    ]
99];
100
101#[repr(C, align(32))]
102struct TransferControlDescriptor {
103    saddr: ReadWrite<u32>,
104    soff: ReadWrite<u16>, // Signed number
105    attr: ReadWrite<u16, TransferAttributes::Register>,
106    nbytes: ReadWrite<u32>, // Assumes minor loop mapping is disabled (EMLM = 0)
107    slast: ReadWrite<u32>,  // Signed number
108    daddr: ReadWrite<u32>,
109    doff: ReadWrite<u16>, // Signed number
110    citer: ReadWrite<u16>,
111    dlast_sga: ReadWrite<u32>, // Signed number
112    csr: ReadWrite<u16, ControlAndStatus::Register>,
113    biter: ReadWrite<u16>,
114}
115
116impl TransferControlDescriptor {
117    fn reset(&self) {
118        self.saddr.set(0);
119        self.soff.set(0);
120        self.attr.set(0);
121        self.nbytes.set(0);
122        self.slast.set(0);
123        self.daddr.set(0);
124        self.doff.set(0);
125        self.citer.set(0);
126        self.dlast_sga.set(0);
127        self.csr.set(0);
128        self.biter.set(0);
129    }
130}
131
132const _STATIC_ASSERT_TCD_32_BYTES: [u32; 1] =
133    [0; (32 == mem::size_of::<TransferControlDescriptor>()) as usize];
134
135registers::register_bitfields![u16,
136    TransferAttributes [
137        SMOD OFFSET(11) NUMBITS(5) [],
138        SSIZE OFFSET(8) NUMBITS(3) [],
139        DMOD OFFSET(3) NUMBITS(5) [],
140        DSIZE OFFSET(0) NUMBITS(3) []
141    ],
142
143    ControlAndStatus [
144        /// Bandwidth control.
145        ///
146        /// Throttle bandwidth consumed by DMA.
147        BWC OFFSET(14) NUMBITS(2) [
148            /// No engine stalls
149            NoStalls = 0b00,
150            /// Stalls for 4 cycles after each R/W
151            FourCycles = 0b10,
152            /// Stalls for 8 cycles after each R/W
153            EightCycles = 0b11
154        ],
155        /// Major loop link channel number.
156        ///
157        /// If zero, then no channel-to-channel linking is performed
158        /// after major loop count exhaustion.
159        ///
160        /// Otherwise, the DMA engine initiates a channel service request
161        /// at the channel defined here, setting START in that channel.
162        MAJORLINKCH OFFSET(8) NUMBITS(5) [],
163        /// Channel done.
164        ///
165        /// Must be clear to write MAJORELINK or ESG
166        DONE OFFSET(7) NUMBITS(1) [],
167        /// Channel active
168        ACTIVE OFFSET(6) NUMBITS(1) [],
169        /// Enable channel-to-channel linking on major loop completion.
170        MAJORELINK OFFSET(5) NUMBITS(1) [],
171        /// Enable scatter/gatter.
172        ESG OFFSET(4) NUMBITS(1) [],
173        /// Disable request.
174        ///
175        /// If set, DMA hardware clears ERQ when the current major iteration
176        /// count reaches zero.
177        DREQ OFFSET(3) NUMBITS(1) [],
178        /// Enable interrupt when major count is half complete.
179        INTHALF OFFSET(2) NUMBITS(1) [],
180        /// Enable an interrupt when major count is complete.
181        INTMAJOR OFFSET(1) NUMBITS(1) [],
182        /// Channel start.
183        ///
184        /// When set, channel is requesting service. DMA hardware will clear this
185        /// after it starts execution.
186        START OFFSET(0) NUMBITS(1) []
187    ]
188];
189
190/// Wrapper for channel priority registers.
191///
192/// Channel priority registers cannot be accessed with
193/// normal channel indexes. This adapter makes it so that
194/// we *can* access them with channel indexes by converting
195/// the channel number to a reference to the priority
196/// register.
197#[repr(transparent)]
198struct ChannelPriorityRegisters([ReadWrite<u8, ChannelPriority::Register>; 32]);
199
200impl Index<usize> for ChannelPriorityRegisters {
201    type Output = ReadWrite<u8, ChannelPriority::Register>;
202    fn index(&self, channel: usize) -> &ReadWrite<u8, ChannelPriority::Register> {
203        // Pattern follows
204        //
205        //   3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, ...
206        //
207        // for all channels < 32. NXP keeping us on our toes.
208        let idx = 4 * (channel / 4) + (3 - (channel % 4));
209        &self.0[idx]
210    }
211}
212
213registers::register_structs! {
214    /// DMA registers.
215    DmaRegisters {
216        /// Control Register
217        (0x000 => cr: ReadWrite<u32, Control::Register>),
218        /// Error Status Register
219        (0x004 => es: ReadOnly<u32, ErrorStatus::Register>),
220        (0x008 => _reserved0),
221        /// Enable Request Register
222        (0x00C => erq: ReadWrite<u32>),
223        (0x010 => _reserved1),
224        /// Enable Error Interrupt Register
225        (0x014 => eei: ReadWrite<u32>),
226        /// Clear Enable Error Interrupt Register
227        (0x018 => ceei: WriteOnly<u8, MemoryMappedChannel::Register>),
228        /// Set Enable Error Interrupt Register
229        (0x019 => seei: WriteOnly<u8, MemoryMappedChannel::Register>),
230        /// Clear Enable Request Register
231        (0x01A => cerq: WriteOnly<u8, MemoryMappedChannel::Register>),
232        /// Set Enable Request Register
233        (0x01B => serq: WriteOnly<u8, MemoryMappedChannel::Register>),
234        /// Clear DONE Status Bit Register
235        (0x01C => cdne: WriteOnly<u8, MemoryMappedChannel::Register>),
236        /// Set START Bit Register
237        (0x01D => ssrt: WriteOnly<u8, MemoryMappedChannel::Register>),
238        /// Clear Error Register
239        (0x01E => cerr: WriteOnly<u8, MemoryMappedChannel::Register>),
240        /// Clear Interrupt Request Register
241        (0x01F => cint: WriteOnly<u8, MemoryMappedChannel::Register>),
242        (0x020 => _reserved2),
243        /// Interrupt Request Register
244        (0x024 => int: ReadWrite<u32>),
245        (0x028 => _reserved3),
246        /// Error Register
247        (0x02C => err: ReadWrite<u32>),
248        (0x030 => _reserved4),
249        /// Hardware Request Status Register
250        (0x034 => hrs: ReadOnly<u32>),
251        (0x038 => _reserved5),
252        /// Enable Asynchronous Request in Stop Register
253        (0x044 => ears: ReadWrite<u32>),
254        (0x048 => _reserved6),
255        (0x0100 => dchpri: ChannelPriorityRegisters),
256        (0x0120 => _reserved7),
257        (0x1000 => tcd: [TransferControlDescriptor; 32]),
258        (0x1400 => @END),
259    }
260}
261
262registers::register_bitfields![u8,
263    /// Used in DCHPRI registers.
264    ChannelPriority [
265        /// Enable channel premption.
266        ///
267        /// 0b - Channel n cannot be suspended by a higher priority channel's service request.
268        /// 1b - Channel n can be temporarily suspended by the service request of a higher priority channel.
269        ECP OFFSET(7) NUMBITS(1) [],
270        /// Disable Preempt Ability.
271        ///
272        /// 0b - Channel n can suspend a lower priority channel.
273        /// 1b - Channel n cannot suspend any channel, regardless of channel priority.
274        DPA OFFSET(6) NUMBITS(1) [],
275        /// Channel current group priority.
276        ///
277        /// Group priority assigned to this channel group when
278        /// fixed-priority arbitration is enabled. This field is
279        /// read- only; writes are ignored.
280        GRPPRI OFFSET(4) NUMBITS(2) [],
281        /// Channel arbitration priority.
282        CHPRI OFFSET(0) NUMBITS(4) []
283    ],
284    /// Generic bitband register for CEEI, SEEI, CERQ, SERQ, ...
285    MemoryMappedChannel [
286        /// NoOp operation.
287        ///
288        /// Disable all other bits in this register.
289        NOOP OFFSET(7) NUMBITS(1) [],
290        /// Perform this register's operation on all 32 channels.
291        ALL OFFSET(6) NUMBITS(1) [],
292        /// Channel number.
293        ///
294        /// Specify the channel to act on.
295        CHANNEL OFFSET(0) NUMBITS(5) []
296    ]
297];
298
299registers::register_bitfields![u32,
300    Control [
301        /// DMA active status.
302        ACTIVE OFFSET(31) NUMBITS(1) [],
303        /// Cancel the active transfer.
304        CX OFFSET(17) NUMBITS(1) [],
305        /// Error cancel transfer.
306        ///
307        /// Like cancel transfer (CX), but it updates the error
308        /// status register (ES) for the channel. It optionally
309        /// generates an error interrupt.
310        ECX OFFSET(16) NUMBITS(1) [],
311        /// Channel group 1 priority.
312        ///
313        /// Group 1 priority level when fixed priority group arbitration is enabled.
314        GRP1PRI OFFSET(10) NUMBITS(1) [],
315        /// Channel group 9 priority.
316        ///
317        /// Group 0 priority level when fixed priority group arbitration is enabled.
318        GRP0PRI OFFSET(8) NUMBITS(1) [],
319        /// Enable minor loop mapping.
320        ///
321        /// 0b - Disabled. TCDn.word2 is defined as a 32-bit NBYTES field.
322        /// 1b - Enabled. TCDn.word2 is redefined to include individual enable fields,
323        /// an offset field, and the NBYTES field. The individual enable fields allow
324        /// the minor loop offset to be applied to the source address, the destination
325        /// address, or both. The NBYTES field is reduced when either offset is enabled.
326        EMLM OFFSET(7) NUMBITS(1) [],
327        /// Continuous link mode.
328        CLM OFFSET(6) NUMBITS(1) [],
329        /// Halt DMA operations.
330        ///
331        /// Writing 1 stalls the start of any new channels. Executing channels may complete. Write
332        /// 0 to resume channel execution.
333        HALT OFFSET(5) NUMBITS(1) [],
334        /// Halt on Error.
335        ///
336        /// Any error sets HALT bit. Software must clear HALT.
337        HOE OFFSET(4) NUMBITS(1) [],
338        /// Enable round robin group arbitration.
339        ///
340        /// 0b - Fixed priority arbitration is used for selection among the groups.
341        /// 1b - Round robin arbitration is used for selection among the groups.
342        ERGA OFFSET(3) NUMBITS(1) [],
343        /// Enable round robin channel arbitration.
344        ///
345        /// 0b - Fixed priority arbitration is used for channel selection within each group.
346        /// 1b - Round robin arbitration is used for channel selection within each group.
347        ERCA OFFSET(2) NUMBITS(1) [],
348        /// Enable debug.
349        ///
350        /// Set to stall the start of a new channel when in debug mode.
351        EDBG OFFSET(1) NUMBITS(1) []
352    ],
353    ErrorStatus [
354        /// At least one ERR bit is set.
355        VLD OFFSET(31) NUMBITS(1) [],
356        /// Transfer canceled.
357        ///
358        /// Last recorded entry was a cancelled transfer by error cancel transfer input.
359        ECX OFFSET(16) NUMBITS(1) [],
360        /// Group priority error.
361        ///
362        /// Priority groups are not unique.
363        GPE OFFSET(15) NUMBITS(1) [],
364        /// Channel priority error.
365        ///
366        /// Channel priorities within a group are not unique.
367        CPE OFFSET(14) NUMBITS(1) [],
368        /// Error channel number.
369        ///
370        /// Channel number of last recorded error, excluding group or channel priority errors,
371        /// or last error canceled transfer.
372        ERRCHN OFFSET(8) NUMBITS(5) [],
373        /// Source address error.
374        ///
375        /// Configuration error detected in the TCDn_SADDR field. TCDn_SADDR is inconsistent with TCDn_ATTR[SSIZE].
376        SAE OFFSET(7) NUMBITS(1) [],
377        /// Source offset error.
378        ///
379        /// Configuration error detected in the TCDn_SOFF field. TCDn_SOFF is inconsistent with TCDn_ATTR[SSIZE].
380        SOE OFFSET(6) NUMBITS(1) [],
381        /// Destination address error.
382        ///
383        /// Configuration error detected in the TCDn_DADDR field. TCDn_DADDR is inconsistent with TCDn_ATTR[DSIZE].
384        DAE OFFSET(5) NUMBITS(1) [],
385        /// Destination offset error.
386        ///
387        /// Configuration error detected in the TCDn_DOFF field. TCDn_DOFF is inconsistent with TCDn_ATTR[DSIZE].
388        DOE OFFSET(4) NUMBITS(1) [],
389        /// NBYTES/CITER configuration error.
390        NCE OFFSET(3) NUMBITS(1) [],
391        /// Scatter/Gather Configuration Error.
392        SGE OFFSET(2) NUMBITS(1) [],
393        /// Source bus error.
394        SBE OFFSET(1) NUMBITS(1) [],
395        /// Destination bus error.
396        DBE OFFSET(0) NUMBITS(1) []
397    ]
398];
399
400const DMA_BASE: StaticRef<DmaRegisters> =
401    unsafe { StaticRef::new(0x400E_8000 as *const DmaRegisters) };
402
403/// A DMA channel.
404///
405/// `DmaChannel` can coordinate the transfer of data between buffers and
406/// peripherals without processor intervention.
407pub struct DmaChannel {
408    base: StaticRef<DmaRegisters>,
409    mux: StaticRef<DmaMultiplexerRegisters>,
410    channel: usize,
411    client: OptionalCell<&'static dyn DmaClient>,
412    hardware_source: Cell<Option<DmaHardwareSource>>,
413}
414
415/// Describes a type that can be transferred via DMA.
416///
417/// This trait is sealed and cannot be implemented outside of this
418/// crate. However, it may be used outside of this crate.
419pub trait DmaElement: private::Sealed {
420    /// An identifier describing the data transfer size
421    ///
422    /// See TCD\[SSIZE\] and TCD\[DSIZE\] for more information.
423    #[doc(hidden)] // Crate implementation detail
424    const DATA_TRANSFER_ID: u16;
425}
426
427/// Details for the sealed `DmaElement` trait.
428///
429/// See the Rust API Guidelines, and the Sealed trait pattern,
430/// for more information.
431///
432/// <https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed>
433mod private {
434    pub trait Sealed {}
435    impl Sealed for u8 {}
436    impl Sealed for u16 {}
437    impl Sealed for u32 {}
438    impl Sealed for u64 {}
439}
440
441impl DmaElement for u8 {
442    const DATA_TRANSFER_ID: u16 = 0;
443}
444
445impl DmaElement for u16 {
446    const DATA_TRANSFER_ID: u16 = 1;
447}
448
449impl DmaElement for u32 {
450    const DATA_TRANSFER_ID: u16 = 2;
451}
452
453impl DmaElement for u64 {
454    const DATA_TRANSFER_ID: u16 = 3;
455}
456
457impl DmaChannel {
458    /// Allocate a new DMA channel.
459    ///
460    /// Note that channels 0 through 3 are the only channels capable of periodic
461    /// transfers. Consider reserving these channels for that use case.
462    pub(crate) const fn new(channel: usize) -> Self {
463        DmaChannel {
464            base: DMA_BASE,
465            mux: DMA_MUX_BASE,
466            channel,
467            client: const { OptionalCell::empty() },
468            hardware_source: Cell::new(None),
469        }
470    }
471
472    /// Reset the DMA channel's TCD.
473    fn reset_tcd(&self) {
474        self.base.tcd[self.channel].reset();
475    }
476
477    /// Set the client using this DMA channel.
478    ///
479    /// This should be invoked by the client itself.
480    pub(crate) fn set_client(&self, client: &'static dyn DmaClient, source: DmaHardwareSource) {
481        self.client.set(client);
482        self.trigger_from_hardware(source);
483    }
484
485    /// Set this DMA channel to trigger from a hardware source.
486    fn trigger_from_hardware(&self, source: DmaHardwareSource) {
487        let chcfg = &self.mux.chcfg[self.channel];
488        chcfg.set(0);
489        chcfg.write(
490            ChannelConfiguration::ENBL::SET + ChannelConfiguration::SOURCE.val(source as u32),
491        );
492        self.hardware_source.set(Some(source));
493    }
494
495    /// Manually start the DMA transfer.
496    ///
497    /// A manual trigger is useful for memory-to-memory DMA transfers. If you're sending
498    /// or receiving data from a peripheral, use `trigger_from_hardware()`.
499    pub fn trigger_manually(&self) {
500        self.base
501            .ssrt
502            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
503    }
504
505    /// Returns `true` is this DMA channel is actively receiving a hardware signal.
506    ///
507    /// A hardware signal comes from an associated peripheral, indicating a request
508    /// for transfer. It's important to deassert the hardware before disabling a
509    /// DMA channel. This gives you an opportunity to check for hardware signal.
510    ///
511    /// Returns `false` if the DMA channel is disabled, or if there's no associated
512    /// hardware (see `trigger_from_hardware()`).
513    pub fn is_hardware_signaling(&self) -> bool {
514        self.base.hrs.get() & (1 << self.channel) != 0
515    }
516
517    /// Enables this DMA channel.
518    pub fn enable(&self) {
519        self.base
520            .serq
521            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
522    }
523
524    /// Disables this DMA channel.
525    pub fn disable(&self) {
526        self.base
527            .cerq
528            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
529    }
530
531    /// Clear the interrupt associated with this DMA channel.
532    fn clear_interrupt(&self) {
533        self.base
534            .cint
535            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
536    }
537
538    /// Returns `true` if this DMA channel generated an interrupt.
539    pub fn is_interrupt(&self) -> bool {
540        self.base.int.get() & (1 << self.channel) != 0
541    }
542
543    /// Returns `true` if this DMA channel has completed its transfer.
544    pub fn is_complete(&self) -> bool {
545        self.base.tcd[self.channel]
546            .csr
547            .is_set(ControlAndStatus::DONE)
548    }
549
550    /// Clears the completion of this DMA channel.
551    fn clear_complete(&self) {
552        self.base
553            .cdne
554            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
555    }
556
557    /// Returns `true` if this DMA channel is in an error state.
558    pub fn is_error(&self) -> bool {
559        self.base.err.get() & (1 << self.channel) != 0
560    }
561
562    /// Clears the error flag for this channel.
563    fn clear_error(&self) {
564        self.base
565            .cerr
566            .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
567    }
568
569    /// Returns `true` if this DMA channel is in an active transfer.
570    pub fn is_active(&self) -> bool {
571        self.base.tcd[self.channel]
572            .csr
573            .is_set(ControlAndStatus::ACTIVE)
574    }
575
576    /// Set a buffer of data as the source of a DMA transfer.
577    ///
578    /// Safety: caller is responsible for ensuring the buffer's lifetime is
579    /// valid for the life of the transfer.
580    pub unsafe fn set_source_buffer<T: DmaElement>(&self, buffer: &[T]) {
581        let tcd = &self.base.tcd[self.channel];
582        tcd.saddr.set(buffer.as_ptr() as u32);
583        tcd.soff.set(mem::size_of::<T>() as u16);
584        tcd.attr.modify(
585            TransferAttributes::SSIZE.val(T::DATA_TRANSFER_ID) + TransferAttributes::SMOD.val(0),
586        );
587        tcd.nbytes.set(mem::size_of::<T>() as u32);
588        tcd.slast.set((-(buffer.len() as i32)) as u32);
589        let iterations: u16 = buffer.len() as u16;
590        tcd.biter.set(iterations);
591        tcd.citer.set(iterations);
592    }
593
594    /// Set a buffer of data as the destination of a DMA receive.
595    ///
596    /// Safety: caller is responsible for ensuring the buffer's lifetime is
597    /// valid for the life of the transfer.
598    pub unsafe fn set_destination_buffer<T: DmaElement>(&self, buffer: &mut [T]) {
599        let tcd = &self.base.tcd[self.channel];
600        tcd.daddr.set(buffer.as_mut_ptr() as u32);
601        tcd.doff.set(mem::size_of::<T>() as u16);
602        tcd.attr.modify(
603            TransferAttributes::DSIZE.val(T::DATA_TRANSFER_ID) + TransferAttributes::DMOD.val(0),
604        );
605        tcd.nbytes.set(mem::size_of::<T>() as u32);
606        tcd.dlast_sga.set((-(buffer.len() as i32)) as u32);
607        let iterations: u16 = buffer.len() as u16;
608        tcd.biter.set(iterations);
609        tcd.citer.set(iterations);
610    }
611
612    /// Set the source of a DMA transfer.
613    ///
614    /// Use `set_source` if the transfer source is a peripheral register.
615    ///
616    /// Safety: caller responsible for ensuring pointer's lifetime is valid
617    /// for the transfer.
618    pub unsafe fn set_source<T: DmaElement>(&self, source: *const T) {
619        let tcd = &self.base.tcd[self.channel];
620        tcd.saddr.set(source as u32);
621        tcd.soff.set(0);
622        tcd.attr.modify(
623            TransferAttributes::SSIZE.val(T::DATA_TRANSFER_ID) + TransferAttributes::SMOD.val(0),
624        );
625        tcd.nbytes.set(mem::size_of::<T>() as u32);
626        tcd.slast.set(0);
627    }
628
629    /// Set the destination of a DMA transfer.
630    ///
631    /// Use `set_destination` if the tranfer destination is a peripheral register.
632    ///
633    /// Safety: caller responsible for ensuring pointer's lifetime is valid for
634    /// the transfer.
635    pub unsafe fn set_destination<T: DmaElement>(&self, dest: *const T) {
636        let tcd = &self.base.tcd[self.channel];
637        tcd.daddr.set(dest as u32);
638        tcd.doff.set(0);
639        tcd.attr.modify(
640            TransferAttributes::DSIZE.val(T::DATA_TRANSFER_ID) + TransferAttributes::DMOD.val(0),
641        );
642        tcd.nbytes.set(mem::size_of::<T>() as u32);
643        tcd.dlast_sga.set(0);
644    }
645
646    /// Configures the DMA channel to automatically disable when the transfer completes.
647    pub fn set_disable_on_completion(&self, dreq: bool) {
648        self.base.tcd[self.channel]
649            .csr
650            .modify(ControlAndStatus::DREQ.val(dreq as u16));
651    }
652
653    /// Configures the DMA channel to interrupt when complete, or when there
654    /// is an error.
655    pub fn set_interrupt_on_completion(&self, intr: bool) {
656        self.base.tcd[self.channel]
657            .csr
658            .modify(ControlAndStatus::INTMAJOR.val(intr as u16));
659        if intr {
660            self.base
661                .seei
662                .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
663        } else {
664            self.base
665                .ceei
666                .write(MemoryMappedChannel::CHANNEL.val(self.channel as u8));
667        }
668    }
669
670    /// Handle an interrupt.
671    ///
672    /// Assumes that the caller knows that this DMA channel was the source of the
673    /// interrupt, or the cause of a DMA error. See `is_interrupt()` and `is_error()`.
674    /// The implementation panics if there is neither an error, or an interrupt.
675    pub fn handle_interrupt(&self) {
676        self.clear_interrupt();
677        let hardware_source = self.hardware_source.clone().get().unwrap();
678        let result = if self.is_error() {
679            self.clear_error();
680            self.clear_complete();
681            self.disable();
682            Err(hardware_source)
683        } else if self.is_complete() {
684            self.clear_complete();
685            Ok(hardware_source)
686        } else {
687            unreachable!(
688                "DMA Channel {} should either be complete, or in an error state",
689                self.channel
690            );
691        };
692        self.client.map(|client| client.transfer_complete(result));
693    }
694}
695
696/// Indicates success or failure when executing a DMA transfer
697///
698/// An `Ok(source)` describes a successful DMA transfer to / from the hardware
699/// source. An `Err(source)` describes a failed DMA transfer.
700pub type Result = core::result::Result<DmaHardwareSource, DmaHardwareSource>;
701
702/// A type that responds to DMA completion events
703pub trait DmaClient {
704    /// Handle the completion of a DMA transfer, which either succeeded or failed.
705    fn transfer_complete(&self, source: Result);
706}
707
708/// DMA hardware sources.
709///
710/// Extend this to add support for more DMA-powered peripherals.
711/// To understand where the numbers come from, see Chapter 4,
712/// DMA Mux, to find the DMA request signals (iMXRT1060RM, Rev 2).
713#[derive(Clone, Copy, PartialEq, Eq)]
714#[repr(u32)]
715pub enum DmaHardwareSource {
716    Lpuart1Transfer = 2,
717    Lpuart1Receive = 3,
718    Lpuart2Transfer = 66,
719    Lpuart2Receive = 67,
720}
721
722/// The DMA peripheral exposes DMA channels.
723pub struct Dma<'a> {
724    /// The DMA channels
725    pub channels: [DmaChannel; 32],
726    /// DMA clock gate
727    clock_gate: ccm::PeripheralClock<'a>,
728    /// DMA registers.
729    registers: StaticRef<DmaRegisters>,
730}
731
732impl<'a> Dma<'a> {
733    /// Create a DMA peripheral.
734    pub const fn new(ccm: &'a ccm::Ccm) -> Self {
735        Dma {
736            channels: const {
737                [
738                    DmaChannel::new(0),
739                    DmaChannel::new(1),
740                    DmaChannel::new(2),
741                    DmaChannel::new(3),
742                    DmaChannel::new(4),
743                    DmaChannel::new(5),
744                    DmaChannel::new(6),
745                    DmaChannel::new(7),
746                    DmaChannel::new(8),
747                    DmaChannel::new(9),
748                    DmaChannel::new(10),
749                    DmaChannel::new(11),
750                    DmaChannel::new(12),
751                    DmaChannel::new(13),
752                    DmaChannel::new(14),
753                    DmaChannel::new(15),
754                    DmaChannel::new(16),
755                    DmaChannel::new(17),
756                    DmaChannel::new(18),
757                    DmaChannel::new(19),
758                    DmaChannel::new(20),
759                    DmaChannel::new(21),
760                    DmaChannel::new(22),
761                    DmaChannel::new(23),
762                    DmaChannel::new(24),
763                    DmaChannel::new(25),
764                    DmaChannel::new(26),
765                    DmaChannel::new(27),
766                    DmaChannel::new(28),
767                    DmaChannel::new(29),
768                    DmaChannel::new(30),
769                    DmaChannel::new(31),
770                ]
771            },
772            clock_gate: ccm::PeripheralClock::ccgr5(ccm, ccm::HCLK5::DMA),
773            registers: DMA_BASE,
774        }
775    }
776
777    /// Returns the interface that controls the DMA clock
778    pub fn clock(&self) -> &(impl kernel::platform::chip::ClockInterface + '_) {
779        &self.clock_gate
780    }
781
782    /// Reset all DMA transfer control descriptors.
783    ///
784    /// You should reset these descriptors shortly after system
785    /// initialization, and before using a DMA channel.
786    pub fn reset_tcds(&self) {
787        for channel in &self.channels {
788            channel.reset_tcd();
789        }
790    }
791
792    /// Returns a DMA channel that has an error.
793    ///
794    /// This will be faster than searching all DMA channels
795    /// for an error flag. However, if more than one DMA channel
796    /// has an error, there's no guarantee which will be returned
797    /// first. You should continue calling, and clearing errors,
798    /// until this returns `None`.
799    pub fn error_channel(&self) -> Option<&DmaChannel> {
800        let es = self.registers.es.extract();
801        es.is_set(ErrorStatus::VLD).then(|| {
802            let idx = es.read(ErrorStatus::ERRCHN) as usize;
803            &self.channels[idx]
804        })
805    }
806}