Skip to main content

esp_hal/dma/pdma/
spi.rs

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