Skip to main content

esp_hal/dma/gdma/
mod.rs

1//! # General Direct Memory Access (GDMA)
2//!
3//! ## Overview
4//! GDMA is a feature that allows peripheral-to-memory, memory-to-peripheral,
5//! and memory-to-memory data transfer at high speed. The CPU is not involved in
6//! the GDMA transfer and therefore is more efficient with less workload.
7//!
8//! The `GDMA` module provides multiple DMA channels, each capable of managing
9//! data transfer for various peripherals.
10
11use core::marker::PhantomData;
12
13use crate::{
14    dma::*,
15    handler,
16    interrupt::Priority,
17    peripherals::{DMA, Interrupt, pac},
18};
19
20#[cfg_attr(dma_gdma_version = "1", path = "ahb_v1.rs")]
21#[cfg_attr(dma_gdma_version = "2", path = "ahb_v2.rs")]
22mod implementation;
23
24/// An arbitrary GDMA channel
25#[derive(Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub struct AnyGdmaChannel<'d> {
28    channel: u8,
29    _lifetime: PhantomData<&'d mut ()>,
30}
31
32impl AnyGdmaChannel<'_> {
33    #[cfg_attr(any(esp32c2, esp32c61), expect(unused))]
34    pub(crate) unsafe fn clone_unchecked(&self) -> Self {
35        Self {
36            channel: self.channel,
37            _lifetime: PhantomData,
38        }
39    }
40}
41
42impl crate::private::Sealed for AnyGdmaChannel<'_> {}
43impl<'d> DmaChannel for AnyGdmaChannel<'d> {
44    type Rx = AnyGdmaRxChannel<'d>;
45    type Tx = AnyGdmaTxChannel<'d>;
46
47    unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
48        (
49            AnyGdmaRxChannel {
50                channel: self.channel,
51                _lifetime: PhantomData,
52            },
53            AnyGdmaTxChannel {
54                channel: self.channel,
55                _lifetime: PhantomData,
56            },
57        )
58    }
59}
60
61/// An arbitrary GDMA RX channel
62#[derive(Debug)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64pub struct AnyGdmaRxChannel<'d> {
65    channel: u8,
66    _lifetime: PhantomData<&'d mut ()>,
67}
68
69impl<'d> DmaChannelConvert<AnyGdmaRxChannel<'d>> for AnyGdmaRxChannel<'d> {
70    fn degrade(self) -> AnyGdmaRxChannel<'d> {
71        self
72    }
73}
74
75/// An arbitrary GDMA TX channel
76#[derive(Debug)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78pub struct AnyGdmaTxChannel<'d> {
79    channel: u8,
80    _lifetime: PhantomData<&'d mut ()>,
81}
82
83impl<'d> DmaChannelConvert<AnyGdmaTxChannel<'d>> for AnyGdmaTxChannel<'d> {
84    fn degrade(self) -> AnyGdmaTxChannel<'d> {
85        self
86    }
87}
88
89use crate::asynch::AtomicWaker;
90
91static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
92static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
93
94cfg_if::cfg_if! {
95    if #[cfg(any(esp32c2, esp32c3))] {
96        use portable_atomic::AtomicBool;
97        static TX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
98        static RX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
99    }
100}
101
102impl crate::private::Sealed for AnyGdmaTxChannel<'_> {}
103impl DmaTxChannel for AnyGdmaTxChannel<'_> {}
104
105impl crate::private::Sealed for AnyGdmaRxChannel<'_> {}
106impl DmaRxChannel for AnyGdmaRxChannel<'_> {}
107
108impl<CH: DmaChannel, Dm: DriverMode> Channel<Dm, CH> {
109    /// Asserts that the channel is compatible with the given peripheral.
110    pub fn runtime_ensure_compatible<P: DmaEligible>(&self, _peripheral: &P) {
111        // No runtime checks; GDMA channels are compatible with any peripheral
112    }
113}
114
115macro_rules! impl_channel {
116    ($num:literal, $interrupt_in:ident $(, $interrupt_out:ident)? ) => {
117        paste::paste! {
118            use $crate::peripherals::[<DMA_CH $num>];
119            impl [<DMA_CH $num>]<'_> {
120                fn handler_in() -> Option<InterruptHandler> {
121                    $crate::if_set! {
122                        $({
123                            // $interrupt_out is present, meaning we have split handlers
124                            #[handler(priority = Priority::max())]
125                            fn interrupt_handler_in() {
126                                $crate::ignore!($interrupt_out);
127                                super::asynch::handle_in_interrupt::<[< DMA_CH $num >]<'static>>();
128                            }
129                            Some(interrupt_handler_in)
130                        })?,
131                        {
132                            #[handler(priority = Priority::max())]
133                            fn interrupt_handler() {
134                                super::asynch::handle_in_interrupt::<[< DMA_CH $num >]<'static>>();
135                                super::asynch::handle_out_interrupt::<[< DMA_CH $num >]<'static>>();
136                            }
137                            Some(interrupt_handler)
138                        }
139                    }
140                }
141
142                fn isr_in() -> Option<Interrupt> {
143                    Some(Interrupt::$interrupt_in)
144                }
145
146                fn handler_out() -> Option<InterruptHandler> {
147                    $crate::if_set! {
148                        $({
149                            #[handler(priority = Priority::max())]
150                            fn interrupt_handler_out() {
151                                $crate::ignore!($interrupt_out);
152                                super::asynch::handle_out_interrupt::<[< DMA_CH $num >]<'static>>();
153                            }
154                            Some(interrupt_handler_out)
155                        })?,
156                        None
157                    }
158                }
159
160                fn isr_out() -> Option<Interrupt> {
161                    $crate::if_set! { $(Some(Interrupt::$interrupt_out))?, None }
162                }
163            }
164
165            impl<'d> DmaChannel for [<DMA_CH $num>]<'d> {
166                type Rx = AnyGdmaRxChannel<'d>;
167                type Tx = AnyGdmaTxChannel<'d>;
168
169                unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
170                    (
171                        AnyGdmaRxChannel {
172                            channel: $num,
173                            _lifetime: core::marker::PhantomData,
174                        },
175                        AnyGdmaTxChannel {
176                            channel: $num,
177                            _lifetime: core::marker::PhantomData,
178                        },
179                    )
180                }
181            }
182
183            impl<'d> DmaChannelConvert<AnyGdmaChannel<'d>> for [<DMA_CH $num>]<'d> {
184                fn degrade(self) -> AnyGdmaChannel<'d> {
185                    AnyGdmaChannel {
186                        channel: $num,
187                        _lifetime: core::marker::PhantomData,
188                    }
189                }
190            }
191
192            impl<'d> DmaChannelConvert<AnyGdmaRxChannel<'d>> for [<DMA_CH $num>]<'d> {
193                fn degrade(self) -> AnyGdmaRxChannel<'d> {
194                    AnyGdmaRxChannel {
195                        channel: $num,
196                        _lifetime: core::marker::PhantomData,
197                    }
198                }
199            }
200
201            impl<'d> DmaChannelConvert<AnyGdmaTxChannel<'d>> for [<DMA_CH $num>]<'d> {
202                fn degrade(self) -> AnyGdmaTxChannel<'d> {
203                    AnyGdmaTxChannel {
204                        channel: $num,
205                        _lifetime: core::marker::PhantomData,
206                    }
207                }
208            }
209
210            impl DmaChannelExt for [<DMA_CH $num>]<'_> {
211                fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
212                    AnyGdmaRxChannel {
213                        channel: $num,
214                        _lifetime: core::marker::PhantomData,
215                    }
216                }
217
218                fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
219                    AnyGdmaTxChannel {
220                        channel: $num,
221                        _lifetime: core::marker::PhantomData,
222                    }
223                }
224            }
225        }
226    };
227}
228
229const CHANNEL_COUNT: usize = cfg!(soc_has_dma_ch0) as usize
230    + cfg!(soc_has_dma_ch1) as usize
231    + cfg!(soc_has_dma_ch2) as usize
232    + cfg!(soc_has_dma_ch3) as usize
233    + cfg!(soc_has_dma_ch4) as usize;
234
235cfg_if::cfg_if! {
236    if #[cfg(dma_separate_in_out_interrupts)] {
237        #[cfg(soc_has_dma_ch0)]
238        impl_channel!(0, DMA_IN_CH0, DMA_OUT_CH0);
239        #[cfg(soc_has_dma_ch1)]
240        impl_channel!(1, DMA_IN_CH1, DMA_OUT_CH1);
241        #[cfg(soc_has_dma_ch2)]
242        impl_channel!(2, DMA_IN_CH2, DMA_OUT_CH2);
243        #[cfg(soc_has_dma_ch3)]
244        impl_channel!(3, DMA_IN_CH3, DMA_OUT_CH3);
245        #[cfg(soc_has_dma_ch4)]
246        impl_channel!(4, DMA_IN_CH4, DMA_OUT_CH4);
247    } else {
248        #[cfg(soc_has_dma_ch0)]
249        impl_channel!(0, DMA_CH0);
250        #[cfg(soc_has_dma_ch1)]
251        impl_channel!(1, DMA_CH1);
252        #[cfg(soc_has_dma_ch2)]
253        impl_channel!(2, DMA_CH2);
254        #[cfg(soc_has_dma_ch3)]
255        impl_channel!(3, DMA_CH3);
256        #[cfg(soc_has_dma_ch4)]
257        impl_channel!(4, DMA_CH4);
258    }
259}
260
261for_each_peripheral! {
262    (dma_eligible $(( $peri:ident, $name:ident, $id:literal )),*) => {
263        crate::dma::impl_dma_eligible! {
264            AnyGdmaChannel {
265                $($peri => $name,)*
266            }
267        }
268    };
269}
270
271pub(super) fn init_dma_racey() {
272    DMA::regs()
273        .misc_conf()
274        .modify(|_, w| w.ahbm_rst_inter().set_bit());
275    DMA::regs()
276        .misc_conf()
277        .modify(|_, w| w.ahbm_rst_inter().clear_bit());
278    DMA::regs().misc_conf().modify(|_, w| w.clk_en().set_bit());
279
280    implementation::setup();
281}