esp_hal/dma/pdma/
spi.rs

1use enumset::EnumSet;
2use portable_atomic::{AtomicBool, Ordering};
3
4use crate::{
5    asynch::AtomicWaker,
6    dma::{
7        BurstConfig,
8        DmaChannel,
9        DmaPeripheral,
10        DmaRxChannel,
11        DmaRxInterrupt,
12        DmaTxChannel,
13        DmaTxInterrupt,
14        InterruptAccess,
15        PdmaChannel,
16        RegisterAccess,
17        RxRegisterAccess,
18        TxRegisterAccess,
19    },
20    interrupt::InterruptHandler,
21    peripherals::Interrupt,
22    system::Peripheral,
23};
24
25pub(super) type SpiRegisterBlock = crate::pac::spi2::RegisterBlock;
26
27/// The RX half of an arbitrary SPI DMA channel.
28#[derive(Debug)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30pub struct AnySpiDmaRxChannel<'d>(pub(crate) AnySpiDmaChannel<'d>);
31
32impl AnySpiDmaRxChannel<'_> {
33    fn regs(&self) -> &SpiRegisterBlock {
34        self.0.register_block()
35    }
36}
37
38impl crate::private::Sealed for AnySpiDmaRxChannel<'_> {}
39impl DmaRxChannel for AnySpiDmaRxChannel<'_> {}
40
41/// The TX half of an arbitrary SPI DMA channel.
42#[derive(Debug)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44pub struct AnySpiDmaTxChannel<'d>(pub(crate) AnySpiDmaChannel<'d>);
45
46impl AnySpiDmaTxChannel<'_> {
47    fn regs(&self) -> &SpiRegisterBlock {
48        self.0.register_block()
49    }
50}
51
52impl crate::private::Sealed for AnySpiDmaTxChannel<'_> {}
53impl DmaTxChannel for AnySpiDmaTxChannel<'_> {}
54
55impl RegisterAccess for AnySpiDmaTxChannel<'_> {
56    fn peripheral_clock(&self) -> Option<Peripheral> {
57        cfg_if::cfg_if! {
58            if #[cfg(esp32)] {
59                Some(Peripheral::SpiDma)
60            } else {
61                match self.0 {
62                    AnySpiDmaChannel(any::Inner::Spi2(_)) => Some(Peripheral::Spi2Dma),
63                    AnySpiDmaChannel(any::Inner::Spi3(_)) => Some(Peripheral::Spi3Dma),
64                }
65            }
66        }
67    }
68
69    fn reset(&self) {
70        self.regs().dma_conf().modify(|_, w| w.out_rst().set_bit());
71        self.regs()
72            .dma_conf()
73            .modify(|_, w| w.out_rst().clear_bit());
74    }
75
76    fn set_burst_mode(&self, burst_mode: BurstConfig) {
77        self.regs()
78            .dma_conf()
79            .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
80    }
81
82    fn set_descr_burst_mode(&self, burst_mode: bool) {
83        self.regs()
84            .dma_conf()
85            .modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
86    }
87
88    fn set_peripheral(&self, _peripheral: u8) {
89        // no-op
90    }
91
92    fn set_link_addr(&self, address: u32) {
93        self.regs()
94            .dma_out_link()
95            .modify(|_, w| unsafe { w.outlink_addr().bits(address) });
96    }
97
98    fn start(&self) {
99        self.regs()
100            .dma_out_link()
101            .modify(|_, w| w.outlink_start().set_bit());
102    }
103
104    fn stop(&self) {
105        self.regs()
106            .dma_out_link()
107            .modify(|_, w| w.outlink_stop().set_bit());
108    }
109
110    fn restart(&self) {
111        self.regs()
112            .dma_out_link()
113            .modify(|_, w| w.outlink_restart().set_bit());
114    }
115
116    fn set_check_owner(&self, check_owner: Option<bool>) {
117        if check_owner == Some(true) {
118            panic!("SPI DMA does not support checking descriptor ownership");
119        }
120    }
121
122    fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
123        self.0.is_compatible_with(peripheral)
124    }
125
126    #[cfg(psram_dma)]
127    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
128        self.regs()
129            .dma_conf()
130            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
131    }
132
133    #[cfg(psram_dma)]
134    fn can_access_psram(&self) -> bool {
135        matches!(self.0, AnySpiDmaChannel(any::Inner::Spi2(_)))
136    }
137}
138
139impl TxRegisterAccess for AnySpiDmaTxChannel<'_> {
140    fn is_fifo_empty(&self) -> bool {
141        cfg_if::cfg_if! {
142            if #[cfg(esp32)] {
143                self.regs().dma_rstatus().read().dma_out_status().bits() & 0x80000000 != 0
144            } else {
145                self.regs().dma_outstatus().read().dma_outfifo_empty().bit_is_set()
146            }
147        }
148    }
149
150    fn set_auto_write_back(&self, enable: bool) {
151        // there is no `auto_wrback` for SPI
152        assert!(!enable);
153    }
154
155    fn last_dscr_address(&self) -> usize {
156        self.regs()
157            .out_eof_des_addr()
158            .read()
159            .dma_out_eof_des_addr()
160            .bits() as usize
161    }
162
163    fn peripheral_interrupt(&self) -> Option<Interrupt> {
164        None
165    }
166
167    fn async_handler(&self) -> Option<InterruptHandler> {
168        None
169    }
170}
171
172impl InterruptAccess<DmaTxInterrupt> for AnySpiDmaTxChannel<'_> {
173    fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
174        self.regs().dma_int_ena().modify(|_, w| {
175            for interrupt in interrupts {
176                match interrupt {
177                    DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
178                    DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().bit(enable),
179                    DmaTxInterrupt::Eof => w.out_eof().bit(enable),
180                    DmaTxInterrupt::Done => w.out_done().bit(enable),
181                };
182            }
183            w
184        });
185    }
186
187    fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
188        let mut result = EnumSet::new();
189
190        let int_ena = self.regs().dma_int_ena().read();
191        if int_ena.out_total_eof().bit_is_set() {
192            result |= DmaTxInterrupt::TotalEof;
193        }
194        if int_ena.outlink_dscr_error().bit_is_set() {
195            result |= DmaTxInterrupt::DescriptorError;
196        }
197        if int_ena.out_eof().bit_is_set() {
198            result |= DmaTxInterrupt::Eof;
199        }
200        if int_ena.out_done().bit_is_set() {
201            result |= DmaTxInterrupt::Done;
202        }
203
204        result
205    }
206
207    fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
208        self.regs().dma_int_clr().write(|w| {
209            for interrupt in interrupts.into() {
210                match interrupt {
211                    DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
212                    DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(),
213                    DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
214                    DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
215                };
216            }
217            w
218        });
219    }
220
221    fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
222        let mut result = EnumSet::new();
223
224        let int_raw = self.regs().dma_int_raw().read();
225        if int_raw.out_total_eof().bit_is_set() {
226            result |= DmaTxInterrupt::TotalEof;
227        }
228        if int_raw.outlink_dscr_error().bit_is_set() {
229            result |= DmaTxInterrupt::DescriptorError;
230        }
231        if int_raw.out_eof().bit_is_set() {
232            result |= DmaTxInterrupt::Eof;
233        }
234        if int_raw.out_done().bit_is_set() {
235            result |= DmaTxInterrupt::Done;
236        }
237
238        result
239    }
240
241    fn waker(&self) -> &'static AtomicWaker {
242        self.0.tx_waker()
243    }
244
245    fn is_async(&self) -> bool {
246        self.0.tx_async_flag().load(Ordering::Acquire)
247    }
248
249    fn set_async(&self, is_async: bool) {
250        self.0.tx_async_flag().store(is_async, Ordering::Release);
251    }
252}
253
254impl RegisterAccess for AnySpiDmaRxChannel<'_> {
255    fn peripheral_clock(&self) -> Option<Peripheral> {
256        cfg_if::cfg_if! {
257            if #[cfg(esp32)] {
258                Some(Peripheral::SpiDma)
259            } else {
260                match self.0 {
261                    AnySpiDmaChannel(any::Inner::Spi2(_)) => Some(Peripheral::Spi2Dma),
262                    AnySpiDmaChannel(any::Inner::Spi3(_)) => Some(Peripheral::Spi3Dma),
263                }
264            }
265        }
266    }
267
268    fn reset(&self) {
269        self.regs().dma_conf().modify(|_, w| w.in_rst().set_bit());
270        self.regs().dma_conf().modify(|_, w| w.in_rst().clear_bit());
271    }
272
273    fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
274
275    fn set_descr_burst_mode(&self, burst_mode: bool) {
276        self.regs()
277            .dma_conf()
278            .modify(|_, w| w.indscr_burst_en().bit(burst_mode));
279    }
280
281    fn set_peripheral(&self, _peripheral: u8) {
282        // no-op
283    }
284
285    fn set_link_addr(&self, address: u32) {
286        self.regs()
287            .dma_in_link()
288            .modify(|_, w| unsafe { w.inlink_addr().bits(address) });
289    }
290
291    fn start(&self) {
292        self.regs()
293            .dma_in_link()
294            .modify(|_, w| w.inlink_start().set_bit());
295    }
296
297    fn stop(&self) {
298        self.regs()
299            .dma_in_link()
300            .modify(|_, w| w.inlink_stop().set_bit());
301    }
302
303    fn restart(&self) {
304        self.regs()
305            .dma_in_link()
306            .modify(|_, w| w.inlink_restart().set_bit());
307    }
308
309    fn set_check_owner(&self, check_owner: Option<bool>) {
310        if check_owner == Some(true) {
311            panic!("SPI DMA does not support checking descriptor ownership");
312        }
313    }
314
315    fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
316        self.0.is_compatible_with(peripheral)
317    }
318
319    #[cfg(psram_dma)]
320    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
321        self.regs()
322            .dma_conf()
323            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
324    }
325
326    #[cfg(psram_dma)]
327    fn can_access_psram(&self) -> bool {
328        matches!(self.0, AnySpiDmaChannel(any::Inner::Spi2(_)))
329    }
330}
331
332impl RxRegisterAccess for AnySpiDmaRxChannel<'_> {
333    fn peripheral_interrupt(&self) -> Option<Interrupt> {
334        Some(self.0.peripheral_interrupt())
335    }
336
337    fn async_handler(&self) -> Option<InterruptHandler> {
338        Some(self.0.async_handler())
339    }
340}
341
342impl InterruptAccess<DmaRxInterrupt> for AnySpiDmaRxChannel<'_> {
343    fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
344        self.regs().dma_int_ena().modify(|_, w| {
345            for interrupt in interrupts {
346                match interrupt {
347                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
348                    DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
349                    DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().bit(enable),
350                    DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().bit(enable),
351                    DmaRxInterrupt::Done => w.in_done().bit(enable),
352                };
353            }
354            w
355        });
356    }
357
358    fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
359        let mut result = EnumSet::new();
360
361        let int_ena = self.regs().dma_int_ena().read();
362        if int_ena.inlink_dscr_error().bit_is_set() {
363            result |= DmaRxInterrupt::DescriptorError;
364        }
365        if int_ena.inlink_dscr_empty().bit_is_set() {
366            result |= DmaRxInterrupt::DescriptorEmpty;
367        }
368        if int_ena.in_suc_eof().bit_is_set() {
369            result |= DmaRxInterrupt::SuccessfulEof;
370        }
371        if int_ena.in_err_eof().bit_is_set() {
372            result |= DmaRxInterrupt::ErrorEof;
373        }
374        if int_ena.in_done().bit_is_set() {
375            result |= DmaRxInterrupt::Done;
376        }
377
378        result
379    }
380
381    fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
382        self.regs().dma_int_clr().modify(|_, w| {
383            for interrupt in interrupts.into() {
384                match interrupt {
385                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
386                    DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
387                    DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(),
388                    DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(),
389                    DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
390                };
391            }
392            w
393        });
394    }
395
396    fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
397        let mut result = EnumSet::new();
398
399        let int_raw = self.regs().dma_int_raw().read();
400        if int_raw.inlink_dscr_error().bit_is_set() {
401            result |= DmaRxInterrupt::DescriptorError;
402        }
403        if int_raw.inlink_dscr_empty().bit_is_set() {
404            result |= DmaRxInterrupt::DescriptorEmpty;
405        }
406        if int_raw.in_suc_eof().bit_is_set() {
407            result |= DmaRxInterrupt::SuccessfulEof;
408        }
409        if int_raw.in_err_eof().bit_is_set() {
410            result |= DmaRxInterrupt::ErrorEof;
411        }
412        if int_raw.in_done().bit_is_set() {
413            result |= DmaRxInterrupt::Done;
414        }
415
416        result
417    }
418
419    fn waker(&self) -> &'static AtomicWaker {
420        self.0.rx_waker()
421    }
422
423    fn is_async(&self) -> bool {
424        self.0.rx_async_flag().load(Ordering::Relaxed)
425    }
426
427    fn set_async(&self, _is_async: bool) {
428        self.0.rx_async_flag().store(_is_async, Ordering::Relaxed);
429    }
430}
431
432crate::any_peripheral! {
433    /// An SPI-compatible type-erased DMA channel.
434    pub peripheral AnySpiDmaChannel<'d> {
435        Spi2(crate::peripherals::DMA_SPI2<'d>),
436        Spi3(crate::peripherals::DMA_SPI3<'d>),
437    }
438}
439
440impl<'d> DmaChannel for AnySpiDmaChannel<'d> {
441    type Rx = AnySpiDmaRxChannel<'d>;
442    type Tx = AnySpiDmaTxChannel<'d>;
443
444    unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
445        (
446            AnySpiDmaRxChannel(unsafe { self.clone_unchecked() }),
447            AnySpiDmaTxChannel(unsafe { self.clone_unchecked() }),
448        )
449    }
450}
451
452impl PdmaChannel for AnySpiDmaChannel<'_> {
453    type RegisterBlock = SpiRegisterBlock;
454
455    delegate::delegate! {
456        to match &self.0 {
457            any::Inner::Spi2(channel) => channel,
458            any::Inner::Spi3(channel) => channel,
459        } {
460            fn register_block(&self) -> &SpiRegisterBlock;
461            fn tx_waker(&self) -> &'static AtomicWaker;
462            fn rx_waker(&self) -> &'static AtomicWaker;
463            fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool;
464            fn peripheral_interrupt(&self) -> Interrupt;
465            fn async_handler(&self) -> InterruptHandler;
466            fn rx_async_flag(&self) -> &'static AtomicBool;
467            fn tx_async_flag(&self) -> &'static AtomicBool;
468        }
469    }
470}