1
use core::{
2
    cell::{Cell, UnsafeCell},
3
    cmp::min,
4
    mem::{ManuallyDrop, MaybeUninit},
5
    pin::Pin,
6
    ptr::NonNull,
7
    sync::atomic::{Ordering, fence},
8
    task::{Context, Poll},
9
};
10

            
11
#[cfg(feature = "unstable")]
12
use embedded_hal::spi::{ErrorType, SpiBus};
13
use enumset::EnumSet;
14
#[cfg(place_spi_master_driver_in_ram)]
15
use procmacros::ram;
16

            
17
use super::*;
18
use crate::{
19
    RegisterToggle,
20
    dma::{
21
        CHUNK_SIZE,
22
        Channel,
23
        DmaDescriptor,
24
        DmaEligiblePeripheral,
25
        DmaRxBuf,
26
        DmaRxBuffer,
27
        DmaTxBuf,
28
        DmaTxBuffer,
29
        NoBuffer,
30
        ScopedDmaRxBuf,
31
        ScopedDmaTxBuf,
32
        TransferDirection,
33
        aligned::{DmaAlignedMut, InternalMemory},
34
        asynch::DmaRxFuture,
35
        prepare_for_rx,
36
        prepare_for_tx,
37
    },
38
    pac::spi2::RegisterBlock,
39
    private::DropGuard,
40
    soc::is_slice_in_dram,
41
    spi::{DmaError, master::low_level::SpiClockGuard},
42
};
43
#[cfg(dma_can_access_psram)]
44
use crate::{dma::ManualWritebackBuffer, soc::is_slice_in_psram};
45

            
46
const MAX_DMA_SIZE: usize = 32736;
47

            
48
impl<'d> Spi<'d, Blocking> {
49
    #[doc_replace(
50
        "dma_channel" => {
51
            cfg(spi_master_dma_engine = "SPI_DMA") => "DMA_SPI2",
52
            cfg(spi_master_dma_engine = "AHB_GDMA") => "DMA_CH0",
53
            cfg(spi_master_dma_engine = "AXI_GDMA") => "DMA_AXI_CH0",
54
        }
55
    )]
56
    /// Converts the driver into an [`SpiDma`] driver that uses the specified DMA channel.
57
    ///
58
    /// ```rust, no_run
59
    /// # {before_snippet}
60
    /// use esp_hal::spi::{
61
    ///     Mode,
62
    ///     master::{Config, Spi},
63
    /// };
64
    ///
65
    /// let mut spi_dma = Spi::new(
66
    ///     peripherals.SPI2,
67
    ///     Config::default()
68
    ///         .with_frequency(Rate::from_khz(100))
69
    ///         .with_mode(Mode::_0),
70
    /// )?
71
    /// .with_dma(peripherals.__dma_channel__);
72
    /// # {after_snippet}
73
    /// ```
74
    #[instability::unstable]
75
    pub fn with_dma(
76
        self,
77
        channel: impl SpiMasterDmaChannel<'d, AnySpi<'d>>,
78
    ) -> SpiDma<'d, crate::Blocking> {
79
2
        SpiDma::new_from_spi(self, channel.into())
80
    }
81
}
82

            
83
#[doc_replace(
84
    "dma_channel" => {
85
        cfg(spi_master_dma_engine = "SPI_DMA") => "DMA_SPI2",
86
        cfg(spi_master_dma_engine = "AHB_GDMA") => "DMA_CH0",
87
        cfg(spi_master_dma_engine = "AXI_GDMA") => "DMA_AXI_CH0",
88
    }
89
)]
90
/// DMA-controlled SPI driver.
91
///
92
/// This driver uses DMA to transfer data, allowing the CPU to continue working while the SPI
93
/// transfer is in progress.
94
///
95
/// The driver provides two separate approaches to transferring data:
96
///
97
/// - The slice-based API allows transferring data from/to slices of memory. The data may be copied
98
///   into an internal buffer before the transfer begins. A pair of copy buffers can be set up by
99
///   passing them to [`with_buffers`](SpiDma::with_buffers) before the first transfer begins. For
100
///   more details on when copying is necessary, see the documentation of the
101
///   [`with_buffers`](SpiDma::with_buffers) method.
102
/// - The buffer API allows transferring externally managed buffers. In this mode, the buffers to be
103
///   transferred are provided by the caller. The buffer objects ensure that data is located in
104
///   appropriate memory regions. The buffers and the driver object are moved into transfer objects
105
///   for the duration of the transfer. These functions take [`DmaRxBuf`] and [`DmaTxBuf`] objects
106
///   as arguments as well as the number of bytes to transfer, and their names end with `_buffer`
107
///
108
/// These approaches provide different trade-offs between memory usage / CPU overhead and ease of
109
/// use. `embedded-hal` traits are implemented by the slice-based API's functions.
110
///
111
/// # Examples
112
///
113
/// ```rust, no_run
114
/// # {before_snippet}
115
/// use esp_hal::{
116
///     dma::{DmaRxBuf, DmaTxBuf},
117
///     dma_rx_buffer,
118
///     dma_tx_buffer,
119
///     spi::{
120
///         Mode,
121
///         master::{Config, Spi},
122
///     },
123
/// };
124
///
125
/// // Optional: create and set up copy buffers.
126
/// let dma_rx_buf = dma_rx_buffer!(32000)?;
127
/// let dma_tx_buf = dma_tx_buffer!(32000)?;
128
///
129
/// let mut spi = Spi::new(
130
///     peripherals.SPI2,
131
///     Config::default()
132
///         .with_frequency(Rate::from_khz(100))
133
///         .with_mode(Mode::_0),
134
/// )?
135
/// .with_dma(peripherals.__dma_channel__)
136
/// .with_buffers(dma_rx_buf, dma_tx_buf);
137
/// #
138
/// # {after_snippet}
139
/// ```
140
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141
pub struct SpiDma<'d, Dm>
142
where
143
    Dm: DriverMode,
144
{
145
    spi: SpiWrapper<'d>,
146
    pub(crate) channel: Channel<Dm, SpiMasterErased<'d>>,
147
}
148

            
149
impl<Dm> crate::private::Sealed for SpiDma<'_, Dm> where Dm: DriverMode {}
150

            
151
impl<Dm> core::fmt::Debug for SpiDma<'_, Dm>
152
where
153
    Dm: DriverMode + core::fmt::Debug,
154
{
155
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156
        f.debug_struct("SpiDma").field("spi", &self.spi).finish()
157
    }
158
}
159

            
160
#[instability::unstable]
161
impl crate::interrupt::InterruptConfigurable for SpiDma<'_, Blocking> {
162
    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
163
        self.set_interrupt_handler(handler);
164
    }
165
}
166

            
167
#[instability::unstable]
168
impl<Dm> embassy_embedded_hal::SetConfig for SpiDma<'_, Dm>
169
where
170
    Dm: DriverMode,
171
{
172
    type Config = Config;
173
    type ConfigError = ConfigError;
174

            
175
    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
176
        self.apply_config(config)
177
    }
178
}
179

            
180
#[instability::unstable]
181
impl<Dm> ErrorType for SpiDma<'_, Dm>
182
where
183
    Dm: DriverMode,
184
{
185
    type Error = Error;
186
}
187

            
188
#[instability::unstable]
189
impl<Dm> SpiBus for SpiDma<'_, Dm>
190
where
191
    Dm: DriverMode,
192
{
193
    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
194
        self.read(words)
195
    }
196

            
197
    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
198
        self.write(words)
199
    }
200

            
201
    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
202
        self.transfer(read, write)
203
    }
204

            
205
    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
206
        self.transfer_in_place(words)
207
    }
208

            
209
    fn flush(&mut self) -> Result<(), Self::Error> {
210
        // DMA limitation - we must ensure the transfers complete before returning
211
        // to user code, otherwise the user might access the buffers while the transfer
212
        // is still in progress. Therefore, there is no such thing as "flushing".
213
        Ok(())
214
    }
215
}
216

            
217
#[instability::unstable]
218
impl embedded_hal_async::spi::SpiBus for SpiDma<'_, Async> {
219
    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
220
        self.read_async(words).await
221
    }
222

            
223
    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
224
        self.write_async(words).await
225
    }
226

            
227
    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
228
        self.transfer_async(read, write).await
229
    }
230

            
231
    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
232
        self.transfer_in_place_async(words).await
233
    }
