esp_hal/dma/pdma/
mod.rs

1//! # Direct Memory Access
2//!
3//! ## Overview
4//! The `pdma` module is part of the DMA driver of `ESP32` and `ESP32-S2`.
5//!
6//! This module provides efficient direct data transfer capabilities between
7//! peripherals and memory without involving the CPU. It enables bidirectional
8//! data transfers through DMA channels, making it particularly useful for
9//! high-speed data transfers, such as [SPI] and [I2S] communication.
10//!
11//! [SPI]: ../spi/index.html
12//! [I2S]: ../i2s/index.html
13
14use portable_atomic::AtomicBool;
15
16use crate::{
17    DriverMode,
18    asynch::AtomicWaker,
19    dma::{
20        Channel,
21        DmaChannel,
22        DmaChannelConvert,
23        DmaChannelExt,
24        DmaEligible,
25        DmaPeripheral,
26        DmaRxInterrupt,
27        DmaTxInterrupt,
28        InterruptAccess,
29        InterruptHandler,
30        RegisterAccess,
31    },
32    handler,
33    interrupt::Priority,
34    peripherals::Interrupt,
35};
36
37#[cfg(soc_has_copy_dma)]
38mod copy;
39#[cfg(soc_has_crypto_dma)]
40mod crypto;
41mod i2s;
42mod spi;
43
44#[cfg(soc_has_copy_dma)]
45pub use copy::{CopyDmaRxChannel, CopyDmaTxChannel};
46#[cfg(soc_has_crypto_dma)]
47pub use crypto::{CryptoDmaRxChannel, CryptoDmaTxChannel};
48use i2s::I2sRegisterBlock;
49pub use i2s::{AnyI2sDmaChannel, AnyI2sDmaRxChannel, AnyI2sDmaTxChannel};
50use spi::SpiRegisterBlock;
51pub use spi::{AnySpiDmaChannel, AnySpiDmaRxChannel, AnySpiDmaTxChannel};
52
53#[doc(hidden)]
54pub trait PdmaChannel: crate::private::Sealed {
55    type RegisterBlock;
56
57    fn register_block(&self) -> &Self::RegisterBlock;
58    fn tx_waker(&self) -> &'static AtomicWaker;
59    fn rx_waker(&self) -> &'static AtomicWaker;
60    fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool;
61
62    fn peripheral_interrupt(&self) -> Interrupt;
63    fn async_handler(&self) -> InterruptHandler;
64    fn rx_async_flag(&self) -> &'static AtomicBool;
65    fn tx_async_flag(&self) -> &'static AtomicBool;
66}
67
68macro_rules! impl_pdma_channel {
69    ($peri:ident, $register_block:ident, $instance:ident, $int:ident, [$($compatible:ident),*]) => {
70        paste::paste! {
71            use $crate::peripherals::[< $instance >];
72            impl<'d> DmaChannel for $instance<'d> {
73                type Rx = [<$peri DmaRxChannel>]<'d>;
74                type Tx = [<$peri DmaTxChannel>]<'d>;
75
76                unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) { unsafe {
77                    (
78                        [<$peri DmaRxChannel>](Self::steal().into()),
79                        [<$peri DmaTxChannel>](Self::steal().into()),
80                    )
81                }}
82            }
83
84            impl DmaChannelExt for $instance<'_> {
85                fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
86                    [<$peri DmaRxChannel>](unsafe { Self::steal() }.into())
87                }
88                fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
89                    [<$peri DmaTxChannel>](unsafe { Self::steal() }.into())
90                }
91            }
92
93            impl PdmaChannel for $instance<'_> {
94                type RegisterBlock = $register_block;
95
96                fn register_block(&self) -> &Self::RegisterBlock {
97                    $crate::peripherals::[< $instance >]::regs()
98                }
99                fn tx_waker(&self) -> &'static AtomicWaker {
100                    static WAKER: AtomicWaker = AtomicWaker::new();
101                    &WAKER
102                }
103                fn rx_waker(&self) -> &'static AtomicWaker {
104                    static WAKER: AtomicWaker = AtomicWaker::new();
105                    &WAKER
106                }
107                fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
108                    let compatible_peripherals = [$(DmaPeripheral::$compatible),*];
109                    compatible_peripherals.contains(&peripheral)
110                }
111
112                fn peripheral_interrupt(&self) -> Interrupt {
113                    Interrupt::$int
114                }
115
116                fn async_handler(&self) -> InterruptHandler {
117                    #[handler(priority = Priority::max())]
118                    pub(crate) fn interrupt_handler() {
119                        super::asynch::handle_in_interrupt::<$instance<'static>>();
120                        super::asynch::handle_out_interrupt::<$instance<'static>>();
121                    }
122
123                    interrupt_handler
124                }
125                fn rx_async_flag(&self) -> &'static AtomicBool {
126                    static FLAG: AtomicBool = AtomicBool::new(false);
127                    &FLAG
128                }
129                fn tx_async_flag(&self) -> &'static AtomicBool {
130                    static FLAG: AtomicBool = AtomicBool::new(false);
131                    &FLAG
132                }
133            }
134
135            impl<'d> DmaChannelConvert<[<$peri DmaChannel>]<'d>> for $instance<'d> {
136                fn degrade(self) -> [<$peri DmaChannel>]<'d> {
137                    self.into()
138                }
139            }
140
141            impl<'d> DmaChannelConvert<[<$peri DmaRxChannel>]<'d>> for $instance<'d> {
142                fn degrade(self) -> [<$peri DmaRxChannel>]<'d> {
143                    [<$peri DmaRxChannel>](self.into())
144                }
145            }
146
147            impl<'d> DmaChannelConvert<[<$peri DmaTxChannel>]<'d>> for $instance<'d> {
148                fn degrade(self) -> [<$peri DmaTxChannel>]<'d> {
149                    [<$peri DmaTxChannel>](self.into())
150                }
151            }
152        }
153    };
154}
155
156impl_pdma_channel!(AnySpi, SpiRegisterBlock, DMA_SPI2, SPI2_DMA, [Spi2]);
157impl_pdma_channel!(AnySpi, SpiRegisterBlock, DMA_SPI3, SPI3_DMA, [Spi3]);
158
159#[cfg(soc_has_i2s0)]
160impl_pdma_channel!(AnyI2s, I2sRegisterBlock, DMA_I2S0, I2S0, [I2s0]);
161#[cfg(soc_has_i2s1)]
162impl_pdma_channel!(AnyI2s, I2sRegisterBlock, DMA_I2S1, I2S1, [I2s1]);
163
164// Specific peripherals use specific channels. Note that this may be overly
165// restrictive (ESP32 allows configuring 2 SPI DMA channels between 3 different
166// peripherals), but for the current set of restrictions this is sufficient.
167#[cfg(soc_has_spi2)]
168crate::dma::impl_dma_eligible!([DMA_SPI2] SPI2 => Spi2);
169#[cfg(soc_has_spi3)]
170crate::dma::impl_dma_eligible!([DMA_SPI3] SPI3 => Spi3);
171#[cfg(soc_has_i2s0)]
172crate::dma::impl_dma_eligible!([DMA_I2S0] I2S0 => I2s0);
173#[cfg(soc_has_i2s1)]
174crate::dma::impl_dma_eligible!([DMA_I2S1] I2S1 => I2s1);
175#[cfg(esp32s2)]
176use crate::peripherals::DMA_CRYPTO;
177#[cfg(esp32s2)]
178crate::dma::impl_dma_eligible!([DMA_CRYPTO] AES => Aes);
179#[cfg(esp32s2)]
180crate::dma::impl_dma_eligible!([DMA_CRYPTO] SHA => Sha);
181
182pub(super) fn init_dma_racey() {
183    #[cfg(esp32)]
184    {
185        // (only) on ESP32 we need to configure DPORT for the SPI DMA channels
186        // This assignes the DMA channels to the SPI peripherals, which is more
187        // restrictive than necessary but we currently support the same
188        // number of SPI peripherals as SPI DMA channels so it's not a big
189        // deal.
190        use crate::peripherals::DPORT;
191
192        DPORT::regs()
193            .spi_dma_chan_sel()
194            .modify(|_, w| unsafe { w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) });
195    }
196
197    #[cfg(esp32s2)]
198    {
199        // This is the only DMA channel on the S2 that needs to be enabled this way
200        // (using its own registers). Ideally this should be enabled only when
201        // the DMA channel is in use but we don't have a good mechanism for that
202        // yet. For now, we shall just turn in on forever once any DMA channel is used.
203
204        use crate::peripherals::DMA_COPY;
205
206        DMA_COPY::regs().conf().modify(|_, w| w.clk_en().set_bit());
207    }
208}
209
210impl<CH, Dm> Channel<Dm, CH>
211where
212    CH: DmaChannel,
213    Dm: DriverMode,
214{
215    /// Asserts that the channel is compatible with the given peripheral.
216    #[instability::unstable]
217    pub fn runtime_ensure_compatible(&self, peripheral: &impl DmaEligible) {
218        assert!(
219            self.tx
220                .tx_impl
221                .is_compatible_with(peripheral.dma_peripheral()),
222            "This DMA channel is not compatible with {:?}",
223            peripheral.dma_peripheral()
224        );
225    }
226}