1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Direct Memory Access (DMA) driver for i.MX RT processors.
//!
//! `imxrt-dma` provides
//!
//! - an unsafe API for defining and scheduling transfers with DMA `Channel`s.
//! - safe DMA futures for memcpy, peripheral-to-memory, and memory-to-peripheral
//!   transfers.
//!
//! This DMA driver may be re-exported from a hardware abstraction layer
//! (HAL). If it is, you should use the safer APIs provided by your HAL.
//!
//! # Getting started
//!
//! To allocate a [`Dma`](crate::Dma) driver, you'll need to know
//!
//! 1. the location of the DMA controller registers.
//! 2. the location of the DMAMUX registers.
//! 3. the number of DMA channels supported by your chip.
//!
//! These parameters depend on the i.MX RT chip you're targeting. If you're
//! already using [`imxrt-ral`](https://docs.rs/imxrt-ral), consider using the
//! `DMA` and `DMAMUX` constants for the addresses. You're always responsible
//! for configuring the number of DMA channels.
//!
//! With those three parameters, assign a `Dma` to a static. Then, use that
//! object to create DMA [`Channel`](crate::channel::Channel)s.
//!
//! ```
//! use imxrt_dma::Dma;
//! # const DMA_PTR: *const () = core::ptr::null() as _;
//! # const DMAMUX_PTR: *const () = core::ptr::null() as  _;
//!
//! // Safety: addresses and channel count are valid for this target.
//! static DMA: Dma<32> = unsafe { Dma::new(DMA_PTR, DMAMUX_PTR) };
//!
//! // Safety: we only allocate one DMA channel 7 object.
//! let mut channel = unsafe { DMA.channel(7) };
//! ```
//!
//! Once you have a channel, you can use the higher-level DMA APIs, like
//!
//! - [`memcpy`](crate::memcpy::memcpy) for memory copies.
//! - [`write`](crate::peripheral::write) to transmit data from memory to
//! a peripheral.
//! - [`read`](crate::peripheral::read) to receive data from a peripheral.
//! - [`full_duplex`](crate::peripheral::full_duplex) to read / write with a
//! peripheral using a single buffer.
//!
//! Peripheral transfers depends on a peripheral's DMA support. These are signaled
//! through various [`peripheral`](crate::peripheral) traits.
//!
//! For a lower-level API, use the [`channel`](crate::channel) objects and helper
//! functions.
//!
//! ### License
//!
//! Licensed under either of
//!
//! - [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) ([LICENSE-APACHE](./LICENSE-APACHE))
//! - [MIT License](http://opensource.org/licenses/MIT) ([LICENSE-MIT](./LICENSE-MIT))
//!
//! at your option.
//!
//! Unless you explicitly state otherwise, any contribution intentionally submitted
//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
//! dual licensed as above, without any additional terms or conditions.

#![no_std]

pub mod channel;
mod element;
mod error;
mod interrupt;
pub mod memcpy;
pub mod peripheral;
mod ral;

pub use element::Element;
pub use error::Error;
pub use interrupt::Transfer;
pub use ral::tcd::BandwidthControl;

/// A DMA result
pub type Result<T> = core::result::Result<T, Error>;

/// A DMA driver.
///
/// This DMA driver manages the DMA controller and the multiplexer.
/// It's configured with pointers to both peripherals.
///
/// `Dma` allocates [`Channel`](channel::Channel)s. `Channel` provides
/// the interface for scheduling transfers.
pub struct Dma<const CHANNELS: usize> {
    controller: ral::Static<ral::dma::RegisterBlock>,
    multiplexer: ral::Static<ral::dmamux::RegisterBlock>,
    wakers: [SharedWaker; CHANNELS],
}

// Safety: OK to allocate a DMA driver in a static context.
unsafe impl<const CHANNELS: usize> Sync for Dma<CHANNELS> {}

impl<const CHANNELS: usize> Dma<CHANNELS> {
    /// Create the DMA driver.
    ///
    /// Note that this can evaluate at compile time. Consider using this to
    /// expose a `Dma` through your higher-level API that you can use to
    /// allocate DMA channels.
    ///
    /// `CHANNELS` specifies the total number of channels supported by the DMA
    /// controller. It's referenced when allocating channels.
    ///
    /// # Safety
    ///
    /// Caller must make sure that `controller` is a pointer to the start of the
    /// DMA controller register block. Caller must also make sure that
    /// `multiplexer` is a pointer to the start of the DMA multiplexer. Both
    /// pointers must be valid for your MCU.
    ///
    /// An incorrect `CHANNELS` value prevents proper bounds checking when
    /// allocating channels. This may result in DMA channels that point to
    /// invalid memory.
    pub const unsafe fn new(controller: *const (), multiplexer: *const ()) -> Self {
        Self {
            controller: ral::Static(controller.cast()),
            multiplexer: ral::Static(multiplexer.cast()),
            wakers: [NO_WAKER; CHANNELS],
        }
    }
}

use interrupt::{SharedWaker, NO_WAKER};