234

            
235
    async fn flush(&mut self) -> Result<(), Self::Error> {
236
        // DMA limitation - we must ensure the transfers complete before returning
237
        // to user code, otherwise the user might access the buffers while the transfer
238
        // is still in progress. Therefore, there is no such thing as "flushing".
239
        Ok(())
240
    }
241
}
242

            
243
impl<'d> SpiDma<'d, Blocking> {
244
    /// Converts the SPI driver into async mode.
245
    #[instability::unstable]
246
5
    pub fn into_async(self) -> SpiDma<'d, Async> {
247
        self.spi
248
            .set_interrupt_handler(self.spi.info().async_handler);
249
5
        SpiDma {
250
5
            spi: self.spi,
251
            channel: self.channel.into_async(),
252
        }
253
5
    }
254

            
255
    fn new_inner(spi: SpiWrapper<'d>, channel: SpiMasterErased<'d>) -> Self {
256
36
        let channel = Channel::new(channel);
257
36
        channel.runtime_ensure_compatible(spi.spi.dma_peripheral());
258

            
259
        let state = spi.spi.dma_state();
260

            
261
        state.tx_transfer_in_progress.set(false);
262
        state.rx_transfer_in_progress.set(false);
263

            
264
        // Safety: The descriptors occupy their own shared cache line and are updated in a
265
        // synchronised fashion.
266
        let (tx_descriptors, rx_descriptors) = unsafe {
267
            let descriptors = (&mut *state.descriptors.get()).get_mut().into_inner();
268
            descriptors.fill(DmaDescriptor::EMPTY);
269
            let (tx_descriptors, rx_descriptors) = descriptors.split_at_mut(1);
270
            (
271
                DmaAlignedMut::new_unchecked(tx_descriptors),
272
                DmaAlignedMut::new_unchecked(rx_descriptors),
273
            )
274
        };
275

            
276
        let tx_buffer = cfg_select! {
277
            all(spi_master_version = "1", spi_address_workaround) => unsafe {
278
                (&mut *state.default_tx_buffer.get()).get_mut().unsize()
279
            },
280
            _ => unsafe { DmaAlignedMut::new_unchecked(&mut [][..]) },
281
        };
282

            
283
        let rx_buffer = unwrap!(DmaRxBuf::new(rx_descriptors, unsafe {
284
            DmaAlignedMut::new_unchecked(&mut [])
285
        }));
286
        let tx_buffer = unwrap!(DmaTxBuf::new(tx_descriptors, tx_buffer));
287

            
288
        // The buffers must be set up when creating the driver.
289
        unsafe { (&mut *state.tx_buffer.get()).write(tx_buffer.into_scoped()) };
290
        unsafe { (&mut *state.rx_buffer.get()).write(rx_buffer.into_scoped()) };
291

            
292
36
        Self { spi, channel }
293
    }
294

            
295
36
    pub(super) fn new_from_spi(
296
        spi_driver: Spi<'d, Blocking>,
297
        channel: SpiMasterErased<'d>,
298
    ) -> Self {
299
        let spi = spi_driver.spi;
300

            
301
        Self::new_inner(spi, channel)
302
36
    }
303

            
304
    /// Listens for the given interrupts.
305
    #[instability::unstable]
306
    pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
307
        self.driver().enable_listen(interrupts.into(), true);
308
    }
309

            
310
    /// Unlistens from the given interrupts.
311
    #[instability::unstable]
312
    pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
313
        self.driver().enable_listen(interrupts.into(), false);
314
    }
315

            
316
    /// Returns the asserted interrupts.
317
    #[instability::unstable]
318
    pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
319
        self.driver().interrupts()
320
    }
321

            
322
    /// Resets asserted interrupts.
323
    #[instability::unstable]
324
    pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
325
        self.driver().clear_interrupts(interrupts.into());
326
    }
327

            
328
    #[cfg_attr(
329
        not(multi_core),
330
        doc = "Registers an interrupt handler for the peripheral."
331
    )]
332
    #[cfg_attr(
333
        multi_core,
334
        doc = "Registers an interrupt handler for the peripheral on the current core."
335
    )]
336
    #[doc = ""]
337
    /// Replaces any previously registered interrupt handlers.
338
    ///
339
    /// The default/unhandled interrupt handler can be restored with
340
    /// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER]
341
    ///
342
    /// # Panics
343
    ///
344
    /// Panics if passed interrupt handler is invalid (e.g. has priority
345
    /// `None`)
346
    #[instability::unstable]
347
    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
348
        self.spi.set_interrupt_handler(handler);
349
    }
350
}
351

            
352
impl<'d> SpiDma<'d, Async> {
353
    /// Converts the SPI instance into blocking mode.
354
    #[instability::unstable]
355
    pub fn into_blocking(self) -> SpiDma<'d, Blocking> {
356
        self.spi.disable_peri_interrupt_on_all_cores();
357
        SpiDma {
358
            spi: self.spi,
359
            channel: self.channel.into_blocking(),
360
        }
361
    }
362

            
363
9
    async fn wait_for_idle_async(&mut self) {
364
6
        if self.dma_driver().state.rx_transfer_in_progress.get() {
365
10
            _ = DmaRxFuture::new(&mut self.channel.rx).await;
366
            self.dma_driver().state.rx_transfer_in_progress.set(false);
367
        }
368

            
369
        struct Fut(Driver);
370
        impl Fut {
371
            const DONE_EVENTS: EnumSet<SpiInterrupt> =
372
                enumset::enum_set!(SpiInterrupt::TransferDone);
373
        }
374
        impl Future for Fut {
375
            type Output = ();
376

            
377
            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
378
                if !self.0.interrupts().is_disjoint(Self::DONE_EVENTS) {
379
                    #[cfg(any(spi_master_version = "1", spi_master_version = "2"))]
380
                    // Need to poll for done-ness even after interrupt fires.
381
                    if self.0.busy() {
382
                        cx.waker().wake_by_ref();
383
                        return Poll::Pending;
384
                    }
385

            
386
                    self.0.clear_interrupts(Self::DONE_EVENTS);
387
                    return Poll::Ready(());
388
                }
389

            
390
                self.0.state.waker.register(cx.waker());
391
                self.0.enable_listen(Self::DONE_EVENTS, true);
392
                Poll::Pending
393
            }
394
        }
395
        impl Drop for Fut {
396
            fn drop(&mut self) {
397
                self.0.enable_listen(Self::DONE_EVENTS, false);
398
            }
399
        }
400

            
401
5
        if !self.is_done() {
402
            Fut(self.driver()).await;
403
        }
404

            
405
6
        if self.dma_driver().state.tx_transfer_in_progress.get() {
406
            // In case DMA TX buffer is bigger than what the SPI consumes, stop the DMA.
407
1
            if !self.channel.tx.is_done() {
408
                self.channel.tx.stop_transfer();
409
            }
410
            self.dma_driver().state.tx_transfer_in_progress.set(false);
411
        }
412
    }
413

            
414
    /// Fills the given buffer with data from the bus.
415
    #[instability::unstable]
416
    pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
417
        if words.is_empty() {
418
            return Ok(());
419
        }
420

            
421
        let _clock = SpiClockGuard::new(self.spi.info());
422

            
423
        self.driver().setup_full_duplex()?;
424

            
425
        if self.use_blocking_transfer(words.len()) {
426
            self.dma_driver().disable_dma();
427
            return self.driver().read(words);
428
        }
429

            
430
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
431
        let mut maybe_copy_buffer = match DmaOperationKind::for_read(words) {
432
            DmaOperationKind::Copied => {
433
                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
434
            }
435
            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
436
                descriptors: &mut descriptors,
437
                #[cfg(dma_can_access_psram)]
438
                align_buffer: [const { None }; 2],
439
            },
440
        };
441

            
442
        if maybe_copy_buffer.chunk_size() == 0 {
443
            return Err(Error::from(DmaError::BufferTooSmall));
444
        }
445

            
446
        for chunk in words.chunks_mut(maybe_copy_buffer.chunk_size()) {
447
            let read_bytes = chunk.len();
448
            let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *chunk)) };
449
            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
450

            
451
            self.transfer_buffers_dma_async(read_bytes, 0, rx_buffer, tx_buffer)
452
                .await?;
453

            
454
            maybe_copy_buffer.finish(chunk);
455
        }
456

            
457
        Ok(())
458
    }
459

            
460
    /// Transmits the given buffer to the bus.
461
    #[instability::unstable]
462
    pub async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
463
        if words.is_empty() {
464
            return Ok(());
465
        }
466

            
467
        let _clock = SpiClockGuard::new(self.spi.info());
468

            
469
        self.driver().setup_full_duplex()?;
