Skip to main content

esp_hal/spi/master/
dma.rs

1use core::{
2    cell::{Cell, UnsafeCell},
3    cmp::min,
4    mem::{ManuallyDrop, MaybeUninit},
5    sync::atomic::{Ordering, fence},
6};
7
8#[cfg(feature = "unstable")]
9use embedded_hal::spi::{ErrorType, SpiBus};
10#[cfg(place_spi_master_driver_in_ram)]
11use procmacros::ram;
12
13use super::*;
14use crate::{
15    dma::{
16        Channel,
17        DmaChannelFor,
18        DmaDescriptor,
19        DmaEligible,
20        DmaRxBuf,
21        DmaRxBuffer,
22        DmaTxBuf,
23        DmaTxBuffer,
24        PeripheralDmaChannel,
25        asynch::DmaRxFuture,
26    },
27    private::DropGuard,
28    spi::DmaError,
29};
30
31const MAX_DMA_SIZE: usize = 32736;
32
33impl<'d> Spi<'d, Blocking> {
34    #[doc_replace(
35        "dma_channel" => {
36            cfg(any(esp32, esp32s2)) => "DMA_SPI2",
37            _ => "DMA_CH0",
38        }
39    )]
40    /// Configures the SPI instance to use DMA with the specified channel.
41    ///
42    /// This method prepares the SPI instance for DMA transfers using SPI
43    /// and returns an instance of `SpiDma` that supports DMA
44    /// operations.
45    /// ```rust, no_run
46    /// # {before_snippet}
47    /// use esp_hal::{
48    ///     dma::{DmaRxBuf, DmaTxBuf},
49    ///     dma_buffers,
50    ///     spi::{
51    ///         Mode,
52    ///         master::{Config, Spi},
53    ///     },
54    /// };
55    /// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000);
56    ///
57    /// let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer)?;
58    /// let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer)?;
59    ///
60    /// let mut spi = Spi::new(
61    ///     peripherals.SPI2,
62    ///     Config::default()
63    ///         .with_frequency(Rate::from_khz(100))
64    ///         .with_mode(Mode::_0),
65    /// )?
66    /// .with_dma(peripherals.__dma_channel__)
67    /// .with_buffers(dma_rx_buf, dma_tx_buf);
68    /// # {after_snippet}
69    /// ```
70    #[instability::unstable]
71    pub fn with_dma(self, channel: impl DmaChannelFor<AnySpi<'d>>) -> SpiDma<'d, Blocking> {
72        SpiDma::new(self, channel.degrade())
73    }
74}
75
76#[doc_replace(
77    "dma_channel" => {
78        cfg(any(esp32, esp32s2)) => "DMA_SPI2",
79        _ => "DMA_CH0",
80    }
81)]
82/// A DMA capable SPI instance.
83///
84/// Using `SpiDma` is not recommended unless you wish
85/// to manage buffers yourself. It's recommended to use
86/// [`SpiDmaBus`] via `with_buffers` to get access
87/// to a DMA capable SPI bus that implements the
88/// embedded-hal traits.
89/// ```rust, no_run
90/// # {before_snippet}
91/// use esp_hal::{
92///     dma::{DmaRxBuf, DmaTxBuf},
93///     dma_buffers,
94///     spi::{
95///         Mode,
96///         master::{Config, Spi},
97///     },
98/// };
99/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000);
100///
101/// let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer)?;
102/// let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer)?;
103///
104/// let mut spi = Spi::new(
105///     peripherals.SPI2,
106///     Config::default()
107///         .with_frequency(Rate::from_khz(100))
108///         .with_mode(Mode::_0),
109/// )?
110/// .with_dma(peripherals.__dma_channel__)
111/// .with_buffers(dma_rx_buf, dma_tx_buf);
112/// #
113/// # {after_snippet}
114/// ```
115#[cfg_attr(feature = "defmt", derive(defmt::Format))]
116pub struct SpiDma<'d, Dm>
117where
118    Dm: DriverMode,
119{
120    spi: SpiWrapper<'d>,
121    pub(crate) channel: Channel<Dm, PeripheralDmaChannel<AnySpi<'d>>>,
122}
123
124impl<Dm> crate::private::Sealed for SpiDma<'_, Dm> where Dm: DriverMode {}
125
126impl<'d> SpiDma<'d, Blocking> {
127    /// Converts the SPI instance into async mode.
128    #[instability::unstable]
129    pub fn into_async(self) -> SpiDma<'d, Async> {
130        self.spi
131            .set_interrupt_handler(self.spi.info().async_handler);
132        SpiDma {
133            spi: self.spi,
134            channel: self.channel.into_async(),
135        }
136    }
137
138    pub(super) fn new(
139        spi_driver: Spi<'d, Blocking>,
140        channel: PeripheralDmaChannel<AnySpi<'d>>,
141    ) -> Self {
142        let spi = spi_driver.spi;
143
144        let channel = Channel::new(channel);
145        channel.runtime_ensure_compatible(&spi.spi);
146
147        for_each_spi_master!((all $($inst:tt),*) => {
148            const SPI_NUM: usize = 0 $(+ { stringify!($inst); 1 })*;
149        };);
150        let id = if spi.info() == unsafe { crate::peripherals::SPI2::steal().info() } {
151            0
152        } else {
153            1
154        };
155
156        let state = spi.spi.dma_state();
157
158        state.tx_transfer_in_progress.set(false);
159        state.rx_transfer_in_progress.set(false);
160
161        static mut TX_DESCRIPTORS: [[DmaDescriptor; 1]; SPI_NUM] =
162            [[DmaDescriptor::EMPTY]; SPI_NUM];
163        static mut RX_DESCRIPTORS: [[DmaDescriptor; 1]; SPI_NUM] =
164            [[DmaDescriptor::EMPTY]; SPI_NUM];
165
166        let empty_rx_buffer = unwrap!(DmaRxBuf::new(unsafe { &mut RX_DESCRIPTORS[id] }, &mut []));
167
168        cfg_if::cfg_if! {
169            if #[cfg(all(esp32, spi_address_workaround))] {
170                static mut BUFFERS: [[u32; 1]; SPI_NUM] = [[0]; SPI_NUM];
171                let buffer = crate::dma::as_mut_byte_array!(BUFFERS[id], 4);
172                let empty_tx_buffer = unwrap!(DmaTxBuf::new(unsafe { &mut TX_DESCRIPTORS[id] }, buffer));
173            } else {
174                let empty_tx_buffer = unwrap!(DmaTxBuf::new(unsafe { &mut TX_DESCRIPTORS[id] }, &mut []));
175            }
176        }
177
178        // The buffers must be set up when creating the driver.
179        unsafe { (&mut *state.empty_tx_buffer.get()).write(empty_tx_buffer) };
180        unsafe { (&mut *state.empty_rx_buffer.get()).write(empty_rx_buffer) };
181
182        Self { spi, channel }
183    }
184
185    /// Listen for the given interrupts
186    #[instability::unstable]
187    pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
188        self.driver().enable_listen(interrupts.into(), true);
189    }
190
191    /// Unlisten the given interrupts
192    #[instability::unstable]
193    pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
194        self.driver().enable_listen(interrupts.into(), false);
195    }
196
197    /// Gets asserted interrupts
198    #[instability::unstable]
199    pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
200        self.driver().interrupts()
201    }
202
203    /// Resets asserted interrupts
204    #[instability::unstable]
205    pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
206        self.driver().clear_interrupts(interrupts.into());
207    }
208
209    #[cfg_attr(
210        not(multi_core),
211        doc = "Registers an interrupt handler for the peripheral."
212    )]
213    #[cfg_attr(
214        multi_core,
215        doc = "Registers an interrupt handler for the peripheral on the current core."
216    )]
217    #[doc = ""]
218    /// Note that this will replace any previously registered interrupt
219    /// handlers.
220    ///
221    /// You can restore the default/unhandled interrupt handler by using
222    /// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER]
223    ///
224    /// # Panics
225    ///
226    /// Panics if passed interrupt handler is invalid (e.g. has priority
227    /// `None`)
228    #[instability::unstable]
229    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
230        self.spi.set_interrupt_handler(handler);
231    }
232}
233
234impl<'d> SpiDma<'d, Async> {
235    /// Converts the SPI instance into blocking mode.
236    #[instability::unstable]
237    pub fn into_blocking(self) -> SpiDma<'d, Blocking> {
238        self.spi.disable_peri_interrupt_on_all_cores();
239        SpiDma {
240            spi: self.spi,
241            channel: self.channel.into_blocking(),
242        }
243    }
244
245    async fn wait_for_idle_async(&mut self) {
246        if self.dma_driver().state.rx_transfer_in_progress.get() {
247            _ = DmaRxFuture::new(&mut self.channel.rx).await;
248            self.dma_driver().state.rx_transfer_in_progress.set(false);
249        }
250
251        struct Fut(Driver);
252        impl Fut {
253            const DONE_EVENTS: EnumSet<SpiInterrupt> =
254                enumset::enum_set!(SpiInterrupt::TransferDone);
255        }
256        impl Future for Fut {
257            type Output = ();
258
259            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
260                if !self.0.interrupts().is_disjoint(Self::DONE_EVENTS) {
261                    #[cfg(any(esp32, esp32s2))]
262                    // Need to poll for done-ness even after interrupt fires.
263                    if self.0.busy() {
264                        cx.waker().wake_by_ref();
265                        return Poll::Pending;
266                    }
267
268                    self.0.clear_interrupts(Self::DONE_EVENTS);
269                    return Poll::Ready(());
270                }
271
272                self.0.state.waker.register(cx.waker());
273                self.0.enable_listen(Self::DONE_EVENTS, true);
274                Poll::Pending
275            }
276        }
277        impl Drop for Fut {
278            fn drop(&mut self) {
279                self.0.enable_listen(Self::DONE_EVENTS, false);
280            }
281        }
282
283        if !self.is_done() {
284            Fut(self.driver()).await;
285        }
286
287        if self.dma_driver().state.tx_transfer_in_progress.get() {
288            // In case DMA TX buffer is bigger than what the SPI consumes, stop the DMA.
289            if !self.channel.tx.is_done() {
290                self.channel.tx.stop_transfer();
291            }
292            self.dma_driver().state.tx_transfer_in_progress.set(false);
293        }
294    }
295}
296
297impl<Dm> core::fmt::Debug for SpiDma<'_, Dm>
298where
299    Dm: DriverMode + core::fmt::Debug,
300{
301    /// Formats the `SpiDma` instance for debugging purposes.
302    ///
303    /// This method returns a debug struct with the name "SpiDma" without
304    /// exposing internal details.
305    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
306        f.debug_struct("SpiDma").field("spi", &self.spi).finish()
307    }
308}
309
310#[instability::unstable]
311impl crate::interrupt::InterruptConfigurable for SpiDma<'_, Blocking> {
312    /// Sets the interrupt handler
313    ///
314    /// Interrupts are not enabled at the peripheral level here.
315    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
316        self.set_interrupt_handler(handler);
317    }
318}
319
320impl<Dm> SpiDma<'_, Dm>
321where
322    Dm: DriverMode,
323{
324    fn spi(&self) -> &SpiWrapper<'_> {
325        &self.spi
326    }
327
328    fn driver(&self) -> Driver {
329        Driver {
330            info: self.spi.info(),
331            state: self.spi.state(),
332        }
333    }
334
335    fn dma_driver(&self) -> DmaDriver {
336        DmaDriver {
337            driver: self.driver(),
338            dma_peripheral: self.spi().dma_peripheral(),
339            state: self.spi().dma_state(),
340        }
341    }
342
343    fn is_done(&self) -> bool {
344        if self.driver().busy() {
345            return false;
346        }
347        if self.dma_driver().state.rx_transfer_in_progress.get() {
348            // If this is an asymmetric transfer and the RX side is smaller, the RX channel
349            // will never be "done" as it won't have enough descriptors/buffer to receive
350            // the EOF bit from the SPI. So instead the RX channel will hit
351            // a "descriptor empty" which means the DMA is written as much
352            // of the received data as possible into the buffer and
353            // discarded the rest. The user doesn't care about this discarded data.
354
355            if !self.channel.rx.is_done() && !self.channel.rx.has_dscr_empty_error() {
356                return false;
357            }
358        }
359        true
360    }
361
362    fn wait_for_idle(&mut self) {
363        while !self.is_done() {
364            // Wait for the SPI to become idle
365        }
366        self.dma_driver().state.rx_transfer_in_progress.set(false);
367        self.dma_driver().state.tx_transfer_in_progress.set(false);
368        fence(Ordering::Acquire);
369    }
370
371    /// # Safety:
372    ///
373    /// The caller must ensure to not access the buffer contents while the
374    /// transfer is in progress. Moving the buffer itself is allowed.
375    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
376    unsafe fn start_transfer_dma<RX: DmaRxBuffer, TX: DmaTxBuffer>(
377        &mut self,
378        full_duplex: bool,
379        bytes_to_read: usize,
380        bytes_to_write: usize,
381        rx_buffer: &mut RX,
382        tx_buffer: &mut TX,
383    ) -> Result<(), Error> {
384        if bytes_to_read > MAX_DMA_SIZE || bytes_to_write > MAX_DMA_SIZE {
385            return Err(Error::MaxDmaTransferSizeExceeded);
386        }
387
388        self.dma_driver()
389            .state
390            .rx_transfer_in_progress
391            .set(bytes_to_read > 0);
392        self.dma_driver()
393            .state
394            .tx_transfer_in_progress
395            .set(bytes_to_write > 0);
396        unsafe {
397            self.dma_driver().start_transfer_dma(
398                full_duplex,
399                bytes_to_read,
400                bytes_to_write,
401                rx_buffer,
402                tx_buffer,
403                &mut self.channel,
404            )
405        }
406    }
407
408    /// # Safety:
409    ///
410    /// The caller must ensure that the buffers are not accessed while the
411    /// transfer is in progress. Moving the buffers is allowed.
412    #[cfg(all(esp32, spi_address_workaround))]
413    unsafe fn set_up_address_workaround(
414        &mut self,
415        cmd: Command,
416        address: Address,
417        dummy: u8,
418    ) -> Result<(), Error> {
419        if dummy > 0 {
420            // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
421            error!("Dummy bits are not supported when there is no data to write");
422            return Err(Error::Unsupported);
423        }
424
425        let buffer = unsafe { self.spi.dma_state().empty_tx_buffer() };
426
427        let bytes_to_write = address.width().div_ceil(8);
428        // The address register is read in big-endian order,
429        // we have to prepare the emulated write in the same way.
430        let addr_bytes = address.value().to_be_bytes();
431        let addr_bytes = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
432        buffer.fill(addr_bytes);
433
434        self.driver().setup_half_duplex(
435            true,
436            cmd,
437            Address::None,
438            false,
439            dummy,
440            bytes_to_write == 0,
441            address.mode(),
442        )?;
443
444        let empty_rx_buffer = unsafe { self.dma_driver().empty_rx_buffer() };
445
446        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, empty_rx_buffer, buffer) }
447    }
448
449    fn cancel_transfer(&mut self) {
450        let state = self.dma_driver().state;
451        if state.tx_transfer_in_progress.get() || state.rx_transfer_in_progress.get() {
452            self.dma_driver().abort_transfer();
453
454            // We need to stop the DMA transfer, too.
455            if state.tx_transfer_in_progress.get() {
456                self.channel.tx.stop_transfer();
457                state.tx_transfer_in_progress.set(false);
458            }
459            if state.rx_transfer_in_progress.get() {
460                self.channel.rx.stop_transfer();
461                state.rx_transfer_in_progress.set(false);
462            }
463        }
464    }
465}
466
467#[instability::unstable]
468impl<Dm> embassy_embedded_hal::SetConfig for SpiDma<'_, Dm>
469where
470    Dm: DriverMode,
471{
472    type Config = Config;
473    type ConfigError = ConfigError;
474
475    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
476        self.apply_config(config)
477    }
478}
479
480/// A structure representing a DMA transfer for SPI.
481///
482/// This structure holds references to the SPI instance, DMA buffers, and
483/// transfer status.
484#[instability::unstable]
485pub struct SpiDmaTransfer<'d, Dm, Buf>
486where
487    Dm: DriverMode,
488{
489    spi_dma: ManuallyDrop<SpiDma<'d, Dm>>,
490    dma_buf: ManuallyDrop<Buf>,
491}
492
493impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
494    /// Waits for the DMA transfer to complete asynchronously.
495    ///
496    /// This method awaits the completion of both RX and TX operations.
497    #[instability::unstable]
498    pub async fn wait_for_done(&mut self) {
499        self.spi_dma.wait_for_idle_async().await;
500    }
501}
502
503impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
504where
505    Dm: DriverMode,
506{
507    fn new(spi_dma: SpiDma<'d, Dm>, dma_buf: Buf) -> Self {
508        Self {
509            spi_dma: ManuallyDrop::new(spi_dma),
510            dma_buf: ManuallyDrop::new(dma_buf),
511        }
512    }
513
514    /// Checks if the transfer is complete.
515    ///
516    /// This method returns `true` if both RX and TX operations are done,
517    /// and the SPI instance is no longer busy.
518    pub fn is_done(&self) -> bool {
519        self.spi_dma.is_done()
520    }
521
522    /// Waits for the DMA transfer to complete.
523    ///
524    /// This method blocks until the transfer is finished and returns the
525    /// `SpiDma` instance and the associated buffer.
526    #[instability::unstable]
527    pub fn wait(mut self) -> (SpiDma<'d, Dm>, Buf) {
528        self.spi_dma.wait_for_idle();
529        let retval = unsafe {
530            (
531                ManuallyDrop::take(&mut self.spi_dma),
532                ManuallyDrop::take(&mut self.dma_buf),
533            )
534        };
535        core::mem::forget(self);
536        retval
537    }
538
539    /// Cancels the DMA transfer.
540    #[instability::unstable]
541    pub fn cancel(&mut self) {
542        if !self.spi_dma.is_done() {
543            self.spi_dma.cancel_transfer();
544        }
545    }
546}
547
548impl<Dm, Buf> Drop for SpiDmaTransfer<'_, Dm, Buf>
549where
550    Dm: DriverMode,
551{
552    fn drop(&mut self) {
553        if !self.is_done() {
554            self.spi_dma.cancel_transfer();
555            self.spi_dma.wait_for_idle();
556        }
557
558        unsafe {
559            ManuallyDrop::drop(&mut self.spi_dma);
560            ManuallyDrop::drop(&mut self.dma_buf);
561        }
562    }
563}
564
565impl<'d, Dm> SpiDma<'d, Dm>
566where
567    Dm: DriverMode,
568{
569    /// # Safety:
570    ///
571    /// The caller must ensure that the buffers are not accessed while the
572    /// transfer is in progress. Moving the buffers is allowed.
573    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
574    unsafe fn start_dma_write(
575        &mut self,
576        bytes_to_write: usize,
577        buffer: &mut impl DmaTxBuffer,
578    ) -> Result<(), Error> {
579        let empty_rx_buffer = unsafe { self.dma_driver().empty_rx_buffer() };
580
581        unsafe { self.start_dma_transfer(0, bytes_to_write, empty_rx_buffer, buffer) }
582    }
583
584    /// Configures the DMA buffers for the SPI instance.
585    ///
586    /// This method sets up both RX and TX buffers for DMA transfers.
587    /// It returns an instance of `SpiDmaBus` that can be used for SPI
588    /// communication.
589    #[instability::unstable]
590    pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDmaBus<'d, Dm> {
591        SpiDmaBus::new(self, dma_rx_buf, dma_tx_buf)
592    }
593
594    /// Perform a DMA write.
595    ///
596    /// This will return a [SpiDmaTransfer] owning the buffer and the
597    /// SPI instance. The maximum amount of data to be sent is 32736
598    /// bytes.
599    #[allow(clippy::type_complexity)]
600    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
601    #[instability::unstable]
602    pub fn write<TX: DmaTxBuffer>(
603        mut self,
604        bytes_to_write: usize,
605        mut buffer: TX,
606    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
607        self.wait_for_idle();
608        if let Err(e) = self.driver().setup_full_duplex() {
609            return Err((e, self, buffer));
610        };
611        match unsafe { self.start_dma_write(bytes_to_write, &mut buffer) } {
612            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer)),
613            Err(e) => Err((e, self, buffer)),
614        }
615    }
616
617    /// # Safety:
618    ///
619    /// The caller must ensure that the buffers are not accessed while the
620    /// transfer is in progress. Moving the buffers is allowed.
621    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
622    unsafe fn start_dma_read(
623        &mut self,
624        bytes_to_read: usize,
625        buffer: &mut impl DmaRxBuffer,
626    ) -> Result<(), Error> {
627        let empty_tx_buffer = unsafe { self.dma_driver().empty_tx_buffer() };
628
629        unsafe { self.start_dma_transfer(bytes_to_read, 0, buffer, empty_tx_buffer) }
630    }
631
632    /// Perform a DMA read.
633    ///
634    /// This will return a [SpiDmaTransfer] owning the buffer and
635    /// the SPI instance. The maximum amount of data to be
636    /// received is 32736 bytes.
637    #[allow(clippy::type_complexity)]
638    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
639    #[instability::unstable]
640    pub fn read<RX: DmaRxBuffer>(
641        mut self,
642        bytes_to_read: usize,
643        mut buffer: RX,
644    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
645        self.wait_for_idle();
646        if let Err(e) = self.driver().setup_full_duplex() {
647            return Err((e, self, buffer));
648        };
649        match unsafe { self.start_dma_read(bytes_to_read, &mut buffer) } {
650            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer)),
651            Err(e) => Err((e, self, buffer)),
652        }
653    }
654
655    /// # Safety:
656    ///
657    /// The caller must ensure that the buffers are not accessed while the
658    /// transfer is in progress. Moving the buffers is allowed.
659    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
660    unsafe fn start_dma_transfer(
661        &mut self,
662        bytes_to_read: usize,
663        bytes_to_write: usize,
664        rx_buffer: &mut impl DmaRxBuffer,
665        tx_buffer: &mut impl DmaTxBuffer,
666    ) -> Result<(), Error> {
667        unsafe {
668            self.start_transfer_dma(true, bytes_to_read, bytes_to_write, rx_buffer, tx_buffer)
669        }
670    }
671
672    /// Perform a DMA transfer
673    ///
674    /// This will return a [SpiDmaTransfer] owning the buffers and
675    /// the SPI instance. The maximum amount of data to be
676    /// sent/received is 32736 bytes.
677    #[allow(clippy::type_complexity)]
678    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
679    #[instability::unstable]
680    pub fn transfer<RX: DmaRxBuffer, TX: DmaTxBuffer>(
681        mut self,
682        bytes_to_read: usize,
683        mut rx_buffer: RX,
684        bytes_to_write: usize,
685        mut tx_buffer: TX,
686    ) -> Result<SpiDmaTransfer<'d, Dm, (RX, TX)>, (Error, Self, RX, TX)> {
687        self.wait_for_idle();
688        if let Err(e) = self.driver().setup_full_duplex() {
689            return Err((e, self, rx_buffer, tx_buffer));
690        };
691        match unsafe {
692            self.start_dma_transfer(
693                bytes_to_read,
694                bytes_to_write,
695                &mut rx_buffer,
696                &mut tx_buffer,
697            )
698        } {
699            Ok(_) => Ok(SpiDmaTransfer::new(self, (rx_buffer, tx_buffer))),
700            Err(e) => Err((e, self, rx_buffer, tx_buffer)),
701        }
702    }
703
704    /// # Safety:
705    ///
706    /// The caller must ensure that the buffers are not accessed while the
707    /// transfer is in progress. Moving the buffers is allowed.
708    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
709    unsafe fn start_half_duplex_read(
710        &mut self,
711        data_mode: DataMode,
712        cmd: Command,
713        address: Address,
714        dummy: u8,
715        bytes_to_read: usize,
716        buffer: &mut impl DmaRxBuffer,
717    ) -> Result<(), Error> {
718        self.driver().setup_half_duplex(
719            false,
720            cmd,
721            address,
722            false,
723            dummy,
724            bytes_to_read == 0,
725            data_mode,
726        )?;
727
728        let empty_tx_buffer = unsafe { self.dma_driver().empty_tx_buffer() };
729
730        unsafe { self.start_transfer_dma(false, bytes_to_read, 0, buffer, empty_tx_buffer) }
731    }
732
733    /// Perform a half-duplex read operation using DMA.
734    #[allow(clippy::type_complexity)]
735    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
736    #[instability::unstable]
737    pub fn half_duplex_read<RX: DmaRxBuffer>(
738        mut self,
739        data_mode: DataMode,
740        cmd: Command,
741        address: Address,
742        dummy: u8,
743        bytes_to_read: usize,
744        mut buffer: RX,
745    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
746        self.wait_for_idle();
747
748        match unsafe {
749            self.start_half_duplex_read(data_mode, cmd, address, dummy, bytes_to_read, &mut buffer)
750        } {
751            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer)),
752            Err(e) => Err((e, self, buffer)),
753        }
754    }
755
756    /// # Safety:
757    ///
758    /// The caller must ensure that the buffers are not accessed while the
759    /// transfer is in progress. Moving the buffers is allowed.
760    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
761    unsafe fn start_half_duplex_write(
762        &mut self,
763        data_mode: DataMode,
764        cmd: Command,
765        address: Address,
766        dummy: u8,
767        bytes_to_write: usize,
768        buffer: &mut impl DmaTxBuffer,
769    ) -> Result<(), Error> {
770        #[cfg(all(esp32, spi_address_workaround))]
771        {
772            // On the ESP32, if we don't have data, the address is always sent
773            // on a single line, regardless of its data mode.
774            if bytes_to_write == 0 && address.mode() != DataMode::SingleTwoDataLines {
775                return unsafe { self.set_up_address_workaround(cmd, address, dummy) };
776            }
777        }
778
779        self.driver().setup_half_duplex(
780            true,
781            cmd,
782            address,
783            false,
784            dummy,
785            bytes_to_write == 0,
786            data_mode,
787        )?;
788
789        let empty_rx_buffer = unsafe { self.dma_driver().empty_rx_buffer() };
790
791        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, empty_rx_buffer, buffer) }
792    }
793
794    /// Perform a half-duplex write operation using DMA.
795    #[allow(clippy::type_complexity)]
796    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
797    #[instability::unstable]
798    pub fn half_duplex_write<TX: DmaTxBuffer>(
799        mut self,
800        data_mode: DataMode,
801        cmd: Command,
802        address: Address,
803        dummy: u8,
804        bytes_to_write: usize,
805        mut buffer: TX,
806    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
807        self.wait_for_idle();
808
809        match unsafe {
810            self.start_half_duplex_write(
811                data_mode,
812                cmd,
813                address,
814                dummy,
815                bytes_to_write,
816                &mut buffer,
817            )
818        } {
819            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer)),
820            Err(e) => Err((e, self, buffer)),
821        }
822    }
823
824    /// Change the bus configuration.
825    ///
826    /// # Errors
827    ///
828    /// If frequency passed in config exceeds
829    #[cfg_attr(not(esp32h2), doc = " 80MHz")]
830    #[cfg_attr(esp32h2, doc = " 48MHz")]
831    /// or is below 70kHz,
832    /// [`ConfigError::UnsupportedFrequency`] error will be returned.
833    #[instability::unstable]
834    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
835        self.driver().apply_config(config)
836    }
837}
838
839/// A DMA-capable SPI bus.
840///
841/// This structure is responsible for managing SPI transfers using DMA
842/// buffers.
843#[derive(Debug)]
844#[cfg_attr(feature = "defmt", derive(defmt::Format))]
845#[instability::unstable]
846pub struct SpiDmaBus<'d, Dm>
847where
848    Dm: DriverMode,
849{
850    spi_dma: SpiDma<'d, Dm>,
851    rx_buf: DmaRxBuf,
852    tx_buf: DmaTxBuf,
853}
854
855impl<Dm> crate::private::Sealed for SpiDmaBus<'_, Dm> where Dm: DriverMode {}
856
857impl<'d> SpiDmaBus<'d, Blocking> {
858    /// Converts the SPI instance into async mode.
859    #[instability::unstable]
860    pub fn into_async(self) -> SpiDmaBus<'d, Async> {
861        SpiDmaBus {
862            spi_dma: self.spi_dma.into_async(),
863            rx_buf: self.rx_buf,
864            tx_buf: self.tx_buf,
865        }
866    }
867
868    /// Listen for the given interrupts
869    #[instability::unstable]
870    pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
871        self.spi_dma.listen(interrupts.into());
872    }
873
874    /// Unlisten the given interrupts
875    #[instability::unstable]
876    pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
877        self.spi_dma.unlisten(interrupts.into());
878    }
879
880    /// Gets asserted interrupts
881    #[instability::unstable]
882    pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
883        self.spi_dma.interrupts()
884    }
885
886    /// Resets asserted interrupts
887    #[instability::unstable]
888    pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
889        self.spi_dma.clear_interrupts(interrupts.into());
890    }
891}
892
893impl<'d> SpiDmaBus<'d, Async> {
894    /// Converts the SPI instance into async mode.
895    #[instability::unstable]
896    pub fn into_blocking(self) -> SpiDmaBus<'d, Blocking> {
897        SpiDmaBus {
898            spi_dma: self.spi_dma.into_blocking(),
899            rx_buf: self.rx_buf,
900            tx_buf: self.tx_buf,
901        }
902    }
903
904    /// Fill the given buffer with data from the bus.
905    #[instability::unstable]
906    pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
907        self.spi_dma.wait_for_idle_async().await;
908        self.spi_dma.driver().setup_full_duplex()?;
909        let chunk_size = self.rx_buf.capacity();
910
911        let empty_tx_buffer = unsafe { self.spi_dma.dma_driver().empty_tx_buffer() };
912
913        for chunk in words.chunks_mut(chunk_size) {
914            let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
915
916            unsafe { spi.start_dma_transfer(chunk.len(), 0, &mut self.rx_buf, empty_tx_buffer)? };
917
918            spi.wait_for_idle_async().await;
919
920            chunk.copy_from_slice(&self.rx_buf.as_slice()[..chunk.len()]);
921
922            spi.defuse();
923        }
924
925        Ok(())
926    }
927
928    /// Transmit the given buffer to the bus.
929    #[instability::unstable]
930    pub async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
931        self.spi_dma.wait_for_idle_async().await;
932        self.spi_dma.driver().setup_full_duplex()?;
933
934        let empty_rx_buffer = unsafe { self.spi_dma.dma_driver().empty_rx_buffer() };
935
936        let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
937        let chunk_size = self.tx_buf.capacity();
938
939        for chunk in words.chunks(chunk_size) {
940            self.tx_buf.as_mut_slice()[..chunk.len()].copy_from_slice(chunk);
941
942            unsafe { spi.start_dma_transfer(0, chunk.len(), empty_rx_buffer, &mut self.tx_buf)? };
943
944            spi.wait_for_idle_async().await;
945        }
946        spi.defuse();
947
948        Ok(())
949    }
950
951    /// Transfer by writing out a buffer and reading the response from
952    /// the bus into another buffer.
953    #[instability::unstable]
954    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
955        self.spi_dma.wait_for_idle_async().await;
956        self.spi_dma.driver().setup_full_duplex()?;
957
958        let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
959        let chunk_size = min(self.tx_buf.capacity(), self.rx_buf.capacity());
960
961        let common_length = min(read.len(), write.len());
962        let (read_common, read_remainder) = read.split_at_mut(common_length);
963        let (write_common, write_remainder) = write.split_at(common_length);
964
965        for (read_chunk, write_chunk) in read_common
966            .chunks_mut(chunk_size)
967            .zip(write_common.chunks(chunk_size))
968        {
969            self.tx_buf.as_mut_slice()[..write_chunk.len()].copy_from_slice(write_chunk);
970
971            unsafe {
972                spi.start_dma_transfer(
973                    read_chunk.len(),
974                    write_chunk.len(),
975                    &mut self.rx_buf,
976                    &mut self.tx_buf,
977                )?;
978            }
979            spi.wait_for_idle_async().await;
980
981            read_chunk.copy_from_slice(&self.rx_buf.as_slice()[..read_chunk.len()]);
982        }
983
984        spi.defuse();
985
986        if !read_remainder.is_empty() {
987            self.read_async(read_remainder).await
988        } else if !write_remainder.is_empty() {
989            self.write_async(write_remainder).await
990        } else {
991            Ok(())
992        }
993    }
994
995    /// Transfer by writing out a buffer and reading the response from
996    /// the bus into the same buffer.
997    #[instability::unstable]
998    pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
999        self.spi_dma.wait_for_idle_async().await;
1000        self.spi_dma.driver().setup_full_duplex()?;
1001
1002        let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
1003        for chunk in words.chunks_mut(self.tx_buf.capacity()) {
1004            self.tx_buf.as_mut_slice()[..chunk.len()].copy_from_slice(chunk);
1005
1006            unsafe {
1007                spi.start_dma_transfer(
1008                    chunk.len(),
1009                    chunk.len(),
1010                    &mut self.rx_buf,
1011                    &mut self.tx_buf,
1012                )?;
1013            }
1014            spi.wait_for_idle_async().await;
1015            chunk.copy_from_slice(&self.rx_buf.as_slice()[..chunk.len()]);
1016        }
1017
1018        spi.defuse();
1019
1020        Ok(())
1021    }
1022}
1023
1024impl<'d, Dm> SpiDmaBus<'d, Dm>
1025where
1026    Dm: DriverMode,
1027{
1028    /// Creates a new `SpiDmaBus` with the specified SPI instance and DMA
1029    /// buffers.
1030    pub fn new(spi_dma: SpiDma<'d, Dm>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf) -> Self {
1031        Self {
1032            spi_dma,
1033            rx_buf,
1034            tx_buf,
1035        }
1036    }
1037
1038    /// Splits [SpiDmaBus] back into [SpiDma], [DmaRxBuf] and [DmaTxBuf].
1039    #[instability::unstable]
1040    pub fn split(mut self) -> (SpiDma<'d, Dm>, DmaRxBuf, DmaTxBuf) {
1041        self.wait_for_idle();
1042        (self.spi_dma, self.rx_buf, self.tx_buf)
1043    }
1044
1045    fn wait_for_idle(&mut self) {
1046        self.spi_dma.wait_for_idle();
1047    }
1048
1049    /// Change the bus configuration.
1050    ///
1051    /// # Errors
1052    ///
1053    /// If frequency passed in config exceeds
1054    #[cfg_attr(not(esp32h2), doc = " 80MHz")]
1055    #[cfg_attr(esp32h2, doc = " 48MHz")]
1056    /// or is below 70kHz,
1057    /// [`ConfigError::UnsupportedFrequency`] error will be returned.
1058    #[instability::unstable]
1059    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
1060        self.spi_dma.apply_config(config)
1061    }
1062
1063    /// Reads data from the SPI bus using DMA.
1064    #[instability::unstable]
1065    pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
1066        self.wait_for_idle();
1067        self.spi_dma.driver().setup_full_duplex()?;
1068
1069        let empty_tx_buffer = unsafe { self.spi_dma.dma_driver().empty_tx_buffer() };
1070
1071        for chunk in words.chunks_mut(self.rx_buf.capacity()) {
1072            unsafe {
1073                self.spi_dma.start_dma_transfer(
1074                    chunk.len(),
1075                    0,
1076                    &mut self.rx_buf,
1077                    empty_tx_buffer,
1078                )?;
1079            }
1080
1081            self.wait_for_idle();
1082            chunk.copy_from_slice(&self.rx_buf.as_slice()[..chunk.len()]);
1083        }
1084
1085        Ok(())
1086    }
1087
1088    /// Writes data to the SPI bus using DMA.
1089    #[instability::unstable]
1090    pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
1091        self.wait_for_idle();
1092        self.spi_dma.driver().setup_full_duplex()?;
1093        let empty_rx_buffer = unsafe { self.spi_dma.dma_driver().empty_rx_buffer() };
1094
1095        for chunk in words.chunks(self.tx_buf.capacity()) {
1096            self.tx_buf.as_mut_slice()[..chunk.len()].copy_from_slice(chunk);
1097
1098            unsafe {
1099                self.spi_dma.start_dma_transfer(
1100                    0,
1101                    chunk.len(),
1102                    empty_rx_buffer,
1103                    &mut self.tx_buf,
1104                )?;
1105            }
1106
1107            self.wait_for_idle();
1108        }
1109
1110        Ok(())
1111    }
1112
1113    /// Transfers data to and from the SPI bus simultaneously using DMA.
1114    #[instability::unstable]
1115    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
1116        self.wait_for_idle();
1117        self.spi_dma.driver().setup_full_duplex()?;
1118        let chunk_size = min(self.tx_buf.capacity(), self.rx_buf.capacity());
1119
1120        let common_length = min(read.len(), write.len());
1121        let (read_common, read_remainder) = read.split_at_mut(common_length);
1122        let (write_common, write_remainder) = write.split_at(common_length);
1123
1124        for (read_chunk, write_chunk) in read_common
1125            .chunks_mut(chunk_size)
1126            .zip(write_common.chunks(chunk_size))
1127        {
1128            self.tx_buf.as_mut_slice()[..write_chunk.len()].copy_from_slice(write_chunk);
1129
1130            unsafe {
1131                self.spi_dma.start_dma_transfer(
1132                    read_chunk.len(),
1133                    write_chunk.len(),
1134                    &mut self.rx_buf,
1135                    &mut self.tx_buf,
1136                )?;
1137            }
1138            self.wait_for_idle();
1139
1140            read_chunk.copy_from_slice(&self.rx_buf.as_slice()[..read_chunk.len()]);
1141        }
1142
1143        if !read_remainder.is_empty() {
1144            self.read(read_remainder)
1145        } else if !write_remainder.is_empty() {
1146            self.write(write_remainder)
1147        } else {
1148            Ok(())
1149        }
1150    }
1151
1152    /// Transfers data in place on the SPI bus using DMA.
1153    #[instability::unstable]
1154    pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
1155        self.wait_for_idle();
1156        self.spi_dma.driver().setup_full_duplex()?;
1157        let chunk_size = min(self.tx_buf.capacity(), self.rx_buf.capacity());
1158
1159        for chunk in words.chunks_mut(chunk_size) {
1160            self.tx_buf.as_mut_slice()[..chunk.len()].copy_from_slice(chunk);
1161
1162            unsafe {
1163                self.spi_dma.start_dma_transfer(
1164                    chunk.len(),
1165                    chunk.len(),
1166                    &mut self.rx_buf,
1167                    &mut self.tx_buf,
1168                )?;
1169            }
1170            self.wait_for_idle();
1171            chunk.copy_from_slice(&self.rx_buf.as_slice()[..chunk.len()]);
1172        }
1173
1174        Ok(())
1175    }
1176
1177    /// Half-duplex read.
1178    #[instability::unstable]
1179    pub fn half_duplex_read(
1180        &mut self,
1181        data_mode: DataMode,
1182        cmd: Command,
1183        address: Address,
1184        dummy: u8,
1185        buffer: &mut [u8],
1186    ) -> Result<(), Error> {
1187        if buffer.len() > self.rx_buf.capacity() {
1188            return Err(Error::from(DmaError::Overflow));
1189        }
1190        self.wait_for_idle();
1191
1192        unsafe {
1193            self.spi_dma.start_half_duplex_read(
1194                data_mode,
1195                cmd,
1196                address,
1197                dummy,
1198                buffer.len(),
1199                &mut self.rx_buf,
1200            )?;
1201        }
1202
1203        self.wait_for_idle();
1204
1205        buffer.copy_from_slice(&self.rx_buf.as_slice()[..buffer.len()]);
1206
1207        Ok(())
1208    }
1209
1210    /// Half-duplex write.
1211    #[instability::unstable]
1212    pub fn half_duplex_write(
1213        &mut self,
1214        data_mode: DataMode,
1215        cmd: Command,
1216        address: Address,
1217        dummy: u8,
1218        buffer: &[u8],
1219    ) -> Result<(), Error> {
1220        if buffer.len() > self.tx_buf.capacity() {
1221            return Err(Error::from(DmaError::Overflow));
1222        }
1223        self.wait_for_idle();
1224        self.tx_buf.as_mut_slice()[..buffer.len()].copy_from_slice(buffer);
1225
1226        unsafe {
1227            self.spi_dma.start_half_duplex_write(
1228                data_mode,
1229                cmd,
1230                address,
1231                dummy,
1232                buffer.len(),
1233                &mut self.tx_buf,
1234            )?;
1235        }
1236
1237        self.wait_for_idle();
1238
1239        Ok(())
1240    }
1241}
1242
1243#[instability::unstable]
1244impl crate::interrupt::InterruptConfigurable for SpiDmaBus<'_, Blocking> {
1245    /// Sets the interrupt handler
1246    ///
1247    /// Interrupts are not enabled at the peripheral level here.
1248    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
1249        self.spi_dma.set_interrupt_handler(handler);
1250    }
1251}
1252
1253#[instability::unstable]
1254impl<Dm> embassy_embedded_hal::SetConfig for SpiDmaBus<'_, Dm>
1255where
1256    Dm: DriverMode,
1257{
1258    type Config = Config;
1259    type ConfigError = ConfigError;
1260
1261    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
1262        self.apply_config(config)
1263    }
1264}
1265
1266pub(super) struct DmaDriver {
1267    driver: Driver,
1268    dma_peripheral: crate::dma::DmaPeripheral,
1269    state: &'static DmaState,
1270}
1271
1272impl DmaDriver {
1273    unsafe fn empty_rx_buffer(&self) -> &'static mut DmaRxBuf {
1274        unsafe { self.state.empty_rx_buffer() }
1275    }
1276
1277    unsafe fn empty_tx_buffer(&self) -> &'static mut DmaTxBuf {
1278        unsafe { self.state.empty_tx_buffer() }
1279    }
1280
1281    fn abort_transfer(&self) {
1282        // The SPI peripheral is controlling how much data we transfer, so let's
1283        // update its counter.
1284        // 0 doesn't take effect on ESP32 and cuts the currently transmitted byte
1285        // immediately.
1286        // 1 seems to stop after transmitting the current byte which is somewhat less
1287        // impolite.
1288        self.driver.configure_datalen(1, 1);
1289        self.driver.update();
1290    }
1291
1292    fn regs(&self) -> &RegisterBlock {
1293        self.driver.regs()
1294    }
1295
1296    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1297    unsafe fn start_transfer_dma<Dm: DriverMode>(
1298        &self,
1299        _full_duplex: bool,
1300        rx_len: usize,
1301        tx_len: usize,
1302        rx_buffer: &mut impl DmaRxBuffer,
1303        tx_buffer: &mut impl DmaTxBuffer,
1304        channel: &mut Channel<Dm, PeripheralDmaChannel<AnySpi<'_>>>,
1305    ) -> Result<(), Error> {
1306        #[cfg(esp32s2)]
1307        {
1308            // without this a transfer after a write will fail
1309            self.regs().dma_out_link().write(|w| unsafe { w.bits(0) });
1310            self.regs().dma_in_link().write(|w| unsafe { w.bits(0) });
1311        }
1312
1313        self.driver.configure_datalen(rx_len, tx_len);
1314
1315        // enable the MISO and MOSI if needed
1316        self.regs()
1317            .user()
1318            .modify(|_, w| w.usr_miso().bit(rx_len > 0).usr_mosi().bit(tx_len > 0));
1319
1320        self.enable_dma();
1321
1322        if rx_len > 0 {
1323            unsafe {
1324                channel
1325                    .rx
1326                    .prepare_transfer(self.dma_peripheral, rx_buffer)
1327                    .and_then(|_| channel.rx.start_transfer())?;
1328            }
1329        } else {
1330            #[cfg(esp32)]
1331            {
1332                // see https://github.com/espressif/esp-idf/commit/366e4397e9dae9d93fe69ea9d389b5743295886f
1333                // see https://github.com/espressif/esp-idf/commit/0c3653b1fd7151001143451d4aa95dbf15ee8506
1334                if _full_duplex {
1335                    self.regs()
1336                        .dma_in_link()
1337                        .modify(|_, w| unsafe { w.inlink_addr().bits(0) });
1338                    self.regs()
1339                        .dma_in_link()
1340                        .modify(|_, w| w.inlink_start().set_bit());
1341                }
1342            }
1343        }
1344        if tx_len > 0 {
1345            unsafe {
1346                channel
1347                    .tx
1348                    .prepare_transfer(self.dma_peripheral, tx_buffer)
1349                    .and_then(|_| channel.tx.start_transfer())?;
1350            }
1351        }
1352
1353        #[cfg(dma_kind = "gdma")]
1354        self.reset_dma();
1355
1356        self.driver.start_operation();
1357
1358        Ok(())
1359    }
1360
1361    fn enable_dma(&self) {
1362        #[cfg(dma_kind = "gdma")]
1363        // for non GDMA this is done in `assign_tx_device` / `assign_rx_device`
1364        self.regs().dma_conf().modify(|_, w| {
1365            w.dma_tx_ena().set_bit();
1366            w.dma_rx_ena().set_bit()
1367        });
1368
1369        #[cfg(dma_kind = "pdma")]
1370        self.reset_dma();
1371    }
1372
1373    fn reset_dma(&self) {
1374        #[cfg(dma_kind = "pdma")]
1375        self.regs().dma_conf().toggle(|w, bit| {
1376            w.out_rst().bit(bit);
1377            w.in_rst().bit(bit);
1378            w.ahbm_fifo_rst().bit(bit);
1379            w.ahbm_rst().bit(bit)
1380        });
1381
1382        #[cfg(dma_kind = "gdma")]
1383        self.regs().dma_conf().toggle(|w, bit| {
1384            w.rx_afifo_rst().bit(bit);
1385            w.buf_afifo_rst().bit(bit);
1386            w.dma_afifo_rst().bit(bit)
1387        });
1388
1389        self.clear_dma_interrupts();
1390    }
1391
1392    #[cfg(dma_kind = "gdma")]
1393    fn clear_dma_interrupts(&self) {
1394        self.regs().dma_int_clr().write(|w| {
1395            w.dma_infifo_full_err().clear_bit_by_one();
1396            w.dma_outfifo_empty_err().clear_bit_by_one();
1397            w.trans_done().clear_bit_by_one();
1398            w.mst_rx_afifo_wfull_err().clear_bit_by_one();
1399            w.mst_tx_afifo_rempty_err().clear_bit_by_one()
1400        });
1401    }
1402
1403    #[cfg(dma_kind = "pdma")]
1404    fn clear_dma_interrupts(&self) {
1405        self.regs().dma_int_clr().write(|w| {
1406            w.inlink_dscr_empty().clear_bit_by_one();
1407            w.outlink_dscr_error().clear_bit_by_one();
1408            w.inlink_dscr_error().clear_bit_by_one();
1409            w.in_done().clear_bit_by_one();
1410            w.in_err_eof().clear_bit_by_one();
1411            w.in_suc_eof().clear_bit_by_one();
1412            w.out_done().clear_bit_by_one();
1413            w.out_eof().clear_bit_by_one();
1414            w.out_total_eof().clear_bit_by_one()
1415        });
1416    }
1417}
1418
1419impl<'d> DmaEligible for AnySpi<'d> {
1420    #[cfg(dma_kind = "gdma")]
1421    type Dma = crate::dma::AnyGdmaChannel<'d>;
1422    #[cfg(dma_kind = "pdma")]
1423    type Dma = crate::dma::AnySpiDmaChannel<'d>;
1424
1425    fn dma_peripheral(&self) -> crate::dma::DmaPeripheral {
1426        let (info, _state) = self.dma_parts();
1427        info.dma_peripheral
1428    }
1429}
1430
1431#[instability::unstable]
1432impl embedded_hal_async::spi::SpiBus for SpiDmaBus<'_, Async> {
1433    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
1434        self.read_async(words).await
1435    }
1436
1437    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
1438        self.write_async(words).await
1439    }
1440
1441    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
1442        self.transfer_async(read, write).await
1443    }
1444
1445    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
1446        self.transfer_in_place_async(words).await
1447    }
1448
1449    async fn flush(&mut self) -> Result<(), Self::Error> {
1450        // All operations currently flush so this is no-op.
1451        Ok(())
1452    }
1453}
1454
1455#[instability::unstable]
1456impl<Dm> ErrorType for SpiDmaBus<'_, Dm>
1457where
1458    Dm: DriverMode,
1459{
1460    type Error = Error;
1461}
1462
1463#[instability::unstable]
1464impl<Dm> SpiBus for SpiDmaBus<'_, Dm>
1465where
1466    Dm: DriverMode,
1467{
1468    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
1469        self.read(words)
1470    }
1471
1472    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
1473        self.write(words)
1474    }
1475
1476    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
1477        self.transfer(read, write)
1478    }
1479
1480    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
1481        self.transfer_in_place(words)
1482    }
1483
1484    fn flush(&mut self) -> Result<(), Self::Error> {
1485        // All operations currently flush so this is no-op.
1486        Ok(())
1487    }
1488}
1489
1490struct DmaInfo {
1491    dma_peripheral: crate::dma::DmaPeripheral,
1492}
1493struct DmaState {
1494    tx_transfer_in_progress: Cell<bool>,
1495    rx_transfer_in_progress: Cell<bool>,
1496
1497    empty_rx_buffer: UnsafeCell<MaybeUninit<DmaRxBuf>>,
1498    empty_tx_buffer: UnsafeCell<MaybeUninit<DmaTxBuf>>,
1499}
1500
1501impl DmaState {
1502    // Syntactic helper to get a mutable reference to the "empty" RX DMA buffer.
1503    //
1504    // # Safety
1505    //
1506    // The caller must ensure that Rust's aliasing rules are upheld.
1507    #[allow(
1508        clippy::mut_from_ref,
1509        reason = "Safety requirements ensure this is okay"
1510    )]
1511    unsafe fn empty_rx_buffer(&self) -> &mut DmaRxBuf {
1512        unsafe { (&mut *self.empty_rx_buffer.get()).assume_init_mut() }
1513    }
1514
1515    // Syntactic helper to get a mutable reference to the "empty" TX DMA buffer.
1516    //
1517    // # Safety
1518    //
1519    // The caller must ensure that Rust's aliasing rules are upheld.
1520    #[allow(
1521        clippy::mut_from_ref,
1522        reason = "Safety requirements ensure this is okay"
1523    )]
1524    unsafe fn empty_tx_buffer(&self) -> &mut DmaTxBuf {
1525        unsafe { (&mut *self.empty_tx_buffer.get()).assume_init_mut() }
1526    }
1527}
1528
1529// SAFETY: State belongs to the currently constructed driver instance. As such, it'll not be
1530// accessed concurrently in multiple threads.
1531unsafe impl Sync for DmaState {}
1532
1533for_each_spi_master!(
1534    (all $( ($peri:ident, $sys:ident, $sclk:ident $_cs:tt $_sio:tt $(, $is_qspi:tt)?)),* ) => {
1535        impl AnySpi<'_> {
1536            #[inline(always)]
1537            fn dma_parts(&self) -> (&'static DmaInfo, &'static DmaState) {
1538                match &self.0 {
1539                    $(
1540                        super::any::Inner::$sys(_spi) => {
1541                            static DMA_INFO: DmaInfo = DmaInfo {
1542                                dma_peripheral: crate::dma::DmaPeripheral::$sys,
1543                            };
1544
1545                            static DMA_STATE: DmaState = DmaState {
1546                                tx_transfer_in_progress: Cell::new(false),
1547                                rx_transfer_in_progress: Cell::new(false),
1548
1549                                empty_rx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
1550                                empty_tx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
1551                            };
1552
1553                            (&DMA_INFO, &DMA_STATE)
1554                        }
1555                    )*
1556                }
1557            }
1558
1559            #[inline(always)]
1560            fn dma_state(&self) -> &'static DmaState {
1561                let (_, state) = self.dma_parts();
1562                state
1563            }
1564
1565            #[inline(always)]
1566            fn dma_info(&self) -> &'static DmaInfo {
1567                let (info, _) = self.dma_parts();
1568                info
1569            }
1570        }
1571    };
1572);
1573
1574impl SpiWrapper<'_> {
1575    fn dma_state(&self) -> &'static DmaState {
1576        self.spi.dma_state()
1577    }
1578
1579    #[inline(always)]
1580    fn dma_peripheral(&self) -> crate::dma::DmaPeripheral {
1581        self.spi.dma_peripheral()
1582    }
1583}