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];