470

            
471
        if self.use_blocking_transfer(words.len()) {
472
            self.dma_driver().disable_dma();
473
            return self.driver().write(words);
474
        }
475

            
476
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
477
        let mut maybe_copy_buffer = match DmaOperationKind::for_write(words) {
478
            DmaOperationKind::Copied => {
479
                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
480
            }
481
            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
482
        };
483

            
484
        if maybe_copy_buffer.chunk_size() == 0 {
485
            return Err(Error::from(DmaError::BufferTooSmall));
486
        }
487

            
488
        for chunk in words.chunks(maybe_copy_buffer.chunk_size()) {
489
            let write_bytes = chunk.len();
490
            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
491
            let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(chunk)) };
492

            
493
            self.transfer_buffers_dma_async(0, write_bytes, rx_buffer, tx_buffer)
494
                .await?;
495
        }
496

            
497
        Ok(())
498
    }
499

            
500
    /// Transfers by writing out a buffer and reading the response from
501
    /// the bus into another buffer.
502
    #[instability::unstable]
503
    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
504
        if read.is_empty() && write.is_empty() {
505
            return Ok(());
506
        }
507

            
508
        let _clock = SpiClockGuard::new(self.spi.info());
509

            
510
        self.driver().setup_full_duplex()?;
511

            
512
        if self.use_blocking_transfer(read.len().max(write.len())) {
513
            self.dma_driver().disable_dma();
514
            return if read.is_empty() {
515
                self.driver().write(write)
516
            } else if write.is_empty() {
517
                self.driver().read(read)
518
            } else {
519
                self.driver().transfer(read, write)
520
            };
521
        }
522

            
523
        let common_length = min(read.len(), write.len());
524
        let (read_common, read_remainder) = read.split_at_mut(common_length);
525
        let (write_common, write_remainder) = write.split_at(common_length);
526

            
527
        // DmaOperationKind must be determined on the sub-slices actually passed to DMA.
528
        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
529
        let mut maybe_copy_rx_buffer = match DmaOperationKind::for_read(read_common) {
530
            DmaOperationKind::Copied => {
531
                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
532
            }
533
            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
534
                descriptors: &mut rx_descriptors,
535
                #[cfg(dma_can_access_psram)]
536
                align_buffer: [const { None }; 2],
537
            },
538
        };
539

            
540
        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
541
        let mut maybe_copy_tx_buffer = match DmaOperationKind::for_write(write_common) {
542
            DmaOperationKind::Copied => {
543
                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
544
            }
545
            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut tx_descriptors),
546
        };
547

            
548
        let chunk_size = min(
549
            maybe_copy_rx_buffer.chunk_size(),
550
            maybe_copy_tx_buffer.chunk_size(),
551
        );
552

            
553
        if chunk_size == 0 {
554
            return Err(Error::from(DmaError::BufferTooSmall));
555
        }
556

            
557
        for (read_chunk, write_chunk) in read_common
558
            .chunks_mut(chunk_size)
559
            .zip(write_common.chunks(chunk_size))
560
        {
561
            let read_bytes = read_chunk.len();
562
            let write_bytes = write_chunk.len();
563
            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(NonNull::from(write_chunk)) };
564
            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(NonNull::from(&mut *read_chunk)) };
565

            
566
            self.transfer_buffers_dma_async(read_bytes, write_bytes, rx_buffer, tx_buffer)
567
                .await?;
568

            
569
            maybe_copy_rx_buffer.finish(read_chunk);
570
        }
571

            
572
        if !read_remainder.is_empty() {
573
            self.read_async(read_remainder).await
574
        } else if !write_remainder.is_empty() {
575
            self.write_async(write_remainder).await
576
        } else {
577
            Ok(())
578
        }
579
    }
580

            
581
    /// Transfers by writing out a buffer and reading the response from
582
    /// the bus into the same buffer.
583
    #[instability::unstable]
584
    pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
585
        if words.is_empty() {
586
            return Ok(());
587
        }
588

            
589
        let _clock = SpiClockGuard::new(self.spi.info());
590
        self.driver().setup_full_duplex()?;
591

            
592
        if self.use_blocking_transfer(words.len()) {
593
            self.dma_driver().disable_dma();
594
            return self.driver().transfer_in_place(words);
595
        }
596

            
597
        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
598
        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
599
        let (mut maybe_copy_rx_buffer, mut maybe_copy_tx_buffer) =
600
            match DmaOperationKind::for_write(words) {
601
                DmaOperationKind::Copied => (
602
                    MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() }),
603
                    MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() }),
604
                ),
605
                DmaOperationKind::InPlace => (
606
                    MaybeCopyRxBuf::Direct {
607
                        descriptors: &mut rx_descriptors,
608
                        #[cfg(dma_can_access_psram)]
609
                        align_buffer: [const { None }; 2],
610
                    },
611
                    MaybeCopyTxBuf::Direct(&mut tx_descriptors),
612
                ),
613
            };
614

            
615
        let chunk_size = min(
616
            maybe_copy_rx_buffer.chunk_size(),
617
            maybe_copy_tx_buffer.chunk_size(),
618
        );
619

            
620
        if chunk_size == 0 {
621
            return Err(Error::from(DmaError::BufferTooSmall));
622
        }
623

            
624
        for chunk in words.chunks_mut(chunk_size) {
625
            let bytes = chunk.len();
626
            let ptr = NonNull::from(&mut *chunk);
627
            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(ptr) };
628
            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(ptr) };
629

            
630
            self.transfer_buffers_dma_async(bytes, bytes, rx_buffer, tx_buffer)
631
                .await?;
632

            
633
            maybe_copy_rx_buffer.finish(chunk);
634
        }
635

            
636
        Ok(())
637
    }
638

            
639
    /// Half-duplex read.
640
    ///
641
    /// This performs the command, address, dummy, and data phases as a single
642
    /// SPI transaction. Because command and address phases cannot be split
643
    /// across multiple DMA transfers, `buffer` must fit in one DMA transfer or
644
    /// in the configured internal RX copy buffer.
645
    #[instability::unstable]
646
    pub async fn half_duplex_read_async(
647
6
        &mut self,
648
6
        data_mode: DataMode,
649
6
        cmd: Command,
650
6
        address: Address,
651
6
        dummy: u8,
652
6
        buffer: &mut [u8],
653
10
    ) -> Result<(), Error> {
654
6
        let _clock = SpiClockGuard::new(self.spi.info());
655

            
656
6
        if buffer.is_empty() {
657
            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
658
            self.half_duplex_read_dma_async(data_mode, cmd, address, dummy, 0, rx_buffer)
659
                .await?;
660
            return Ok(());
661
        }
662

            
663
        // Transfers below the configured threshold can skip the DMA setup cost entirely.
664
6
        if self.use_blocking_transfer(buffer.len()) {
665
2
            self.dma_driver().disable_dma();
666
            return self
667
2
                .driver()
668
2
                .half_duplex_read(data_mode, cmd, address, dummy, buffer);
669
        }
670

            
671
4
        let operation = DmaOperationKind::for_read(buffer);
672
4
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
673
4
        let mut maybe_copy_buffer = match operation {
674
            DmaOperationKind::Copied => {
675
                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
676
            }
677
            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
678
                descriptors: &mut descriptors,
679
                #[cfg(dma_can_access_psram)]
680
                align_buffer: [const { None }; 2],
681
            },
682
        };
683

            
684
        let chunk_size = maybe_copy_buffer.chunk_size();
685
        if chunk_size == 0 {
686
            return Err(Error::from(DmaError::BufferTooSmall));
687
        }
688
4
        if buffer.len() > chunk_size {
689
            return match operation {
690
                DmaOperationKind::Copied => Err(Error::from(DmaError::Overflow)),
691
                DmaOperationKind::InPlace => Err(Error::MaxDmaTransferSizeExceeded),
692
            };
693
        }
694

            
695
4
        let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *buffer)) };
696
4
        self.half_duplex_read_dma_async(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)
697
4
            .await?;
698
4
        maybe_copy_buffer.finish(buffer);
699

            
700
        Ok(())
701
    }
702

            
703
    /// Half-duplex write.
704
    ///
705
    /// This performs the command, address, dummy, and data phases as a single
706
    /// SPI transaction. Because command and address phases cannot be split
707
    /// across multiple DMA transfers, `buffer` must fit in one DMA transfer or
708
    /// in the configured internal TX copy buffer.
709
    #[instability::unstable]
