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