esp_hal/dma/
gdma.rs

1//! # General Direct Memory Access (GMDA)
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//!
11//! ## Configuration
12//! GDMA peripheral can be initializes using the `new` function, which requires
13//! a DMA peripheral instance and a clock control reference.
14//!
15//! <em>PS: Note that the number of DMA channels is chip-specific.</em>
16
17use core::marker::PhantomData;
18
19use critical_section::CriticalSection;
20
21use crate::{
22    dma::*,
23    handler,
24    interrupt::Priority,
25    peripherals::{DMA, Interrupt, pac},
26};
27
28/// An arbitrary GDMA channel
29#[derive(Debug)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub struct AnyGdmaChannel<'d> {
32    channel: u8,
33    _lifetime: PhantomData<&'d mut ()>,
34}
35
36impl crate::private::Sealed for AnyGdmaChannel<'_> {}
37impl<'d> DmaChannel for AnyGdmaChannel<'d> {
38    type Rx = AnyGdmaRxChannel<'d>;
39    type Tx = AnyGdmaTxChannel<'d>;
40
41    unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
42        (
43            AnyGdmaRxChannel {
44                channel: self.channel,
45                _lifetime: PhantomData,
46            },
47            AnyGdmaTxChannel {
48                channel: self.channel,
49                _lifetime: PhantomData,
50            },
51        )
52    }
53}
54
55/// An arbitrary GDMA RX channel
56#[derive(Debug)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub struct AnyGdmaRxChannel<'d> {
59    channel: u8,
60    _lifetime: PhantomData<&'d mut ()>,
61}
62
63impl<'d> DmaChannelConvert<AnyGdmaRxChannel<'d>> for AnyGdmaRxChannel<'d> {
64    fn degrade(self) -> AnyGdmaRxChannel<'d> {
65        self
66    }
67}
68
69/// An arbitrary GDMA TX channel
70#[derive(Debug)]
71#[cfg_attr(feature = "defmt", derive(defmt::Format))]
72pub struct AnyGdmaTxChannel<'d> {
73    channel: u8,
74    _lifetime: PhantomData<&'d mut ()>,
75}
76
77impl<'d> DmaChannelConvert<AnyGdmaTxChannel<'d>> for AnyGdmaTxChannel<'d> {
78    fn degrade(self) -> AnyGdmaTxChannel<'d> {
79        self
80    }
81}
82
83use crate::asynch::AtomicWaker;
84
85static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
86static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
87
88cfg_if::cfg_if! {
89    if #[cfg(any(esp32c2, esp32c3))] {
90        use portable_atomic::AtomicBool;
91        static TX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
92        static RX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
93    }
94}
95
96impl crate::private::Sealed for AnyGdmaTxChannel<'_> {}
97impl DmaTxChannel for AnyGdmaTxChannel<'_> {}
98
99impl AnyGdmaTxChannel<'_> {
100    #[inline(always)]
101    fn ch(&self) -> &pac::dma::ch::CH {
102        DMA::regs().ch(self.channel as usize)
103    }
104
105    #[cfg(any(esp32c2, esp32c3))]
106    #[inline(always)]
107    fn int(&self) -> &pac::dma::int_ch::INT_CH {
108        DMA::regs().int_ch(self.channel as usize)
109    }
110    #[inline(always)]
111    #[cfg(any(esp32c6, esp32h2))]
112    fn int(&self) -> &pac::dma::out_int_ch::OUT_INT_CH {
113        DMA::regs().out_int_ch(self.channel as usize)
114    }
115    #[cfg(esp32s3)]
116    #[inline(always)]
117    fn int(&self) -> &pac::dma::ch::out_int::OUT_INT {
118        DMA::regs().ch(self.channel as usize).out_int()
119    }
120}
121
122impl RegisterAccess for AnyGdmaTxChannel<'_> {
123    fn reset(&self) {
124        let conf0 = self.ch().out_conf0();
125        conf0.modify(|_, w| w.out_rst().set_bit());
126        conf0.modify(|_, w| w.out_rst().clear_bit());
127    }
128
129    fn set_burst_mode(&self, burst_mode: BurstConfig) {
130        self.ch()
131            .out_conf0()
132            .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
133    }
134
135    fn set_descr_burst_mode(&self, burst_mode: bool) {
136        self.ch()
137            .out_conf0()
138            .modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
139    }
140
141    fn set_priority(&self, priority: DmaPriority) {
142        self.ch()
143            .out_pri()
144            .write(|w| unsafe { w.tx_pri().bits(priority as u8) });
145    }
146
147    fn set_peripheral(&self, peripheral: u8) {
148        self.ch()
149            .out_peri_sel()
150            .modify(|_, w| unsafe { w.peri_out_sel().bits(peripheral) });
151    }
152
153    fn set_link_addr(&self, address: u32) {
154        self.ch()
155            .out_link()
156            .modify(|_, w| unsafe { w.outlink_addr().bits(address) });
157    }
158
159    fn start(&self) {
160        self.ch()
161            .out_link()
162            .modify(|_, w| w.outlink_start().set_bit());
163    }
164
165    fn stop(&self) {
166        self.ch()
167            .out_link()
168            .modify(|_, w| w.outlink_stop().set_bit());
169    }
170
171    fn restart(&self) {
172        self.ch()
173            .out_link()
174            .modify(|_, w| w.outlink_restart().set_bit());
175    }
176
177    fn set_check_owner(&self, check_owner: Option<bool>) {
178        self.ch()
179            .out_conf1()
180            .modify(|_, w| w.out_check_owner().bit(check_owner.unwrap_or(true)));
181    }
182
183    #[cfg(esp32s3)]
184    fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
185        self.ch()
186            .out_conf1()
187            .modify(|_, w| unsafe { w.out_ext_mem_bk_size().bits(size as u8) });
188    }
189
190    #[cfg(psram_dma)]
191    fn can_access_psram(&self) -> bool {
192        true
193    }
194}
195
196impl TxRegisterAccess for AnyGdmaTxChannel<'_> {
197    fn is_fifo_empty(&self) -> bool {
198        cfg_if::cfg_if! {
199            if #[cfg(esp32s3)] {
200                 self.ch().outfifo_status().read().outfifo_empty_l3().bit_is_set()
201            } else {
202                 self.ch().outfifo_status().read().outfifo_empty().bit_is_set()
203            }
204        }
205    }
206
207    fn set_auto_write_back(&self, enable: bool) {
208        self.ch()
209            .out_conf0()
210            .modify(|_, w| w.out_auto_wrback().bit(enable));
211    }
212
213    fn last_dscr_address(&self) -> usize {
214        self.ch()
215            .out_eof_des_addr()
216            .read()
217            .out_eof_des_addr()
218            .bits() as _
219    }
220
221    fn async_handler(&self) -> Option<InterruptHandler> {
222        match self.channel {
223            0 => DMA_CH0::handler_out(),
224            #[cfg(not(esp32c2))]
225            1 => DMA_CH1::handler_out(),
226            #[cfg(not(esp32c2))]
227            2 => DMA_CH2::handler_out(),
228            #[cfg(esp32s3)]
229            3 => DMA_CH3::handler_out(),
230            #[cfg(esp32s3)]
231            4 => DMA_CH4::handler_out(),
232            _ => unreachable!(),
233        }
234    }
235
236    fn peripheral_interrupt(&self) -> Option<Interrupt> {
237        match self.channel {
238            0 => DMA_CH0::isr_out(),
239            #[cfg(not(esp32c2))]
240            1 => DMA_CH1::isr_out(),
241            #[cfg(not(esp32c2))]
242            2 => DMA_CH2::isr_out(),
243            #[cfg(esp32s3)]
244            3 => DMA_CH3::isr_out(),
245            #[cfg(esp32s3)]
246            4 => DMA_CH4::isr_out(),
247            _ => unreachable!(),
248        }
249    }
250}
251
252impl InterruptAccess<DmaTxInterrupt> for AnyGdmaTxChannel<'_> {
253    fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
254        self.int().ena().modify(|_, w| {
255            for interrupt in interrupts {
256                match interrupt {
257                    DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
258                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable),
259                    DmaTxInterrupt::Eof => w.out_eof().bit(enable),
260                    DmaTxInterrupt::Done => w.out_done().bit(enable),
261                };
262            }
263            w
264        });
265    }
266
267    fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
268        let mut result = EnumSet::new();
269
270        let int_ena = self.int().ena().read();
271        if int_ena.out_total_eof().bit_is_set() {
272            result |= DmaTxInterrupt::TotalEof;
273        }
274        if int_ena.out_dscr_err().bit_is_set() {
275            result |= DmaTxInterrupt::DescriptorError;
276        }
277        if int_ena.out_eof().bit_is_set() {
278            result |= DmaTxInterrupt::Eof;
279        }
280        if int_ena.out_done().bit_is_set() {
281            result |= DmaTxInterrupt::Done;
282        }
283
284        result
285    }
286
287    fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
288        self.int().clr().write(|w| {
289            for interrupt in interrupts.into() {
290                match interrupt {
291                    DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
292                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(),
293                    DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
294                    DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
295                };
296            }
297            w
298        });
299    }
300
301    fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
302        let mut result = EnumSet::new();
303
304        let int_raw = self.int().raw().read();
305        if int_raw.out_total_eof().bit_is_set() {
306            result |= DmaTxInterrupt::TotalEof;
307        }
308        if int_raw.out_dscr_err().bit_is_set() {
309            result |= DmaTxInterrupt::DescriptorError;
310        }
311        if int_raw.out_eof().bit_is_set() {
312            result |= DmaTxInterrupt::Eof;
313        }
314        if int_raw.out_done().bit_is_set() {
315            result |= DmaTxInterrupt::Done;
316        }
317
318        result
319    }
320
321    fn waker(&self) -> &'static AtomicWaker {
322        &TX_WAKERS[self.channel as usize]
323    }
324
325    fn is_async(&self) -> bool {
326        cfg_if::cfg_if! {
327            if #[cfg(any(esp32c2, esp32c3))] {
328                TX_IS_ASYNC[self.channel as usize].load(portable_atomic::Ordering::Acquire)
329            } else {
330                true
331            }
332        }
333    }
334
335    fn set_async(&self, _is_async: bool) {
336        cfg_if::cfg_if! {
337            if #[cfg(any(esp32c2, esp32c3))] {
338                TX_IS_ASYNC[self.channel as usize].store(_is_async, portable_atomic::Ordering::Release);
339            }
340        }
341    }
342}
343
344impl crate::private::Sealed for AnyGdmaRxChannel<'_> {}
345impl DmaRxChannel for AnyGdmaRxChannel<'_> {}
346
347impl AnyGdmaRxChannel<'_> {
348    #[inline(always)]
349    fn ch(&self) -> &pac::dma::ch::CH {
350        DMA::regs().ch(self.channel as usize)
351    }
352
353    #[cfg(any(esp32c2, esp32c3))]
354    #[inline(always)]
355    fn int(&self) -> &pac::dma::int_ch::INT_CH {
356        DMA::regs().int_ch(self.channel as usize)
357    }
358
359    #[inline(always)]
360    #[cfg(any(esp32c6, esp32h2))]
361    fn int(&self) -> &pac::dma::in_int_ch::IN_INT_CH {
362        DMA::regs().in_int_ch(self.channel as usize)
363    }
364
365    #[cfg(esp32s3)]
366    #[inline(always)]
367    fn int(&self) -> &pac::dma::ch::in_int::IN_INT {
368        DMA::regs().ch(self.channel as usize).in_int()
369    }
370}
371
372impl RegisterAccess for AnyGdmaRxChannel<'_> {
373    fn reset(&self) {
374        let conf0 = self.ch().in_conf0();
375        conf0.modify(|_, w| w.in_rst().set_bit());
376        conf0.modify(|_, w| w.in_rst().clear_bit());
377    }
378
379    fn set_burst_mode(&self, burst_mode: BurstConfig) {
380        self.ch()
381            .in_conf0()
382            .modify(|_, w| w.in_data_burst_en().bit(burst_mode.is_burst_enabled()));
383    }
384
385    fn set_descr_burst_mode(&self, burst_mode: bool) {
386        self.ch()
387            .in_conf0()
388            .modify(|_, w| w.indscr_burst_en().bit(burst_mode));
389    }
390
391    fn set_priority(&self, priority: DmaPriority) {
392        self.ch()
393            .in_pri()
394            .write(|w| unsafe { w.rx_pri().bits(priority as u8) });
395    }
396
397    fn set_peripheral(&self, peripheral: u8) {
398        self.ch()
399            .in_peri_sel()
400            .modify(|_, w| unsafe { w.peri_in_sel().bits(peripheral) });
401    }
402
403    fn set_link_addr(&self, address: u32) {
404        self.ch()
405            .in_link()
406            .modify(|_, w| unsafe { w.inlink_addr().bits(address) });
407    }
408
409    fn start(&self) {
410        self.ch()
411            .in_link()
412            .modify(|_, w| w.inlink_start().set_bit());
413    }
414
415    fn stop(&self) {
416        self.ch().in_link().modify(|_, w| w.inlink_stop().set_bit());
417    }
418
419    fn restart(&self) {
420        self.ch()
421            .in_link()
422            .modify(|_, w| w.inlink_restart().set_bit());
423    }
424
425    fn set_check_owner(&self, check_owner: Option<bool>) {
426        self.ch()
427            .in_conf1()
428            .modify(|_, w| w.in_check_owner().bit(check_owner.unwrap_or(true)));
429    }
430
431    #[cfg(esp32s3)]
432    fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
433        self.ch()
434            .in_conf1()
435            .modify(|_, w| unsafe { w.in_ext_mem_bk_size().bits(size as u8) });
436    }
437
438    #[cfg(psram_dma)]
439    fn can_access_psram(&self) -> bool {
440        true
441    }
442}
443
444impl RxRegisterAccess for AnyGdmaRxChannel<'_> {
445    fn set_mem2mem_mode(&self, value: bool) {
446        self.ch()
447            .in_conf0()
448            .modify(|_, w| w.mem_trans_en().bit(value));
449    }
450
451    fn async_handler(&self) -> Option<InterruptHandler> {
452        match self.channel {
453            0 => DMA_CH0::handler_in(),
454            #[cfg(not(esp32c2))]
455            1 => DMA_CH1::handler_in(),
456            #[cfg(not(esp32c2))]
457            2 => DMA_CH2::handler_in(),
458            #[cfg(esp32s3)]
459            3 => DMA_CH3::handler_in(),
460            #[cfg(esp32s3)]
461            4 => DMA_CH4::handler_in(),
462            _ => unreachable!(),
463        }
464    }
465
466    fn peripheral_interrupt(&self) -> Option<Interrupt> {
467        match self.channel {
468            0 => DMA_CH0::isr_in(),
469            #[cfg(not(esp32c2))]
470            1 => DMA_CH1::isr_in(),
471            #[cfg(not(esp32c2))]
472            2 => DMA_CH2::isr_in(),
473            #[cfg(esp32s3)]
474            3 => DMA_CH3::isr_in(),
475            #[cfg(esp32s3)]
476            4 => DMA_CH4::isr_in(),
477            _ => unreachable!(),
478        }
479    }
480}
481
482impl InterruptAccess<DmaRxInterrupt> for AnyGdmaRxChannel<'_> {
483    fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
484        self.int().ena().modify(|_, w| {
485            for interrupt in interrupts {
486                match interrupt {
487                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
488                    DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
489                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable),
490                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable),
491                    DmaRxInterrupt::Done => w.in_done().bit(enable),
492                };
493            }
494            w
495        });
496    }
497
498    fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
499        let mut result = EnumSet::new();
500
501        let int_ena = self.int().ena().read();
502        if int_ena.in_dscr_err().bit_is_set() {
503            result |= DmaRxInterrupt::DescriptorError;
504        }
505        if int_ena.in_dscr_empty().bit_is_set() {
506            result |= DmaRxInterrupt::DescriptorEmpty;
507        }
508        if int_ena.in_suc_eof().bit_is_set() {
509            result |= DmaRxInterrupt::SuccessfulEof;
510        }
511        if int_ena.in_err_eof().bit_is_set() {
512            result |= DmaRxInterrupt::ErrorEof;
513        }
514        if int_ena.in_done().bit_is_set() {
515            result |= DmaRxInterrupt::Done;
516        }
517
518        result
519    }
520
521    fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
522        self.int().clr().write(|w| {
523            for interrupt in interrupts.into() {
524                match interrupt {
525                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
526                    DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
527                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(),
528                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(),
529                    DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
530                };
531            }
532            w
533        });
534    }
535
536    fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
537        let mut result = EnumSet::new();
538
539        let int_raw = self.int().raw().read();
540        if int_raw.in_dscr_err().bit_is_set() {
541            result |= DmaRxInterrupt::DescriptorError;
542        }
543        if int_raw.in_dscr_empty().bit_is_set() {
544            result |= DmaRxInterrupt::DescriptorEmpty;
545        }
546        if int_raw.in_suc_eof().bit_is_set() {
547            result |= DmaRxInterrupt::SuccessfulEof;
548        }
549        if int_raw.in_err_eof().bit_is_set() {
550            result |= DmaRxInterrupt::ErrorEof;
551        }
552        if int_raw.in_done().bit_is_set() {
553            result |= DmaRxInterrupt::Done;
554        }
555
556        result
557    }
558
559    fn waker(&self) -> &'static AtomicWaker {
560        &RX_WAKERS[self.channel as usize]
561    }
562
563    fn is_async(&self) -> bool {
564        cfg_if::cfg_if! {
565            if #[cfg(any(esp32c2, esp32c3))] {
566                RX_IS_ASYNC[self.channel as usize].load(portable_atomic::Ordering::Acquire)
567            } else {
568                true
569            }
570        }
571    }
572
573    fn set_async(&self, _is_async: bool) {
574        cfg_if::cfg_if! {
575            if #[cfg(any(esp32c2, esp32c3))] {
576                RX_IS_ASYNC[self.channel as usize].store(_is_async, portable_atomic::Ordering::Release);
577            }
578        }
579    }
580}
581
582impl<CH: DmaChannel, Dm: DriverMode> Channel<Dm, CH> {
583    /// Asserts that the channel is compatible with the given peripheral.
584    pub fn runtime_ensure_compatible<P: DmaEligible>(&self, _peripheral: &P) {
585        // No runtime checks; GDMA channels are compatible with any peripheral
586    }
587}
588
589macro_rules! impl_channel {
590    ($num:literal, $interrupt_in:ident $(, $interrupt_out:ident)? ) => {
591        paste::paste! {
592            use $crate::peripherals::[<DMA_CH $num>];
593            impl [<DMA_CH $num>]<'_> {
594                fn handler_in() -> Option<InterruptHandler> {
595                    $crate::if_set! {
596                        $({
597                            // $interrupt_out is present, meaning we have split handlers
598                            #[handler(priority = Priority::max())]
599                            fn interrupt_handler_in() {
600                                $crate::ignore!($interrupt_out);
601                                super::asynch::handle_in_interrupt::<[< DMA_CH $num >]<'static>>();
602                            }
603                            Some(interrupt_handler_in)
604                        })?,
605                        {
606                            #[handler(priority = Priority::max())]
607                            fn interrupt_handler() {
608                                super::asynch::handle_in_interrupt::<[< DMA_CH $num >]<'static>>();
609                                super::asynch::handle_out_interrupt::<[< DMA_CH $num >]<'static>>();
610                            }
611                            Some(interrupt_handler)
612                        }
613                    }
614                }
615
616                fn isr_in() -> Option<Interrupt> {
617                    Some(Interrupt::$interrupt_in)
618                }
619
620                fn handler_out() -> Option<InterruptHandler> {
621                    $crate::if_set! {
622                        $({
623                            #[handler(priority = Priority::max())]
624                            fn interrupt_handler_out() {
625                                $crate::ignore!($interrupt_out);
626                                super::asynch::handle_out_interrupt::<[< DMA_CH $num >]<'static>>();
627                            }
628                            Some(interrupt_handler_out)
629                        })?,
630                        None
631                    }
632                }
633
634                fn isr_out() -> Option<Interrupt> {
635                    $crate::if_set! { $(Some(Interrupt::$interrupt_out))?, None }
636                }
637            }
638
639            impl<'d> DmaChannel for [<DMA_CH $num>]<'d> {
640                type Rx = AnyGdmaRxChannel<'d>;
641                type Tx = AnyGdmaTxChannel<'d>;
642
643                unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
644                    (
645                        AnyGdmaRxChannel {
646                            channel: $num,
647                            _lifetime: core::marker::PhantomData,
648                        },
649                        AnyGdmaTxChannel {
650                            channel: $num,
651                            _lifetime: core::marker::PhantomData,
652                        },
653                    )
654                }
655            }
656
657            impl<'d> DmaChannelConvert<AnyGdmaChannel<'d>> for [<DMA_CH $num>]<'d> {
658                fn degrade(self) -> AnyGdmaChannel<'d> {
659                    AnyGdmaChannel {
660                        channel: $num,
661                        _lifetime: core::marker::PhantomData,
662                    }
663                }
664            }
665
666            impl<'d> DmaChannelConvert<AnyGdmaRxChannel<'d>> for [<DMA_CH $num>]<'d> {
667                fn degrade(self) -> AnyGdmaRxChannel<'d> {
668                    AnyGdmaRxChannel {
669                        channel: $num,
670                        _lifetime: core::marker::PhantomData,
671                    }
672                }
673            }
674
675            impl<'d> DmaChannelConvert<AnyGdmaTxChannel<'d>> for [<DMA_CH $num>]<'d> {
676                fn degrade(self) -> AnyGdmaTxChannel<'d> {
677                    AnyGdmaTxChannel {
678                        channel: $num,
679                        _lifetime: core::marker::PhantomData,
680                    }
681                }
682            }
683
684            impl DmaChannelExt for [<DMA_CH $num>]<'_> {
685                fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
686                    AnyGdmaRxChannel {
687                        channel: $num,
688                        _lifetime: core::marker::PhantomData,
689                    }
690                }
691
692                fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
693                    AnyGdmaTxChannel {
694                        channel: $num,
695                        _lifetime: core::marker::PhantomData,
696                    }
697                }
698            }
699        }
700    };
701}
702
703cfg_if::cfg_if! {
704    if #[cfg(esp32c2)] {
705        const CHANNEL_COUNT: usize = 1;
706        impl_channel!(0, DMA_CH0);
707    } else if #[cfg(esp32c3)] {
708        const CHANNEL_COUNT: usize = 3;
709        impl_channel!(0, DMA_CH0);
710        impl_channel!(1, DMA_CH1);
711        impl_channel!(2, DMA_CH2);
712    } else if #[cfg(any(esp32c6, esp32h2))] {
713        const CHANNEL_COUNT: usize = 3;
714        impl_channel!(0, DMA_IN_CH0, DMA_OUT_CH0);
715        impl_channel!(1, DMA_IN_CH1, DMA_OUT_CH1);
716        impl_channel!(2, DMA_IN_CH2, DMA_OUT_CH2);
717    } else if #[cfg(esp32s3)] {
718        const CHANNEL_COUNT: usize = 5;
719        impl_channel!(0, DMA_IN_CH0, DMA_OUT_CH0);
720        impl_channel!(1, DMA_IN_CH1, DMA_OUT_CH1);
721        impl_channel!(2, DMA_IN_CH2, DMA_OUT_CH2);
722        impl_channel!(3, DMA_IN_CH3, DMA_OUT_CH3);
723        impl_channel!(4, DMA_IN_CH4, DMA_OUT_CH4);
724    }
725}
726
727crate::dma::impl_dma_eligible! {
728    AnyGdmaChannel {
729        #[cfg(spi2)]
730        SPI2 => Spi2,
731
732        #[cfg(spi3)]
733        SPI3 => Spi3,
734
735        #[cfg(uhci0)]
736        UHCI0 => Uhci0,
737
738        #[cfg(i2s0)]
739        I2S0 => I2s0,
740
741        #[cfg(i2s1)]
742        I2S1 => I2s1,
743
744        #[cfg(esp32s3)]
745        LCD_CAM => LcdCam,
746
747        #[cfg(all(gdma, aes))]
748        AES => Aes,
749
750        #[cfg(all(gdma, sha))]
751        SHA => Sha,
752
753        #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
754        ADC1 => Adc,
755
756        #[cfg(any(esp32c3, esp32s3))]
757        ADC2 => Adc,
758
759        #[cfg(esp32s3)]
760        RMT => Rmt,
761
762        #[cfg(parl_io)]
763        PARL_IO => ParlIo,
764
765        #[cfg(any(esp32c2, esp32c6, esp32h2))]
766        MEM2MEM1 => Mem2Mem1,
767    }
768}
769
770#[cfg(any(esp32c6, esp32h2))]
771crate::dma::impl_dma_eligible! {
772    AnyGdmaChannel {
773        MEM2MEM4 => Mem2Mem4,
774        MEM2MEM5 => Mem2Mem5,
775        MEM2MEM10 => Mem2Mem10,
776        MEM2MEM11 => Mem2Mem11,
777        MEM2MEM12 => Mem2Mem12,
778        MEM2MEM13 => Mem2Mem13,
779        MEM2MEM14 => Mem2Mem14,
780        MEM2MEM15 => Mem2Mem15,
781    }
782}
783
784pub(super) fn init_dma(_cs: CriticalSection<'_>) {
785    DMA::regs()
786        .misc_conf()
787        .modify(|_, w| w.ahbm_rst_inter().set_bit());
788    DMA::regs()
789        .misc_conf()
790        .modify(|_, w| w.ahbm_rst_inter().clear_bit());
791    DMA::regs().misc_conf().modify(|_, w| w.clk_en().set_bit());
792}