710
    pub async fn half_duplex_write_async(
711
        &mut self,
712
        data_mode: DataMode,
713
        cmd: Command,
714
        address: Address,
715
        dummy: u8,
716
        buffer: &[u8],
717
    ) -> Result<(), Error> {
718
        let _clock = SpiClockGuard::new(self.spi.info());
719

            
720
        if buffer.is_empty() {
721
            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
722
            self.half_duplex_write_dma_async(data_mode, cmd, address, dummy, 0, tx_buffer)
723
                .await?;
724
            return Ok(());
725
        }
726

            
727
        // Transfers below the configured threshold can skip the DMA setup cost entirely.
728
        if self.use_blocking_transfer(buffer.len()) {
729
            self.dma_driver().disable_dma();
730
            return self
731
                .driver()
732
                .half_duplex_write(data_mode, cmd, address, dummy, buffer);
733
        }
734

            
735
        let operation = DmaOperationKind::for_write(buffer);
736
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
737
        let mut maybe_copy_buffer = match operation {
738
            DmaOperationKind::Copied => {
739
                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
740
            }
741
            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
742
        };
743

            
744
        let chunk_size = maybe_copy_buffer.chunk_size();
745
        if chunk_size == 0 {
746
            return Err(Error::from(DmaError::BufferTooSmall));
747
        }
748
        if buffer.len() > chunk_size {
749
            return match operation {
750
                DmaOperationKind::Copied => Err(Error::from(DmaError::Overflow)),
751
                DmaOperationKind::InPlace => Err(Error::MaxDmaTransferSizeExceeded),
752
            };
753
        }
754

            
755
        let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(buffer)) };
756
        self.half_duplex_write_dma_async(data_mode, cmd, address, dummy, buffer.len(), tx_buffer)
757
            .await
758
    }
759

            
760
    async fn transfer_buffers_dma_async(
761
        &mut self,
762
        read_bytes: usize,
763
        write_bytes: usize,
764
        mut rx_buffer: impl DmaRxBuffer,
765
        mut tx_buffer: impl DmaTxBuffer,
766
    ) -> Result<(), Error> {
767
        let _clock = SpiClockGuard::new(self.spi.info());
768

            
769
        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
770
        unsafe {
771
            spi.start_dma_transfer(read_bytes, write_bytes, &mut rx_buffer, &mut tx_buffer)?;
772
        }
773
        spi.wait_for_idle_async().await;
774
        spi.defuse();
775
        Ok(())
776
    }
777

            
778
    async fn half_duplex_read_dma_async(
779
4
        &mut self,
780
4
        data_mode: DataMode,
781
4
        cmd: Command,
782
4
        address: Address,
783
4
        dummy: u8,
784
4
        bytes_to_read: usize,
785
4
        mut rx_buffer: impl DmaRxBuffer,
786
8
    ) -> Result<(), Error> {
787
4
        let _clock = SpiClockGuard::new(self.spi.info());
788

            
789
4
        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
790
        unsafe {
791
            spi.start_half_duplex_read(
792
                data_mode,
793
                cmd,
794
                address,
795
                dummy,
796
                bytes_to_read,
797
                &mut rx_buffer,
798
            )?;
799
        }
800
4
        spi.wait_for_idle_async().await;
801
        spi.defuse();
802
        Ok(())
803
4
    }
804

            
805
    async fn half_duplex_write_dma_async(
806
        &mut self,
807
        data_mode: DataMode,
808
        cmd: Command,
809
        address: Address,
810
        dummy: u8,
811
        bytes_to_write: usize,
812
        mut tx_buffer: impl DmaTxBuffer,
813
    ) -> Result<(), Error> {
814
        let _clock = SpiClockGuard::new(self.spi.info());
815

            
816
        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
817
        unsafe {
818
            spi.start_half_duplex_write(
819
                data_mode,
820
                cmd,
821
                address,
822
                dummy,
823
                bytes_to_write,
824
                &mut tx_buffer,
825
            )?;
826
        }
827
        spi.wait_for_idle_async().await;
828
        spi.defuse();
829
        Ok(())
830
    }
831
}
832

            
833
// +1 to make sure we have enough descriptors to satisfy strict alignment requirements
834
const LINK_DESCRIPTOR_COUNT: usize = MAX_DMA_SIZE.div_ceil(CHUNK_SIZE) + 2 + 1;
835

            
836
enum MaybeCopyTxBuf<'a> {
837
    Copy(&'a mut ScopedDmaTxBuf<'static>),
838
    Direct(&'a mut [DmaDescriptor; LINK_DESCRIPTOR_COUNT]),
839
}
840

            
841
impl<'a> MaybeCopyTxBuf<'a> {
842
23
    unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
843
23
        match self {
844
            MaybeCopyTxBuf::Copy(tx_buffer) => {
845
                tx_buffer.as_mut_slice()[..data.len()].copy_from_slice(unsafe { data.as_ref() });
846
2
                NoBuffer(tx_buffer.prepare())
847
            }
848
            MaybeCopyTxBuf::Direct(descriptors) => {
849
21
                let (buffer, _) = unsafe { unwrap!(prepare_for_tx(&mut **descriptors, data, 1)) };
850
                buffer
851
            }
852
        }
853
22
    }
854

            
855
    fn chunk_size(&self) -> usize {
856
18
        match self {
857
            MaybeCopyTxBuf::Copy(buffer) => buffer.capacity().min(MAX_DMA_SIZE),
858
            MaybeCopyTxBuf::Direct(_) => MAX_DMA_SIZE,
859
        }
860
    }
861
}
862

            
863
#[allow(clippy::large_enum_variant)]
864
enum MaybeCopyRxBuf<'a> {
865
    Copy(&'a mut ScopedDmaRxBuf<'static>),
866
    Direct {
867
        descriptors: &'a mut [DmaDescriptor; LINK_DESCRIPTOR_COUNT],
868
        #[cfg(dma_can_access_psram)]
869
        align_buffer: [Option<ManualWritebackBuffer>; 2],
870
    },
871
}
872

            
873
impl<'a> MaybeCopyRxBuf<'a> {
874
27
    unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
875
27
        match self {
876
            MaybeCopyRxBuf::Copy(rx_buffer) => NoBuffer(rx_buffer.prepare()),
877
            MaybeCopyRxBuf::Direct {
878
                descriptors,
879
                #[cfg(dma_can_access_psram)]
880
                align_buffer,
881
            } => {
882
27
                let (buffer, _) = unsafe {
883
27
                    prepare_for_rx(
884
27
                        &mut **descriptors,
885
                        #[cfg(dma_can_access_psram)]
886
                        align_buffer,
887
                        data,
888
                    )
889
                };
890
                buffer
891
            }
892
        }
893
27
    }
894

            
895
    fn chunk_size(&self) -> usize {
896
18
        match self {
897
            MaybeCopyRxBuf::Copy(buffer) => buffer.capacity().min(MAX_DMA_SIZE),
898
            MaybeCopyRxBuf::Direct { .. } => MAX_DMA_SIZE,
899
        }
900
    }
901

            
902
27
    fn finish(&mut self, chunk: &mut [u8]) {
903
27
        match self {
904
            MaybeCopyRxBuf::Copy(buffer) => {
905
                chunk.copy_from_slice(&buffer.as_slice()[..chunk.len()]);
906
            }
907
            MaybeCopyRxBuf::Direct {
908
                #[cfg(dma_can_access_psram)]
909
                align_buffer,
910
                ..
911
            } => {
912
                #[cfg(soc_internal_memory_cached)]
913
                unsafe {
914
                    crate::soc::cache_invalidate_addr(chunk.as_ptr() as u32, chunk.len() as u32);
915
                }
916

            
917
                #[cfg(dma_can_access_psram)]
918
                for buffer in align_buffer.iter_mut() {
919
                    if let Some(buffer) = buffer.as_mut() {
920
                        buffer.write_back();
921
                    }
922
                    *buffer = None;
923
                }
924
            }
925
        }
926
27
    }
