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}