1use 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")]
12use embedded_hal::spi::{ErrorType, SpiBus};
13use enumset::EnumSet;
14#[cfg(place_spi_master_driver_in_ram)]
15use procmacros::ram;
16
17use super::*;
18use 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)]
44use crate::{dma::ManualWritebackBuffer, soc::is_slice_in_psram};
45
46const MAX_DMA_SIZE: usize = 32736;
47
48impl<'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 #[instability::unstable]
75 pub fn with_dma(
76 self,
77 channel: impl SpiMasterDmaChannel<'d, AnySpi<'d>>,
78 ) -> SpiDma<'d, crate::Blocking> {
79 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#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141pub struct SpiDma<'d, Dm>
142where
143 Dm: DriverMode,
144{
145 spi: SpiWrapper<'d>,
146 pub(crate) channel: Channel<Dm, SpiMasterErased<'d>>,
147}
148
149impl<Dm> crate::private::Sealed for SpiDma<'_, Dm> where Dm: DriverMode {}
150
151impl<Dm> core::fmt::Debug for SpiDma<'_, Dm>
152where
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]
161impl 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]
168impl<Dm> embassy_embedded_hal::SetConfig for SpiDma<'_, Dm>
169where
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]
181impl<Dm> ErrorType for SpiDma<'_, Dm>
182where
183 Dm: DriverMode,
184{
185 type Error = Error;
186}
187
188#[instability::unstable]
189impl<Dm> SpiBus for SpiDma<'_, Dm>
190where
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 Ok(())
214 }
215}
216
217#[instability::unstable]
218impl 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 Ok(())
240 }
241}
242
243impl<'d> SpiDma<'d, Blocking> {
244 #[instability::unstable]
246 pub fn into_async(self) -> SpiDma<'d, Async> {
247 self.spi
248 .set_interrupt_handler(self.spi.info().async_handler);
249 SpiDma {
250 spi: self.spi,
251 channel: self.channel.into_async(),
252 }
253 }
254
255 fn new_inner(spi: SpiWrapper<'d>, channel: SpiMasterErased<'d>) -> Self {
256 let channel = Channel::new(channel);
257 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 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 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 Self { spi, channel }
293 }
294
295 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 }
303
304 #[instability::unstable]
306 pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
307 self.driver().enable_listen(interrupts.into(), true);
308 }
309
310 #[instability::unstable]
312 pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
313 self.driver().enable_listen(interrupts.into(), false);
314 }
315
316 #[instability::unstable]
318 pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
319 self.driver().interrupts()
320 }
321
322 #[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 #[instability::unstable]
347 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
348 self.spi.set_interrupt_handler(handler);
349 }
350}
351
352impl<'d> SpiDma<'d, Async> {
353 #[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 async fn wait_for_idle_async(&mut self) {
364 if self.dma_driver().state.rx_transfer_in_progress.get() {
365 _ = 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 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 if !self.is_done() {
402 Fut(self.driver()).await;
403 }
404
405 if self.dma_driver().state.tx_transfer_in_progress.get() {
406 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 #[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 #[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 #[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 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 #[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 #[instability::unstable]
646 pub async fn half_duplex_read_async(
647 &mut self,
648 data_mode: DataMode,
649 cmd: Command,
650 address: Address,
651 dummy: u8,
652 buffer: &mut [u8],
653 ) -> Result<(), Error> {
654 let _clock = SpiClockGuard::new(self.spi.info());
655
656 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 if self.use_blocking_transfer(buffer.len()) {
665 self.dma_driver().disable_dma();
666 return self
667 .driver()
668 .half_duplex_read(data_mode, cmd, address, dummy, buffer);
669 }
670
671 let operation = DmaOperationKind::for_read(buffer);
672 let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
673 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 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 let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *buffer)) };
696 self.half_duplex_read_dma_async(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)
697 .await?;
698 maybe_copy_buffer.finish(buffer);
699
700 Ok(())
701 }
702
703 #[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 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 &mut self,
780 data_mode: DataMode,
781 cmd: Command,
782 address: Address,
783 dummy: u8,
784 bytes_to_read: usize,
785 mut rx_buffer: impl DmaRxBuffer,
786 ) -> Result<(), Error> {
787 let _clock = SpiClockGuard::new(self.spi.info());
788
789 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 spi.wait_for_idle_async().await;
801 spi.defuse();
802 Ok(())
803 }
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
833const LINK_DESCRIPTOR_COUNT: usize = MAX_DMA_SIZE.div_ceil(CHUNK_SIZE) + 2 + 1;
835
836enum MaybeCopyTxBuf<'a> {
837 Copy(&'a mut ScopedDmaTxBuf<'static>),
838 Direct(&'a mut [DmaDescriptor; LINK_DESCRIPTOR_COUNT]),
839}
840
841impl<'a> MaybeCopyTxBuf<'a> {
842 unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
843 match self {
844 MaybeCopyTxBuf::Copy(tx_buffer) => {
845 tx_buffer.as_mut_slice()[..data.len()].copy_from_slice(unsafe { data.as_ref() });
846 NoBuffer(tx_buffer.prepare())
847 }
848 MaybeCopyTxBuf::Direct(descriptors) => {
849 let (buffer, _) = unsafe { unwrap!(prepare_for_tx(&mut **descriptors, data, 1)) };
850 buffer
851 }
852 }
853 }
854
855 fn chunk_size(&self) -> usize {
856 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)]
864enum 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
873impl<'a> MaybeCopyRxBuf<'a> {
874 unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
875 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 let (buffer, _) = unsafe {
883 prepare_for_rx(
884 &mut **descriptors,
885 #[cfg(dma_can_access_psram)]
886 align_buffer,
887 data,
888 )
889 };
890 buffer
891 }
892 }
893 }
894
895 fn chunk_size(&self) -> usize {
896 match self {
897 MaybeCopyRxBuf::Copy(buffer) => buffer.capacity().min(MAX_DMA_SIZE),
898 MaybeCopyRxBuf::Direct { .. } => MAX_DMA_SIZE,
899 }
900 }
901
902 fn finish(&mut self, chunk: &mut [u8]) {
903 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 }
927}
928
929#[derive(Clone, Copy)]
930enum DmaOperationKind {
931 Copied,
933
934 InPlace,
936}
937
938impl DmaOperationKind {
939 fn compute(buffer: &[u8], direction: TransferDirection) -> Self {
940 fn is_dma_compatible(buffer: &[u8], _direction: TransferDirection) -> bool {
941 #[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 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 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 }
981
982 fn for_write(buffer: &[u8]) -> Self {
983 Self::compute(buffer, TransferDirection::Out)
984 }
985}
986
987impl<'d, Dm> SpiDma<'d, Dm>
988where
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
1000 fn spi(&self) -> &SpiWrapper<'_> {
1001 &self.spi
1002 }
1003
1004 fn driver(&self) -> Driver {
1005 Driver {
1006 info: self.spi.info(),
1007 state: self.spi.state(),
1008 }
1009 }
1010
1011 fn dma_driver(&self) -> DmaDriver {
1012 DmaDriver {
1013 driver: self.driver(),
1014 state: self.spi().dma_state(),
1015 dma_peripheral: self.spi.spi.dma_peripheral(),
1016 }
1017 }
1018
1019 fn is_done(&self) -> bool {
1020 if self.driver().busy() {
1021 return false;
1022 }
1023 if self.dma_driver().state.rx_transfer_in_progress.get() {
1024 if !self.channel.rx.is_done() && !self.channel.rx.has_dscr_empty_error() {
1032 return false;
1033 }
1034 }
1035 true
1036 }
1037
1038 fn wait_for_idle(&mut self) {
1039 while !self.is_done() {
1040 }
1042 self.dma_driver().state.rx_transfer_in_progress.set(false);
1043 self.dma_driver().state.tx_transfer_in_progress.set(false);
1044 fence(Ordering::Acquire);
1045 }
1046
1047 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1052 unsafe fn start_transfer_dma<RX: DmaRxBuffer, TX: DmaTxBuffer>(
1053 &mut self,
1054 full_duplex: bool,
1055 bytes_to_read: usize,
1056 bytes_to_write: usize,
1057 rx_buffer: &mut RX,
1058 tx_buffer: &mut TX,
1059 ) -> Result<(), Error> {
1060 if bytes_to_read > MAX_DMA_SIZE || bytes_to_write > MAX_DMA_SIZE {
1061 return Err(Error::MaxDmaTransferSizeExceeded);
1062 }
1063
1064 self.dma_driver()
1065 .state
1066 .rx_transfer_in_progress
1067 .set(bytes_to_read > 0);
1068 self.dma_driver()
1069 .state
1070 .tx_transfer_in_progress
1071 .set(bytes_to_write > 0);
1072 unsafe {
1073 self.dma_driver().start_transfer_dma(
1074 full_duplex,
1075 bytes_to_read,
1076 bytes_to_write,
1077 rx_buffer,
1078 tx_buffer,
1079 &mut self.channel,
1080 )
1081 }
1082 }
1083
1084 #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1089 unsafe fn set_up_address_workaround(
1090 &mut self,
1091 cmd: Command,
1092 address: Address,
1093 dummy: u8,
1094 ) -> Result<(), Error> {
1095 if dummy > 0 {
1096 error!("Dummy bits are not supported when there is no data to write");
1098 return Err(Error::Unsupported);
1099 }
1100
1101 let buffer = unsafe { self.dma_driver().tx_buffer() };
1102
1103 let bytes_to_write = address.width().div_ceil(8);
1104 let addr_bytes = address.value().to_be_bytes();
1107 let addr_bytes = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
1108 buffer.fill(addr_bytes);
1109
1110 self.driver().setup_half_duplex(
1111 true,
1112 cmd,
1113 Address::None,
1114 false,
1115 dummy,
1116 bytes_to_write == 0,
1117 address.mode(),
1118 )?;
1119
1120 let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1121
1122 unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
1123 }
1124
1125 fn cancel_transfer(&mut self) {
1126 let state = self.dma_driver().state;
1127 if state.tx_transfer_in_progress.get() || state.rx_transfer_in_progress.get() {
1128 self.dma_driver().abort_transfer();
1129
1130 if state.tx_transfer_in_progress.get() {
1132 self.channel.tx.stop_transfer();
1133 state.tx_transfer_in_progress.set(false);
1134 }
1135 if state.rx_transfer_in_progress.get() {
1136 self.channel.rx.stop_transfer();
1137 state.rx_transfer_in_progress.set(false);
1138 }
1139 }
1140 }
1141
1142 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1147 unsafe fn start_dma_write(
1148 &mut self,
1149 bytes_to_write: usize,
1150 buffer: &mut impl DmaTxBuffer,
1151 ) -> Result<(), Error> {
1152 let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1153
1154 unsafe { self.start_dma_transfer(0, bytes_to_write, rx_buffer, buffer) }
1155 }
1156
1157 #[cfg_attr(
1164 not(spi_master_dma_can_access_flash),
1165 doc = "The DMA cannot read flash memory."
1166 )]
1167 #[cfg_attr(
1170 spi_master_version = "1",
1171 doc = "On ESP32, transferring from internal SRAM requires copying the entire buffer if it is
1172not 4-byte aligned. This is a limitation of the current implementation."
1173 )]
1174 #[cfg_attr(
1175 spi_master_version = "2",
1176 doc = "On ESP32-S2, receiving into PSRAM requires the buffer's _end_ to be 16-byte
1177aligned, otherwise the driver requires copying the entire buffer."
1178 )]
1179 #[doc = ""]
1180 #[instability::unstable]
1185 pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDma<'d, Dm> {
1186 unsafe {
1187 (&mut *self.spi.dma_state().rx_buffer.get()).write(dma_rx_buf.into_scoped());
1188 (&mut *self.spi.dma_state().tx_buffer.get()).write(dma_tx_buf.into_scoped());
1189 }
1190 self
1191 }
1192
1193 #[allow(clippy::type_complexity)]
1199 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1200 #[instability::unstable]
1201 pub fn write_buffer<TX: DmaTxBuffer>(
1202 mut self,
1203 bytes_to_write: usize,
1204 mut buffer: TX,
1205 ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
1206 let clock = SpiClockGuard::new(self.spi.info());
1207
1208 if let Err(e) = self.driver().setup_full_duplex() {
1209 return Err((e, self, buffer));
1210 };
1211 match unsafe { self.start_dma_write(bytes_to_write, &mut buffer) } {
1212 Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1213 Err(e) => Err((e, self, buffer)),
1214 }
1215 }
1216
1217 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1222 unsafe fn start_dma_read(
1223 &mut self,
1224 bytes_to_read: usize,
1225 buffer: &mut impl DmaRxBuffer,
1226 ) -> Result<(), Error> {
1227 let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1228
1229 unsafe { self.start_dma_transfer(bytes_to_read, 0, buffer, tx_buffer) }
1230 }
1231
1232 #[allow(clippy::type_complexity)]
1238 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1239 #[instability::unstable]
1240 pub fn read_buffer<RX: DmaRxBuffer>(
1241 mut self,
1242 bytes_to_read: usize,
1243 mut buffer: RX,
1244 ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
1245 let clock = SpiClockGuard::new(self.spi.info());
1246
1247 if let Err(e) = self.driver().setup_full_duplex() {
1248 return Err((e, self, buffer));
1249 };
1250 match unsafe { self.start_dma_read(bytes_to_read, &mut buffer) } {
1251 Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1252 Err(e) => Err((e, self, buffer)),
1253 }
1254 }
1255
1256 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1261 unsafe fn start_dma_transfer(
1262 &mut self,
1263 bytes_to_read: usize,
1264 bytes_to_write: usize,
1265 rx_buffer: &mut impl DmaRxBuffer,
1266 tx_buffer: &mut impl DmaTxBuffer,
1267 ) -> Result<(), Error> {
1268 unsafe {
1269 self.start_transfer_dma(true, bytes_to_read, bytes_to_write, rx_buffer, tx_buffer)
1270 }
1271 }
1272
1273 #[allow(clippy::type_complexity)]
1279 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1280 #[instability::unstable]
1281 pub fn transfer_buffers<RX: DmaRxBuffer, TX: DmaTxBuffer>(
1282 mut self,
1283 bytes_to_read: usize,
1284 mut rx_buffer: RX,
1285 bytes_to_write: usize,
1286 mut tx_buffer: TX,
1287 ) -> Result<SpiDmaTransfer<'d, Dm, (RX, TX)>, (Error, Self, RX, TX)> {
1288 let clock = SpiClockGuard::new(self.spi.info());
1289
1290 if let Err(e) = self.driver().setup_full_duplex() {
1291 return Err((e, self, rx_buffer, tx_buffer));
1292 };
1293 match unsafe {
1294 self.start_dma_transfer(
1295 bytes_to_read,
1296 bytes_to_write,
1297 &mut rx_buffer,
1298 &mut tx_buffer,
1299 )
1300 } {
1301 Ok(_) => Ok(SpiDmaTransfer::new(self, (rx_buffer, tx_buffer), clock)),
1302 Err(e) => Err((e, self, rx_buffer, tx_buffer)),
1303 }
1304 }
1305
1306 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1311 unsafe fn start_half_duplex_read(
1312 &mut self,
1313 data_mode: DataMode,
1314 cmd: Command,
1315 address: Address,
1316 dummy: u8,
1317 bytes_to_read: usize,
1318 buffer: &mut impl DmaRxBuffer,
1319 ) -> Result<(), Error> {
1320 self.driver().setup_half_duplex(
1321 false,
1322 cmd,
1323 address,
1324 false,
1325 dummy,
1326 bytes_to_read == 0,
1327 data_mode,
1328 )?;
1329
1330 let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1331
1332 unsafe { self.start_transfer_dma(false, bytes_to_read, 0, buffer, tx_buffer) }
1333 }
1334
1335 #[allow(clippy::type_complexity)]
1337 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1338 #[instability::unstable]
1339 pub fn half_duplex_read_buffer<RX: DmaRxBuffer>(
1340 mut self,
1341 data_mode: DataMode,
1342 cmd: Command,
1343 address: Address,
1344 dummy: u8,
1345 bytes_to_read: usize,
1346 mut buffer: RX,
1347 ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
1348 let clock = SpiClockGuard::new(self.spi.info());
1349
1350 match unsafe {
1351 self.start_half_duplex_read(data_mode, cmd, address, dummy, bytes_to_read, &mut buffer)
1352 } {
1353 Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1354 Err(e) => Err((e, self, buffer)),
1355 }
1356 }
1357
1358 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1363 unsafe fn start_half_duplex_write(
1364 &mut self,
1365 data_mode: DataMode,
1366 cmd: Command,
1367 address: Address,
1368 dummy: u8,
1369 bytes_to_write: usize,
1370 buffer: &mut impl DmaTxBuffer,
1371 ) -> Result<(), Error> {
1372 #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1373 {
1374 if bytes_to_write == 0 && address.mode() != DataMode::SingleTwoDataLines {
1377 return unsafe { self.set_up_address_workaround(cmd, address, dummy) };
1378 }
1379 }
1380
1381 self.driver().setup_half_duplex(
1382 true,
1383 cmd,
1384 address,
1385 false,
1386 dummy,
1387 bytes_to_write == 0,
1388 data_mode,
1389 )?;
1390
1391 let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1392
1393 unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
1394 }
1395
1396 #[allow(clippy::type_complexity)]
1398 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1399 #[instability::unstable]
1400 pub fn half_duplex_write_buffer<TX: DmaTxBuffer>(
1401 mut self,
1402 data_mode: DataMode,
1403 cmd: Command,
1404 address: Address,
1405 dummy: u8,
1406 bytes_to_write: usize,
1407 mut buffer: TX,
1408 ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
1409 let clock = SpiClockGuard::new(self.spi.info());
1410
1411 match unsafe {
1412 self.start_half_duplex_write(
1413 data_mode,
1414 cmd,
1415 address,
1416 dummy,
1417 bytes_to_write,
1418 &mut buffer,
1419 )
1420 } {
1421 Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1422 Err(e) => Err((e, self, buffer)),
1423 }
1424 }
1425
1426 #[doc_replace(
1427 "max_frequency" => {
1428 cfg(esp32h2) => "48MHz",
1429 _ => "80MHz",
1430 }
1431 )]
1432 #[instability::unstable]
1439 pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
1440 self.driver().apply_config(config)
1441 }
1442
1443 fn transfer_buffers_dma(
1444 &mut self,
1445 read_bytes: usize,
1446 write_bytes: usize,
1447 mut rx_buffer: impl DmaRxBuffer,
1448 mut tx_buffer: impl DmaTxBuffer,
1449 ) -> Result<(), Error> {
1450 unsafe {
1451 self.start_dma_transfer(read_bytes, write_bytes, &mut rx_buffer, &mut tx_buffer)?;
1452 }
1453 self.wait_for_idle();
1454 Ok(())
1455 }
1456
1457 #[instability::unstable]
1459 pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
1460 let _clock = SpiClockGuard::new(self.spi.info());
1461
1462 self.driver().setup_full_duplex()?;
1463
1464 if self.use_blocking_transfer(words.len()) {
1465 self.dma_driver().disable_dma();
1466 return self.driver().read(words);
1467 }
1468
1469 let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1470 let mut maybe_copy_buffer = match DmaOperationKind::for_read(words) {
1471 DmaOperationKind::Copied => {
1472 MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
1473 }
1474 DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
1475 descriptors: &mut descriptors,
1476 #[cfg(dma_can_access_psram)]
1477 align_buffer: [const { None }; 2],
1478 },
1479 };
1480
1481 if maybe_copy_buffer.chunk_size() == 0 {
1482 return Err(Error::from(DmaError::BufferTooSmall));
1483 }
1484
1485 for chunk in words.chunks_mut(maybe_copy_buffer.chunk_size()) {
1486 let read_bytes = chunk.len();
1487 let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *chunk)) };
1488 let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
1489
1490 self.transfer_buffers_dma(read_bytes, 0, rx_buffer, tx_buffer)?;
1491
1492 maybe_copy_buffer.finish(chunk);
1493 }
1494
1495 Ok(())
1496 }
1497
1498 #[instability::unstable]
1500 pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
1501 let _clock = SpiClockGuard::new(self.spi.info());
1502
1503 self.driver().setup_full_duplex()?;
1504
1505 if self.use_blocking_transfer(words.len()) {
1506 self.dma_driver().disable_dma();
1507 return self.driver().write(words);
1508 }
1509
1510 let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1511 let mut maybe_copy_buffer = match DmaOperationKind::for_write(words) {
1512 DmaOperationKind::Copied => {
1513 MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
1514 }
1515 DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
1516 };
1517
1518 if maybe_copy_buffer.chunk_size() == 0 {
1519 return Err(Error::from(DmaError::BufferTooSmall));
1520 }
1521
1522 for chunk in words.chunks(maybe_copy_buffer.chunk_size()) {
1523 let write_bytes = chunk.len();
1524 let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
1525 let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(chunk)) };
1526
1527 self.transfer_buffers_dma(0, write_bytes, rx_buffer, tx_buffer)?;
1528 }
1529
1530 Ok(())
1531 }
1532
1533 #[instability::unstable]
1535 pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
1536 let _clock = SpiClockGuard::new(self.spi.info());
1537
1538 self.driver().setup_full_duplex()?;
1539
1540 if self.use_blocking_transfer(read.len().max(write.len())) {
1541 self.dma_driver().disable_dma();
1542 if read.is_empty() {
1543 return self.driver().write(write);
1544 } else if write.is_empty() {
1545 return self.driver().read(read);
1546 } else {
1547 return self.driver().transfer(read, write);
1548 }
1549 }
1550
1551 let common_length = min(read.len(), write.len());
1552 let (read_common, read_remainder) = read.split_at_mut(common_length);
1553 let (write_common, write_remainder) = write.split_at(common_length);
1554
1555 let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1557 let mut maybe_copy_rx_buffer = match DmaOperationKind::for_read(read_common) {
1558 DmaOperationKind::Copied => {
1559 MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
1560 }
1561 DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
1562 descriptors: &mut rx_descriptors,
1563 #[cfg(dma_can_access_psram)]
1564 align_buffer: [const { None }; 2],
1565 },
1566 };
1567
1568 let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1569 let mut maybe_copy_tx_buffer = match DmaOperationKind::for_write(write_common) {
1570 DmaOperationKind::Copied => {
1571 MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
1572 }
1573 DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut tx_descriptors),
1574 };
1575
1576 let chunk_size = min(
1577 maybe_copy_rx_buffer.chunk_size(),
1578 maybe_copy_tx_buffer.chunk_size(),
1579 );
1580
1581 if chunk_size == 0 {
1582 return Err(Error::from(DmaError::BufferTooSmall));
1583 }
1584
1585 for (read_chunk, write_chunk) in read_common
1586 .chunks_mut(chunk_size)
1587 .zip(write_common.chunks(chunk_size))
1588 {
1589 let read_bytes = read_chunk.len();
1590 let write_bytes = write_chunk.len();
1591 let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(NonNull::from(write_chunk)) };
1592 let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(NonNull::from(&mut *read_chunk)) };
1593
1594 self.transfer_buffers_dma(read_bytes, write_bytes, rx_buffer, tx_buffer)?;
1595
1596 maybe_copy_rx_buffer.finish(read_chunk);
1597 }
1598
1599 if !read_remainder.is_empty() {
1600 self.read(read_remainder)
1601 } else if !write_remainder.is_empty() {
1602 self.write(write_remainder)
1603 } else {
1604 Ok(())
1605 }
1606 }
1607
1608 #[instability::unstable]
1610 pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
1611 let _clock = SpiClockGuard::new(self.spi.info());
1612
1613 self.driver().setup_full_duplex()?;
1614
1615 if self.use_blocking_transfer(words.len()) {
1616 self.dma_driver().disable_dma();
1617 return self.driver().transfer_in_place(words);
1618 }
1619
1620 let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1621 let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1622 let (mut maybe_copy_rx_buffer, mut maybe_copy_tx_buffer) =
1623 match DmaOperationKind::for_write(words) {
1624 DmaOperationKind::Copied => (
1625 MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() }),
1626 MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() }),
1627 ),
1628 DmaOperationKind::InPlace => (
1629 MaybeCopyRxBuf::Direct {
1630 descriptors: &mut rx_descriptors,
1631 #[cfg(dma_can_access_psram)]
1632 align_buffer: [const { None }; 2],
1633 },
1634 MaybeCopyTxBuf::Direct(&mut tx_descriptors),
1635 ),
1636 };
1637
1638 let chunk_size = min(
1639 maybe_copy_rx_buffer.chunk_size(),
1640 maybe_copy_tx_buffer.chunk_size(),
1641 );
1642
1643 if chunk_size == 0 {
1644 return Err(Error::from(DmaError::BufferTooSmall));
1645 }
1646
1647 for chunk in words.chunks_mut(chunk_size) {
1648 let bytes = chunk.len();
1649 let ptr = NonNull::from(&mut *chunk);
1650 let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(ptr) };
1651 let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(ptr) };
1652
1653 self.transfer_buffers_dma(bytes, bytes, rx_buffer, tx_buffer)?;
1654
1655 maybe_copy_rx_buffer.finish(chunk);
1656 }
1657
1658 Ok(())
1659 }
1660
1661 #[instability::unstable]
1663 pub fn half_duplex_read(
1664 &mut self,
1665 data_mode: DataMode,
1666 cmd: Command,
1667 address: Address,
1668 dummy: u8,
1669 buffer: &mut [u8],
1670 ) -> Result<(), Error> {
1671 let _clock = SpiClockGuard::new(self.spi.info());
1672
1673 let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1674 if rx_buffer.capacity() == 0 {
1675 return Err(Error::from(DmaError::BufferTooSmall));
1676 }
1677 if buffer.len() > rx_buffer.capacity() {
1678 return Err(Error::from(DmaError::Overflow));
1679 }
1680
1681 unsafe {
1682 self.start_half_duplex_read(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)?;
1683 }
1684
1685 self.wait_for_idle();
1686
1687 buffer.copy_from_slice(&rx_buffer.as_slice()[..buffer.len()]);
1688
1689 Ok(())
1690 }
1691
1692 #[instability::unstable]
1694 pub fn half_duplex_write(
1695 &mut self,
1696 data_mode: DataMode,
1697 cmd: Command,
1698 address: Address,
1699 dummy: u8,
1700 buffer: &[u8],
1701 ) -> Result<(), Error> {
1702 let _clock = SpiClockGuard::new(self.spi.info());
1703
1704 let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1705 if tx_buffer.capacity() == 0 {
1706 return Err(Error::from(DmaError::BufferTooSmall));
1707 }
1708 if buffer.len() > tx_buffer.capacity() {
1709 return Err(Error::from(DmaError::Overflow));
1710 }
1711
1712 tx_buffer.as_mut_slice()[..buffer.len()].copy_from_slice(buffer);
1713
1714 unsafe {
1715 self.start_half_duplex_write(data_mode, cmd, address, dummy, buffer.len(), tx_buffer)?;
1716 }
1717
1718 self.wait_for_idle();
1719
1720 Ok(())
1721 }
1722}
1723
1724#[instability::unstable]
1728pub struct SpiDmaTransfer<'d, Dm, Buf>
1729where
1730 Dm: DriverMode,
1731{
1732 spi_dma: ManuallyDrop<SpiDma<'d, Dm>>,
1733 dma_buf: ManuallyDrop<Buf>,
1734 clock: ManuallyDrop<SpiClockGuard>,
1735}
1736
1737impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
1738 #[instability::unstable]
1742 pub async fn wait_for_done(&mut self) {
1743 self.spi_dma.wait_for_idle_async().await;
1744 }
1745}
1746
1747impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
1748where
1749 Dm: DriverMode,
1750{
1751 fn new(spi_dma: SpiDma<'d, Dm>, dma_buf: Buf, clock: SpiClockGuard) -> Self {
1752 Self {
1753 spi_dma: ManuallyDrop::new(spi_dma),
1754 dma_buf: ManuallyDrop::new(dma_buf),
1755 clock: ManuallyDrop::new(clock),
1756 }
1757 }
1758
1759 #[instability::unstable]
1764 pub fn is_done(&self) -> bool {
1765 self.spi_dma.is_done()
1766 }
1767
1768 #[instability::unstable]
1773 pub fn wait(mut self) -> (SpiDma<'d, Dm>, Buf) {
1774 self.spi_dma.wait_for_idle();
1775 let retval = unsafe {
1776 (
1777 ManuallyDrop::take(&mut self.spi_dma),
1778 ManuallyDrop::take(&mut self.dma_buf),
1779 )
1780 };
1781 let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
1782 core::mem::forget(self);
1783 retval
1784 }
1785
1786 #[instability::unstable]
1788 pub fn cancel(&mut self) {
1789 if !self.spi_dma.is_done() {
1790 self.spi_dma.cancel_transfer();
1791 }
1792 }
1793}
1794
1795impl<Dm, Buf> Drop for SpiDmaTransfer<'_, Dm, Buf>
1796where
1797 Dm: DriverMode,
1798{
1799 fn drop(&mut self) {
1800 if !self.is_done() {
1801 self.spi_dma.cancel_transfer();
1802 self.spi_dma.wait_for_idle();
1803 }
1804
1805 unsafe {
1806 ManuallyDrop::drop(&mut self.spi_dma);
1807 ManuallyDrop::drop(&mut self.dma_buf);
1808 }
1809 let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
1810 }
1811}
1812
1813pub(super) struct DmaDriver {
1814 driver: Driver,
1815 dma_peripheral: crate::dma::DmaPeripheral,
1816 state: &'static DmaState,
1817}
1818
1819impl DmaDriver {
1820 unsafe fn rx_buffer(&self) -> &'static mut ScopedDmaRxBuf<'static> {
1821 unsafe { self.state.rx_buffer() }
1822 }
1823
1824 unsafe fn tx_buffer(&self) -> &'static mut ScopedDmaTxBuf<'static> {
1825 unsafe { self.state.tx_buffer() }
1826 }
1827
1828 fn abort_transfer(&self) {
1829 self.driver.configure_datalen(1, 1);
1836 self.driver.update();
1837 }
1838
1839 fn disable_dma(&self) {
1840 #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
1841 self.regs().dma_conf().modify(|_, w| {
1842 w.dma_tx_ena().clear_bit();
1843 w.dma_rx_ena().clear_bit()
1844 });
1845
1846 }
1848
1849 fn regs(&self) -> &RegisterBlock {
1850 self.driver.regs()
1851 }
1852
1853 #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1854 unsafe fn start_transfer_dma<Dm: DriverMode>(
1855 &self,
1856 _full_duplex: bool,
1857 rx_len: usize,
1858 tx_len: usize,
1859 rx_buffer: &mut impl DmaRxBuffer,
1860 tx_buffer: &mut impl DmaTxBuffer,
1861 channel: &mut Channel<Dm, SpiMasterErased<'_>>,
1862 ) -> Result<(), Error> {
1863 #[cfg(spi_master_version = "2")]
1864 {
1865 self.regs().dma_out_link().write(|w| unsafe { w.bits(0) });
1867 self.regs().dma_in_link().write(|w| unsafe { w.bits(0) });
1868 }
1869
1870 self.driver.configure_datalen(rx_len, tx_len);
1871
1872 self.regs()
1874 .user()
1875 .modify(|_, w| w.usr_miso().bit(rx_len > 0).usr_mosi().bit(tx_len > 0));
1876
1877 self.enable_dma();
1878
1879 if rx_len > 0 {
1880 unsafe {
1881 channel
1882 .rx
1883 .prepare_transfer(self.dma_peripheral, rx_buffer)
1884 .and_then(|_| channel.rx.start_transfer())?;
1885 }
1886 } else {
1887 #[cfg(spi_master_version = "1")]
1888 {
1889 if _full_duplex {
1892 self.regs()
1893 .dma_in_link()
1894 .modify(|_, w| unsafe { w.inlink_addr().bits(0) });
1895 self.regs()
1896 .dma_in_link()
1897 .modify(|_, w| w.inlink_start().set_bit());
1898 }
1899 }
1900 }
1901 if tx_len > 0 {
1902 unsafe {
1903 channel
1904 .tx
1905 .prepare_transfer(self.dma_peripheral, tx_buffer)
1906 .and_then(|_| channel.tx.start_transfer())?;
1907 }
1908 }
1909
1910 #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
1911 self.reset_dma();
1912
1913 self.driver.start_operation();
1914
1915 Ok(())
1916 }
1917
1918 fn enable_dma(&self) {
1919 cfg_select! {
1920 any(spi_master_version = "1", spi_master_version = "2") => {
1921 self.reset_dma();
1922 }
1923 _ => {
1924 self.regs().dma_conf().modify(|_, w| {
1925 w.dma_tx_ena().set_bit();
1926 w.dma_rx_ena().set_bit()
1927 });
1928 }
1929 }
1930 }
1931
1932 fn reset_dma(&self) {
1933 self.regs().dma_conf().toggle(|w, bit| {
1934 cfg_select! {
1935 any(spi_master_version = "1", spi_master_version = "2") => {
1936 w.out_rst().bit(bit);
1937 w.in_rst().bit(bit);
1938 w.ahbm_fifo_rst().bit(bit);
1939 w.ahbm_rst().bit(bit)
1940 }
1941 _ => {
1942 w.rx_afifo_rst().bit(bit);
1943 w.buf_afifo_rst().bit(bit);
1944 w.dma_afifo_rst().bit(bit)
1945 }
1946 }
1947 });
1948
1949 self.clear_dma_interrupts();
1950 }
1951
1952 fn clear_dma_interrupts(&self) {
1953 self.regs().dma_int_clr().write(|w| {
1954 cfg_select! {
1955 any(spi_master_version = "1", spi_master_version = "2") => {
1956 w.inlink_dscr_empty().clear_bit_by_one();
1957 w.outlink_dscr_error().clear_bit_by_one();
1958 w.inlink_dscr_error().clear_bit_by_one();
1959 w.in_done().clear_bit_by_one();
1960 w.in_err_eof().clear_bit_by_one();
1961 w.in_suc_eof().clear_bit_by_one();
1962 w.out_done().clear_bit_by_one();
1963 w.out_eof().clear_bit_by_one();
1964 w.out_total_eof().clear_bit_by_one()
1965 }
1966 _ => {
1967 w.dma_infifo_full_err().clear_bit_by_one();
1968 w.dma_outfifo_empty_err().clear_bit_by_one();
1969 w.trans_done().clear_bit_by_one();
1970 w.mst_rx_afifo_wfull_err().clear_bit_by_one();
1971 w.mst_tx_afifo_rempty_err().clear_bit_by_one()
1972 }
1973 }
1974 });
1975 }
1976}
1977
1978struct DmaState {
1979 tx_transfer_in_progress: Cell<bool>,
1980 rx_transfer_in_progress: Cell<bool>,
1981
1982 rx_buffer: UnsafeCell<MaybeUninit<ScopedDmaRxBuf<'static>>>,
1983 tx_buffer: UnsafeCell<MaybeUninit<ScopedDmaTxBuf<'static>>>,
1984
1985 descriptors: UnsafeCell<InternalMemory<[DmaDescriptor; 2]>>,
1986
1987 #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1988 default_tx_buffer: UnsafeCell<InternalMemory<[u8; 4]>>,
1989}
1990
1991impl DmaState {
1992 #[allow(
1998 clippy::mut_from_ref,
1999 reason = "Safety requirements ensure this is okay"
2000 )]
2001 unsafe fn rx_buffer(&self) -> &mut ScopedDmaRxBuf<'static> {
2002 unsafe { (&mut *self.rx_buffer.get()).assume_init_mut() }
2003 }
2004
2005 #[allow(
2011 clippy::mut_from_ref,
2012 reason = "Safety requirements ensure this is okay"
2013 )]
2014 unsafe fn tx_buffer(&self) -> &mut ScopedDmaTxBuf<'static> {
2015 unsafe { (&mut *self.tx_buffer.get()).assume_init_mut() }
2016 }
2017}
2018
2019unsafe impl Sync for DmaState {}
2022
2023for_each_spi_master!(
2024 (all $( ($peri:ident, $sys:ident, $sclk:ident $_cs:tt $_sio:tt $(, $is_qspi:tt)?)),* ) => {
2025 impl AnySpi<'_> {
2026 #[inline(always)]
2027 fn dma_state(&self) -> &'static DmaState {
2028 match &self.0 {
2029 $(
2030 super::any::Inner::$sys(_spi) => {
2031 static DMA_STATE: DmaState = DmaState {
2032 tx_transfer_in_progress: Cell::new(false),
2033 rx_transfer_in_progress: Cell::new(false),
2034
2035 rx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
2036 tx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
2037
2038 descriptors: UnsafeCell::new(InternalMemory::new([DmaDescriptor::EMPTY; 2])),
2039 #[cfg(all(spi_master_version = "1", spi_address_workaround))]
2040 default_tx_buffer: UnsafeCell::new(InternalMemory::new([0; 4])),
2041 };
2042
2043 &DMA_STATE
2044 }
2045 )*
2046 }
2047 }
2048 }
2049 };
2050);
2051
2052impl SpiWrapper<'_> {
2053 fn dma_state(&self) -> &'static DmaState {
2054 self.spi.dma_state()
2055 }
2056}
2057
2058with_spi_master_dma_engine! {
2059 ($engine:tt, $any_channel:ident) => {
2060 #[instability::unstable]
2064 #[diagnostic::on_unimplemented(
2065 message = "The DMA channel cannot be used with this SPI peripheral",
2066 label = "This DMA channel",
2067 note = "Use a channel that matches the SPI instance."
2068 )]
2069 pub trait SpiMasterDmaChannel<'d, S>: crate::private::Sealed + Into<crate::dma::$any_channel<'d>> {}
2070
2071 crate::macros::impl_dma_channel_trait! {
2072 $engine,
2073 any_peri = AnySpi<'d>,
2074 peris = for_each_spi_master,
2075 ($peri:path, $ch:path) => {
2076 impl<'d> SpiMasterDmaChannel<'d, $peri> for $ch {}
2077 }
2078 }
2079
2080 type SpiMasterErased<'d> = crate::dma::$any_channel<'d>;
2082 };
2083}