927
}
928

            
929
#[derive(Clone, Copy)]
930
enum DmaOperationKind {
931
    /// The entire slice must be copied into the internal buffer first.
932
    Copied,
933

            
934
    /// The slice can be transferred directly, with minimal copying done for alignment.
935
    InPlace,
936
}
937

            
938
impl DmaOperationKind {
939
    fn compute(buffer: &[u8], direction: TransferDirection) -> Self {
940
        fn is_dma_compatible(buffer: &[u8], _direction: TransferDirection) -> bool {
941
            // FIXME: lazy workaround for ESP32 TX DMA alignment requirements.
942
            // `prepare_for_tx` and `prepare_for_rx` should be updated to handle ESP32.
943
            #[cfg(spi_master_version = "1")]
944
            if !((buffer.as_ptr() as usize).is_multiple_of(4) && buffer.len().is_multiple_of(4)) {
945
                return false;
946
            }
947

            
948
            if is_slice_in_dram(buffer) {
949
                return true;
950
            }
951
            #[cfg(dma_can_access_psram)]
952
            if is_slice_in_psram(buffer) {
953
                #[cfg(spi_master_version = "2")]
954
                if _direction == TransferDirection::In {
955
                    // For some reason, having tail bytes in internal RAM causes issues, so we
956
                    // force copying if the end of the PSRAM buffer is not aligned.
957
                    let tail_bytes = (buffer.as_ptr() as usize + buffer.len()).wrapping_neg() & 15;
958
                    if tail_bytes > 0 {
959
                        return false;
960
                    }
961
                }
962

            
963
                return true;
964
            }
965

            
966
            // TODO: C5+ DMA can read from flash
967

            
968
            false
969
        }
970

            
971
        if is_dma_compatible(buffer, direction) {
972
            Self::InPlace
973
        } else {
974
            Self::Copied
975
        }
976
    }
977

            
978
    fn for_read(buffer: &mut [u8]) -> Self {
979
        Self::compute(buffer, TransferDirection::In)
980
27
    }
981

            
982
    fn for_write(buffer: &[u8]) -> Self {
983
        Self::compute(buffer, TransferDirection::Out)
984
23
    }
985
}
986

            
987
impl<'d, Dm> SpiDma<'d, Dm>
988
where
989
    Dm: DriverMode,
990
{
991
    fn use_blocking_transfer(&self, transfer_size: usize) -> bool {
992
        let threshold = self
993
            .spi
994
            .state()
995
            .min_async_transfer_size
996
            .load(Ordering::Relaxed);
997
        threshold > 0 && transfer_size < threshold
998
    }
999

            
    fn spi(&self) -> &SpiWrapper<'_> {
        &self.spi
    }
    fn driver(&self) -> Driver {
3374870
        Driver {
            info: self.spi.info(),
            state: self.spi.state(),
        }
    }
    fn dma_driver(&self) -> DmaDriver {
41
        DmaDriver {
            driver: self.driver(),
            state: self.spi().dma_state(),
            dma_peripheral: self.spi.spi.dma_peripheral(),
        }
    }
    fn is_done(&self) -> bool {
3374870
        if self.driver().busy() {
            return false;
        }
39
        if self.dma_driver().state.rx_transfer_in_progress.get() {
            // If this is an asymmetric transfer and the RX side is smaller, the RX channel
            // will never be "done" as it won't have enough descriptors/buffer to receive
            // the EOF bit from the SPI. So instead the RX channel will hit
            // a "descriptor empty" which means the DMA is written as much
            // of the received data as possible into the buffer and
            // discarded the rest. The user doesn't care about this discarded data.
34
            if !self.channel.rx.is_done() && !self.channel.rx.has_dscr_empty_error() {
                return false;
            }
        }
        true
    }
    fn wait_for_idle(&mut self) {
        while !self.is_done() {
            // Wait for the SPI to become idle
        }
        self.dma_driver().state.rx_transfer_in_progress.set(false);
        self.dma_driver().state.tx_transfer_in_progress.set(false);
        fence(Ordering::Acquire);
    }
    /// # Safety
    ///
    /// The caller must ensure to not access the buffer contents while the
    /// transfer is in progress. Moving the buffer itself is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
16
    unsafe fn start_transfer_dma<RX: DmaRxBuffer, TX: DmaTxBuffer>(
        &mut self,
        full_duplex: bool,
        bytes_to_read: usize,
        bytes_to_write: usize,
        rx_buffer: &mut RX,
        tx_buffer: &mut TX,
    ) -> Result<(), Error> {
41
        if bytes_to_read > MAX_DMA_SIZE || bytes_to_write > MAX_DMA_SIZE {
            return Err(Error::MaxDmaTransferSizeExceeded);
        }
        self.dma_driver()
            .state
            .rx_transfer_in_progress
41
            .set(bytes_to_read > 0);
        self.dma_driver()
            .state
            .tx_transfer_in_progress
31
            .set(bytes_to_write > 0);
        unsafe {
            self.dma_driver().start_transfer_dma(
                full_duplex,
                bytes_to_read,
                bytes_to_write,
                rx_buffer,
                tx_buffer,
                &mut self.channel,
            )
        }
16
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg(all(spi_master_version = "1", spi_address_workaround))]
    unsafe fn set_up_address_workaround(
        &mut self,
        cmd: Command,
        address: Address,
        dummy: u8,
    ) -> Result<(), Error> {
        if dummy > 0 {
            // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
            error!("Dummy bits are not supported when there is no data to write");
            return Err(Error::Unsupported);
        }
        let buffer = unsafe { self.dma_driver().tx_buffer() };
        let bytes_to_write = address.width().div_ceil(8);
        // The address register is read in big-endian order,
        // we have to prepare the emulated write in the same way.
        let addr_bytes = address.value().to_be_bytes();
        let addr_bytes = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
        buffer.fill(addr_bytes);
        self.driver().setup_half_duplex(
            true,
            cmd,
            Address::None,
            false,
            dummy,
            bytes_to_write == 0,
            address.mode(),
        )?;
        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
    }
    fn cancel_transfer(&mut self) {
        let state = self.dma_driver().state;
2
        if state.tx_transfer_in_progress.get() || state.rx_transfer_in_progress.get() {
2
            self.dma_driver().abort_transfer();
            // We need to stop the DMA transfer, too.
2
            if state.tx_transfer_in_progress.get() {
                self.channel.tx.stop_transfer();
                state.tx_transfer_in_progress.set(false);
            }
2
            if state.rx_transfer_in_progress.get() {
                self.channel.rx.stop_transfer();
                state.rx_transfer_in_progress.set(false);
            }
        }
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_dma_write(
        &mut self,
        bytes_to_write: usize,
        buffer: &mut impl DmaTxBuffer,
    ) -> Result<(), Error> {
        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
        unsafe { self.start_dma_transfer(0, bytes_to_write, rx_buffer, buffer) }
    }
    /// Assigns copy buffers to the SPI driver.
    ///
    /// These buffers will be used to copy data when using the slice-based transfer functions.
    ///
    /// Data is copied in two cases:
    ///   - When the buffer is not located in a memory region that can be accessed by the DMA.
    #[cfg_attr(
        not(spi_master_dma_can_access_flash),
        doc = "The DMA cannot read flash memory."
    )]
    ///   - When the alignment of the buffer does not meet the DMA's requirements, the unaligned
    ///     parts of the buffer are copied.
    #[cfg_attr(
        spi_master_version = "1",
        doc = "On ESP32, transferring from internal SRAM requires copying the entire buffer if it is
not 4-byte aligned. This is a limitation of the current implementation."
    )]
    #[cfg_attr(
        spi_master_version = "2",
        doc = "On ESP32-S2, receiving into PSRAM requires the buffer's _end_ to be 16-byte
aligned, otherwise the driver requires copying the entire buffer."
    )]
    #[doc = ""]
    /// The maximum useful size for these buffers is 32736 bytes, any additional memory will
    /// be wasted.
    ///
    /// For an example of how to create these buffers, see the [`SpiDma`] documentation.
    #[instability::unstable]
    pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDma<'d, Dm> {
        unsafe {
            (&mut *self.spi.dma_state().rx_buffer.get()).write(dma_rx_buf.into_scoped());
            (&mut *self.spi.dma_state().tx_buffer.get()).write(dma_tx_buf.into_scoped());
        }
