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