2
        self
    }
    /// Performs a DMA write.
    ///
    /// Returns a [`SpiDmaTransfer`] that owns the buffer and the
    /// SPI instance. The maximum amount of data to be sent is 32736
    /// bytes.
    #[allow(clippy::type_complexity)]
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    #[instability::unstable]
2
    pub fn write_buffer<TX: DmaTxBuffer>(
        mut self,
        bytes_to_write: usize,
        mut buffer: TX,
    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
2
        let clock = SpiClockGuard::new(self.spi.info());
2
        if let Err(e) = self.driver().setup_full_duplex() {
            return Err((e, self, buffer));
        };
2
        match unsafe { self.start_dma_write(bytes_to_write, &mut buffer) } {
2
            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
            Err(e) => Err((e, self, buffer)),
        }
2
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_dma_read(
        &mut self,
        bytes_to_read: usize,
        buffer: &mut impl DmaRxBuffer,
    ) -> Result<(), Error> {
        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
        unsafe { self.start_dma_transfer(bytes_to_read, 0, buffer, tx_buffer) }
    }
    /// Performs a DMA read.
    ///
    /// Returns a [`SpiDmaTransfer`] that owns the buffer and
    /// the SPI instance. The maximum amount of data to be
    /// received is 32736 bytes.
    #[allow(clippy::type_complexity)]
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    #[instability::unstable]
    pub fn read_buffer<RX: DmaRxBuffer>(
        mut self,
        bytes_to_read: usize,
        mut buffer: RX,
    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
1
        let clock = SpiClockGuard::new(self.spi.info());
1
        if let Err(e) = self.driver().setup_full_duplex() {
            return Err((e, self, buffer));
        };
1
        match unsafe { self.start_dma_read(bytes_to_read, &mut buffer) } {
1
            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
            Err(e) => Err((e, self, buffer)),
        }
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_dma_transfer(
        &mut self,
        bytes_to_read: usize,
        bytes_to_write: usize,
        rx_buffer: &mut impl DmaRxBuffer,
        tx_buffer: &mut impl DmaTxBuffer,
    ) -> Result<(), Error> {
        unsafe {
2
            self.start_transfer_dma(true, bytes_to_read, bytes_to_write, rx_buffer, tx_buffer)
        }
    }
    /// Performs a DMA transfer.
    ///
    /// Returns a [`SpiDmaTransfer`] that owns the buffers and
    /// the SPI instance. The maximum amount of data to be
    /// sent/received is 32736 bytes.
    #[allow(clippy::type_complexity)]
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    #[instability::unstable]
8
    pub fn transfer_buffers<RX: DmaRxBuffer, TX: DmaTxBuffer>(
        mut self,
        bytes_to_read: usize,
        mut rx_buffer: RX,
        bytes_to_write: usize,
        mut tx_buffer: TX,
    ) -> Result<SpiDmaTransfer<'d, Dm, (RX, TX)>, (Error, Self, RX, TX)> {
8
        let clock = SpiClockGuard::new(self.spi.info());
8
        if let Err(e) = self.driver().setup_full_duplex() {
            return Err((e, self, rx_buffer, tx_buffer));
        };
        match unsafe {
8
            self.start_dma_transfer(
                bytes_to_read,
                bytes_to_write,
                &mut rx_buffer,
                &mut tx_buffer,
            )
        } {
8
            Ok(_) => Ok(SpiDmaTransfer::new(self, (rx_buffer, tx_buffer), clock)),
            Err(e) => Err((e, self, rx_buffer, tx_buffer)),
        }
8
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_half_duplex_read(
        &mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        bytes_to_read: usize,
        buffer: &mut impl DmaRxBuffer,
    ) -> Result<(), Error> {
26
        self.driver().setup_half_duplex(
            false,
            cmd,
            address,
            false,
            dummy,
26
            bytes_to_read == 0,
            data_mode,
        )?;
        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
12
        unsafe { self.start_transfer_dma(false, bytes_to_read, 0, buffer, tx_buffer) }
    }
    /// Performs a half-duplex read operation using DMA.
    #[allow(clippy::type_complexity)]
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    #[instability::unstable]
14
    pub fn half_duplex_read_buffer<RX: DmaRxBuffer>(
        mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        bytes_to_read: usize,
        mut buffer: RX,
    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
26
        let clock = SpiClockGuard::new(self.spi.info());
12
        match unsafe {
            self.start_half_duplex_read(data_mode, cmd, address, dummy, bytes_to_read, &mut buffer)
        } {
26
            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
            Err(e) => Err((e, self, buffer)),
        }
14
    }
    /// # Safety
    ///
    /// The caller must ensure that the buffers are not accessed while the
    /// transfer is in progress. Moving the buffers is allowed.
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_half_duplex_write(
        &mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        bytes_to_write: usize,
        buffer: &mut impl DmaTxBuffer,
    ) -> Result<(), Error> {
        #[cfg(all(spi_master_version = "1", spi_address_workaround))]
        {
            // On the ESP32, if we don't have data, the address is always sent
            // on a single line, regardless of its data mode.
            if bytes_to_write == 0 && address.mode() != DataMode::SingleTwoDataLines {
                return unsafe { self.set_up_address_workaround(cmd, address, dummy) };
            }
        }
8
        self.driver().setup_half_duplex(
            true,
            cmd,
            address,
            false,
            dummy,
8
            bytes_to_write == 0,
            data_mode,
        )?;
        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
4
        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
    }
    /// Performs a half-duplex write operation using DMA.
    #[allow(clippy::type_complexity)]
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    #[instability::unstable]
    pub fn half_duplex_write_buffer<TX: DmaTxBuffer>(
        mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        bytes_to_write: usize,
        mut buffer: TX,
    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
8
        let clock = SpiClockGuard::new(self.spi.info());
4
        match unsafe {
            self.start_half_duplex_write(
                data_mode,
                cmd,
                address,
                dummy,
                bytes_to_write,
                &mut buffer,
            )
        } {
8
            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
            Err(e) => Err((e, self, buffer)),
        }
    }
    #[doc_replace(
        "max_frequency" => {
            cfg(esp32h2) => "48MHz",
            _ => "80MHz",
        }
    )]
    /// Changes the bus configuration.
    ///
    /// # Errors
    ///
    /// [`ConfigError::FrequencyOutOfRange`] when frequency passed in config exceeds
    /// __max_frequency__ or is below 70 kHz.
    #[instability::unstable]
    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
        self.driver().apply_config(config)
    }
28
    fn transfer_buffers_dma(
        &mut self,
        read_bytes: usize,
        write_bytes: usize,
        mut rx_buffer: impl DmaRxBuffer,
        mut tx_buffer: impl DmaTxBuffer,
    ) -> Result<(), Error> {
        unsafe {
            self.start_dma_transfer(read_bytes, write_bytes, &mut rx_buffer, &mut tx_buffer)?;
        }
        self.wait_for_idle();
        Ok(())
28
    }
    /// Reads data from the SPI bus using DMA.
    #[instability::unstable]
    pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
5
        let _clock = SpiClockGuard::new(self.spi.info());
5
        self.driver().setup_full_duplex()?;
5
        if self.use_blocking_transfer(words.len()) {
            self.dma_driver().disable_dma();
            return self.driver().read(words);
        }
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
5
        let mut maybe_copy_buffer = match DmaOperationKind::for_read(words) {
            DmaOperationKind::Copied => {
                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
            }
5
            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
                descriptors: &mut descriptors,
                #[cfg(dma_can_access_psram)]
                align_buffer: [const { None }; 2],
            },
        };
        if maybe_copy_buffer.chunk_size() == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
        for chunk in words.chunks_mut(maybe_copy_buffer.chunk_size()) {
            let read_bytes = chunk.len();
5
            let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *chunk)) };
5
            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
5
            self.transfer_buffers_dma(read_bytes, 0, rx_buffer, tx_buffer)?;
5
            maybe_copy_buffer.finish(chunk);
        }
        Ok(())
5
    }
    /// Writes data to the SPI bus using DMA.
    #[instability::unstable]
5
    pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
5
        let _clock = SpiClockGuard::new(self.spi.info());
5
        self.driver().setup_full_duplex()?;
5
        if self.use_blocking_transfer(words.len()) {
            self.dma_driver().disable_dma();
            return self.driver().write(words);
        }
        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
5
        let mut maybe_copy_buffer = match DmaOperationKind::for_write(words) {
            DmaOperationKind::Copied => {
                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
            }
5
            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
        };
        if maybe_copy_buffer.chunk_size() == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
        for chunk in words.chunks(maybe_copy_buffer.chunk_size()) {
            let write_bytes = chunk.len();
5
            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
5
            let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(chunk)) };
5
            self.transfer_buffers_dma(0, write_bytes, rx_buffer, tx_buffer)?;
        }
        Ok(())
5
    }
    /// Transfers data to and from the SPI bus simultaneously using DMA.
    #[instability::unstable]
20
    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
20
        let _clock = SpiClockGuard::new(self.spi.info());
20
        self.driver().setup_full_duplex()?;
20
        if self.use_blocking_transfer(read.len().max(write.len())) {
2
            self.dma_driver().disable_dma();
            if read.is_empty() {
                return self.driver().write(write);
            } else if write.is_empty() {
                return self.driver().read(read);
            } else {
2
                return self.driver().transfer(read, write);
            }
        }
        let common_length = min(read.len(), write.len());
        let (read_common, read_remainder) = read.split_at_mut(common_length);
        let (write_common, write_remainder) = write.split_at(common_length);
        // DmaOperationKind must be determined on the sub-slices actually passed to DMA.
        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
18
        let mut maybe_copy_rx_buffer = match DmaOperationKind::for_read(read_common) {
            DmaOperationKind::Copied => {
                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
            }
            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
                descriptors: &mut rx_descriptors,
                #[cfg(dma_can_access_psram)]
                align_buffer: [const { None }; 2],
            },
        };
        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
18
        let mut maybe_copy_tx_buffer = match DmaOperationKind::for_write(write_common) {
            DmaOperationKind::Copied => {
                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
            }
            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut tx_descriptors),
        };
        let chunk_size = min(
            maybe_copy_rx_buffer.chunk_size(),
            maybe_copy_tx_buffer.chunk_size(),
        );
17
        if chunk_size == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
        for (read_chunk, write_chunk) in read_common
            .chunks_mut(chunk_size)
            .zip(write_common.chunks(chunk_size))
        {
            let read_bytes = read_chunk.len();
            let write_bytes = write_chunk.len();
18
            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(NonNull::from(write_chunk)) };
18
            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(NonNull::from(&mut *read_chunk)) };
18
            self.transfer_buffers_dma(read_bytes, write_bytes, rx_buffer, tx_buffer)?;
18
            maybe_copy_rx_buffer.finish(read_chunk);
        }
18
        if !read_remainder.is_empty() {
            self.read(read_remainder)
13
        } else if !write_remainder.is_empty() {
5
            self.write(write_remainder)
        } else {
            Ok(())
        }
20
    }
    /// Transfers data in place on the SPI bus using DMA.
    #[instability::unstable]
    pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
        let _clock = SpiClockGuard::new(self.spi.info());
        self.driver().setup_full_duplex()?;
        if self.use_blocking_transfer(words.len()) {
            self.dma_driver().disable_dma();
            return self.driver().transfer_in_place(words);
        }
        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
        let (mut maybe_copy_rx_buffer, mut maybe_copy_tx_buffer) =
            match DmaOperationKind::for_write(words) {
                DmaOperationKind::Copied => (
                    MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() }),
                    MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() }),
                ),
                DmaOperationKind::InPlace => (
                    MaybeCopyRxBuf::Direct {
                        descriptors: &mut rx_descriptors,
                        #[cfg(dma_can_access_psram)]
                        align_buffer: [const { None }; 2],
                    },
                    MaybeCopyTxBuf::Direct(&mut tx_descriptors),
                ),
            };
        let chunk_size = min(
            maybe_copy_rx_buffer.chunk_size(),
            maybe_copy_tx_buffer.chunk_size(),
        );
        if chunk_size == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
        for chunk in words.chunks_mut(chunk_size) {
            let bytes = chunk.len();
            let ptr = NonNull::from(&mut *chunk);
            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(ptr) };
            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(ptr) };
            self.transfer_buffers_dma(bytes, bytes, rx_buffer, tx_buffer)?;
            maybe_copy_rx_buffer.finish(chunk);
        }
        Ok(())
    }
    /// Half-duplex read.
    #[instability::unstable]
20
    pub fn half_duplex_read(
        &mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        buffer: &mut [u8],
    ) -> Result<(), Error> {
20
        let _clock = SpiClockGuard::new(self.spi.info());
        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
20
        if rx_buffer.capacity() == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
20
        if buffer.len() > rx_buffer.capacity() {
            return Err(Error::from(DmaError::Overflow));
        }
        unsafe {
            self.start_half_duplex_read(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)?;
        }
        self.wait_for_idle();
20
        buffer.copy_from_slice(&rx_buffer.as_slice()[..buffer.len()]);
        Ok(())
20
    }
    /// Half-duplex write.
    #[instability::unstable]
    pub fn half_duplex_write(
        &mut self,
        data_mode: DataMode,
        cmd: Command,
        address: Address,
        dummy: u8,
        buffer: &[u8],
    ) -> Result<(), Error> {
        let _clock = SpiClockGuard::new(self.spi.info());
        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
        if tx_buffer.capacity() == 0 {
            return Err(Error::from(DmaError::BufferTooSmall));
        }
        if buffer.len() > tx_buffer.capacity() {
            return Err(Error::from(DmaError::Overflow));
        }
        tx_buffer.as_mut_slice()[..buffer.len()].copy_from_slice(buffer);
        unsafe {
            self.start_half_duplex_write(data_mode, cmd, address, dummy, buffer.len(), tx_buffer)?;
        }
        self.wait_for_idle();
        Ok(())
    }
}
/// A structure representing a DMA transfer for SPI.
///
/// Holds references to the SPI instance, DMA buffers, and transfer status.
#[instability::unstable]
pub struct SpiDmaTransfer<'d, Dm, Buf>
where
    Dm: DriverMode,
{
    spi_dma: ManuallyDrop<SpiDma<'d, Dm>>,
    dma_buf: ManuallyDrop<Buf>,
    clock: ManuallyDrop<SpiClockGuard>,
}
impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
    /// Waits for the DMA transfer to complete asynchronously.
    ///
    /// Awaits the completion of both RX and TX operations.
    #[instability::unstable]
1
    pub async fn wait_for_done(&mut self) {
1
        self.spi_dma.wait_for_idle_async().await;
    }
}
impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
where
    Dm: DriverMode,
{
    fn new(spi_dma: SpiDma<'d, Dm>, dma_buf: Buf, clock: SpiClockGuard) -> Self {
12
        Self {
            spi_dma: ManuallyDrop::new(spi_dma),
            dma_buf: ManuallyDrop::new(dma_buf),
            clock: ManuallyDrop::new(clock),
        }
    }
    /// Returns whether the transfer is complete.
    ///
    /// Both RX and TX operations are done, and the SPI instance is no longer
    /// busy.
    #[instability::unstable]
    pub fn is_done(&self) -> bool {
        self.spi_dma.is_done()
    }
    /// Waits for the DMA transfer to complete.
    ///
    /// Blocks until the transfer is finished and returns the
    /// `SpiDma` instance and the associated buffer.
    #[instability::unstable]
    pub fn wait(mut self) -> (SpiDma<'d, Dm>, Buf) {
        self.spi_dma.wait_for_idle();
        let retval = unsafe {
1
            (
                ManuallyDrop::take(&mut self.spi_dma),
                ManuallyDrop::take(&mut self.dma_buf),
            )
        };
        let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
        core::mem::forget(self);
        retval
    }
    /// Cancels the DMA transfer.
    #[instability::unstable]
4
    pub fn cancel(&mut self) {
        if !self.spi_dma.is_done() {
            self.spi_dma.cancel_transfer();
        }
4
    }
}
impl<Dm, Buf> Drop for SpiDmaTransfer<'_, Dm, Buf>
where
    Dm: DriverMode,
{
    fn drop(&mut self) {
        if !self.is_done() {
            self.spi_dma.cancel_transfer();
            self.spi_dma.wait_for_idle();
        }
        unsafe {
            ManuallyDrop::drop(&mut self.spi_dma);
            ManuallyDrop::drop(&mut self.dma_buf);
        }
        let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
    }
}
pub(super) struct DmaDriver {
    driver: Driver,
    dma_peripheral: crate::dma::DmaPeripheral,
    state: &'static DmaState,
}
impl DmaDriver {
    unsafe fn rx_buffer(&self) -> &'static mut ScopedDmaRxBuf<'static> {
        unsafe { self.state.rx_buffer() }
    }
    unsafe fn tx_buffer(&self) -> &'static mut ScopedDmaTxBuf<'static> {
        unsafe { self.state.tx_buffer() }
    }
2
    fn abort_transfer(&self) {
        // The SPI peripheral is controlling how much data we transfer, so let's
        // update its counter.
        // 0 doesn't take effect on ESP32 and cuts the currently transmitted byte
        // immediately.
        // 1 seems to stop after transmitting the current byte which is somewhat less
        // impolite.
        self.driver.configure_datalen(1, 1);
        self.driver.update();
2
    }
    fn disable_dma(&self) {
        #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
        self.regs().dma_conf().modify(|_, w| {
            w.dma_tx_ena().clear_bit();
            w.dma_rx_ena().clear_bit()
        });
        // PDMA has no enable bit: whether the peripheral takes its data from the
        // descriptors or from the FIFO is decided by whether a link is armed, so
        // a stale link keeps routing a CPU-driven transfer into DMA. This mirrors
        // the clearing `start_transfer_dma` already does on the way *in*.
        #[cfg(any(spi_master_version = "1", spi_master_version = "2"))]
        {
            self.regs().dma_out_link().write(|w| unsafe { w.bits(0) });
            self.regs().dma_in_link().write(|w| unsafe { w.bits(0) });
        }
4
    }
    fn regs(&self) -> &RegisterBlock {
        self.driver.regs()
    }
    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
    unsafe fn start_transfer_dma<Dm: DriverMode>(
        &self,
        _full_duplex: bool,
        rx_len: usize,
        tx_len: usize,
        rx_buffer: &mut impl DmaRxBuffer,
        tx_buffer: &mut impl DmaTxBuffer,
        channel: &mut Channel<Dm, SpiMasterErased<'_>>,
    ) -> Result<(), Error> {
        #[cfg(spi_master_version = "2")]
        {
            // without this a transfer after a write will fail
            self.regs().dma_out_link().write(|w| unsafe { w.bits(0) });
            self.regs().dma_in_link().write(|w| unsafe { w.bits(0) });
        }
41
        self.driver.configure_datalen(rx_len, tx_len);
        // enable the MISO and MOSI if needed
        self.regs()
            .user()
            .modify(|_, w| w.usr_miso().bit(rx_len > 0).usr_mosi().bit(tx_len > 0));
41
        self.enable_dma();
41
        if rx_len > 0 {
            unsafe {
                channel
                    .rx
                    .prepare_transfer(self.dma_peripheral, rx_buffer)
                    .and_then(|_| channel.rx.start_transfer())?;
            }
        } else {
            #[cfg(spi_master_version = "1")]
            {
                // see https://github.com/espressif/esp-idf/commit/366e4397e9dae9d93fe69ea9d389b5743295886f
                // see https://github.com/espressif/esp-idf/commit/0c3653b1fd7151001143451d4aa95dbf15ee8506
                if _full_duplex {
                    self.regs()
                        .dma_in_link()
                        .modify(|_, w| unsafe { w.inlink_addr().bits(0) });
                    self.regs()
                        .dma_in_link()
                        .modify(|_, w| w.inlink_start().set_bit());
                }
            }
        }
31
        if tx_len > 0 {
            unsafe {
23
                channel
                    .tx
                    .prepare_transfer(self.dma_peripheral, tx_buffer)
                    .and_then(|_| channel.tx.start_transfer())?;
            }
        }
        #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
41
        self.reset_dma();
41
        self.driver.start_operation();
        Ok(())
6
    }
    fn enable_dma(&self) {
        cfg_select! {
            any(spi_master_version = "1", spi_master_version = "2") => {
                self.reset_dma();
            }
            _ => {
                self.regs().dma_conf().modify(|_, w| {
                    w.dma_tx_ena().set_bit();
                    w.dma_rx_ena().set_bit()
                });
            }
        }
101
    }
    fn reset_dma(&self) {
        self.regs().dma_conf().toggle(|w, bit| {
            cfg_select! {
                any(spi_master_version = "1", spi_master_version = "2") => {
                    w.out_rst().bit(bit);
                    w.in_rst().bit(bit);
                    w.ahbm_fifo_rst().bit(bit);
                    w.ahbm_rst().bit(bit)
                }
                _ => {
                    w.rx_afifo_rst().bit(bit);
                    w.buf_afifo_rst().bit(bit);
                    w.dma_afifo_rst().bit(bit)
                }
            }
        });
        self.clear_dma_interrupts();
101
    }
    fn clear_dma_interrupts(&self) {
        self.regs().dma_int_clr().write(|w| {
            cfg_select! {
                any(spi_master_version = "1", spi_master_version = "2") => {
                    w.inlink_dscr_empty().clear_bit_by_one();
                    w.outlink_dscr_error().clear_bit_by_one();
                    w.inlink_dscr_error().clear_bit_by_one();
                    w.in_done().clear_bit_by_one();
                    w.in_err_eof().clear_bit_by_one();
                    w.in_suc_eof().clear_bit_by_one();
                    w.out_done().clear_bit_by_one();
                    w.out_eof().clear_bit_by_one();
                    w.out_total_eof().clear_bit_by_one()
                }
                _ => {
                    w.dma_infifo_full_err().clear_bit_by_one();
                    w.dma_outfifo_empty_err().clear_bit_by_one();
                    w.trans_done().clear_bit_by_one();
                    w.mst_rx_afifo_wfull_err().clear_bit_by_one();
                    w.mst_tx_afifo_rempty_err().clear_bit_by_one()
                }
            }
        });
    }
}
struct DmaState {
    tx_transfer_in_progress: Cell<bool>,
    rx_transfer_in_progress: Cell<bool>,
    rx_buffer: UnsafeCell<MaybeUninit<ScopedDmaRxBuf<'static>>>,
    tx_buffer: UnsafeCell<MaybeUninit<ScopedDmaTxBuf<'static>>>,
    descriptors: UnsafeCell<InternalMemory<[DmaDescriptor; 2]>>,
    #[cfg(all(spi_master_version = "1", spi_address_workaround))]
    default_tx_buffer: UnsafeCell<InternalMemory<[u8; 4]>>,
}
impl DmaState {
    // Syntactic helper to get a mutable reference to the "empty" RX DMA buffer.
    //
    // # Safety
    //
    // The caller must ensure that Rust's aliasing rules are upheld.
    #[allow(
        clippy::mut_from_ref,
        reason = "Safety requirements ensure this is okay"
    )]
    unsafe fn rx_buffer(&self) -> &mut ScopedDmaRxBuf<'static> {
        unsafe { (&mut *self.rx_buffer.get()).assume_init_mut() }
    }
    // Syntactic helper to get a mutable reference to the "empty" TX DMA buffer.
    //
    // # Safety
    //
    // The caller must ensure that Rust's aliasing rules are upheld.
    #[allow(
        clippy::mut_from_ref,
        reason = "Safety requirements ensure this is okay"
    )]
    unsafe fn tx_buffer(&self) -> &mut ScopedDmaTxBuf<'static> {
        unsafe { (&mut *self.tx_buffer.get()).assume_init_mut() }
    }
}
// SAFETY: State belongs to the currently constructed driver instance. As such, it'll not be
// accessed concurrently in multiple threads.
unsafe impl Sync for DmaState {}
for_each_spi_master!(
    (all $( ($peri:ident, $sys:ident, $sclk:ident $_cs:tt $_sio:tt $(, $is_qspi:tt)?)),* ) => {
        impl AnySpi<'_> {
            #[inline(always)]
            fn dma_state(&self) -> &'static DmaState {
                match &self.0 {
                    $(
                        super::any::Inner::$sys(_spi) => {
                            static DMA_STATE: DmaState = DmaState {
                                tx_transfer_in_progress: Cell::new(false),
                                rx_transfer_in_progress: Cell::new(false),
                                rx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
                                tx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
                                descriptors: UnsafeCell::new(InternalMemory::new([DmaDescriptor::EMPTY; 2])),
                                #[cfg(all(spi_master_version = "1", spi_address_workaround))]
                                default_tx_buffer: UnsafeCell::new(InternalMemory::new([0; 4])),
                            };
                            &DMA_STATE
                        }
                    )*
                }
            }
        }
    };
);
impl SpiWrapper<'_> {
    fn dma_state(&self) -> &'static DmaState {
        self.spi.dma_state()
    }
}
with_spi_master_dma_engine! {
    ($engine:tt, $any_channel:ident) => {
        /// DMA channel trait for SPI peripherals.
        ///
        /// Implemented for each channel type that can serve a particular SPI instance `S`.
        #[instability::unstable]
        #[diagnostic::on_unimplemented(
            message = "The DMA channel cannot be used with this SPI peripheral",
            label = "This DMA channel",
            note = "Use a channel that matches the SPI instance."
        )]
        pub trait SpiMasterDmaChannel<'d, S>: crate::private::Sealed + Into<crate::dma::$any_channel<'d>> {}
        crate::macros::impl_dma_channel_trait! {
            $engine,
            any_peri = AnySpi<'d>,
            peris = for_each_spi_master,
            ($peri:path, $ch:path) => {
                impl<'d> SpiMasterDmaChannel<'d, $peri> for $ch {}
            }
        }
        // Proxy type so that the type-erased DMA channel can be named in the driver, regardless of the DMA engine.
        type SpiMasterErased<'d> = crate::dma::$any_channel<'d>;
    };
}