1use enumset::EnumSet;
2use portable_atomic::Ordering;
3
4use crate::{
5 RegisterToggle,
6 asynch::AtomicWaker,
7 dma::{
8 BurstConfig,
9 DmaChannel,
10 DmaRxChannel,
11 DmaRxInterrupt,
12 DmaTxChannel,
13 DmaTxInterrupt,
14 InterruptAccess,
15 RegisterAccess,
16 RxRegisterAccess,
17 TxRegisterAccess,
18 },
19 interrupt::InterruptHandler,
20 peripherals::Interrupt,
21 system::{Peripheral, PeripheralGuard},
22};
23
24#[doc(hidden)]
26pub struct ChannelInfo {
27 pub(crate) peripheral_interrupt: Interrupt,
28
29 pub(crate) async_handler: InterruptHandler,
30
31 pub(crate) compatible_peripherals: &'static [u8],
33}
34
35pub(crate) struct ChannelState {
37 pub(crate) tx_waker: AtomicWaker,
39
40 pub(crate) rx_waker: AtomicWaker,
42
43 pub(crate) tx_async_flag: portable_atomic::AtomicBool,
45
46 pub(crate) rx_async_flag: portable_atomic::AtomicBool,
48}
49
50pub(super) type SpiRegisterBlock = crate::pac::spi2::RegisterBlock;
51
52#[derive(Debug)]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
55pub struct SpiDmaRxChannel<'d>(pub(crate) SpiDmaChannel<'d>);
56
57impl SpiDmaRxChannel<'_> {
58 fn regs(&self) -> &SpiRegisterBlock {
59 self.0.register_block()
60 }
61}
62
63impl crate::private::Sealed for SpiDmaRxChannel<'_> {}
64impl DmaRxChannel for SpiDmaRxChannel<'_> {}
65
66#[derive(Debug)]
68#[cfg_attr(feature = "defmt", derive(defmt::Format))]
69pub struct SpiDmaTxChannel<'d>(pub(crate) SpiDmaChannel<'d>);
70
71impl SpiDmaTxChannel<'_> {
72 fn regs(&self) -> &SpiRegisterBlock {
73 self.0.register_block()
74 }
75}
76
77impl crate::private::Sealed for SpiDmaTxChannel<'_> {}
78impl DmaTxChannel for SpiDmaTxChannel<'_> {}
79
80impl RegisterAccess for SpiDmaTxChannel<'_> {
81 #[allow(private_interfaces)]
82 fn enable(&self) -> Option<PeripheralGuard> {
83 cfg_select! {
84 esp32 => {
85 let clock = Peripheral::SpiDma;
86 }
87 _ => {
88 let clock = match self.0 {
89 SpiDmaChannel(any::Inner::Spi2(_)) => Peripheral::Spi2Dma,
90 SpiDmaChannel(any::Inner::Spi3(_)) => Peripheral::Spi3Dma,
91 };
92 }
93 }
94
95 Some(PeripheralGuard::new_with(clock, enable_spi_dma))
96 }
97
98 fn reset(&self) {
99 self.regs().dma_conf().toggle(|w, bit| w.out_rst().bit(bit));
100 }
101
102 fn set_burst_mode(&self, burst_mode: BurstConfig) {
103 self.regs()
104 .dma_conf()
105 .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
106 }
107
108 fn set_descr_burst_mode(&self, burst_mode: bool) {
109 self.regs()
110 .dma_conf()
111 .modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
112 }
113
114 fn set_link_addr(&self, address: u32) {
115 self.regs()
116 .dma_out_link()
117 .modify(|_, w| unsafe { w.outlink_addr().bits(address) });
118 }
119
120 fn start(&self) {
121 self.regs()
122 .dma_out_link()
123 .modify(|_, w| w.outlink_start().set_bit());
124 }
125
126 fn stop(&self) {
127 self.regs()
128 .dma_out_link()
129 .modify(|_, w| w.outlink_stop().set_bit());
130 }
131
132 fn restart(&self) {
133 self.regs()
134 .dma_out_link()
135 .modify(|_, w| w.outlink_restart().set_bit());
136 }
137
138 fn set_check_owner(&self, check_owner: Option<bool>) {
139 if check_owner == Some(true) {
140 panic!("SPI DMA does not support checking descriptor ownership");
141 }
142 }
143
144 #[cfg(dma_ext_mem_configurable_block_size)]
145 fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
146 self.regs()
147 .dma_conf()
148 .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
149 }
150
151 #[cfg(dma_can_access_psram)]
152 fn can_access_psram(&self) -> bool {
153 matches!(self.0, SpiDmaChannel(any::Inner::Spi2(_)))
154 }
155
156 fn compatible_peripherals(&self) -> &[u8] {
157 self.0.info().compatible_peripherals
158 }
159}
160
161impl TxRegisterAccess for SpiDmaTxChannel<'_> {
162 fn is_fifo_empty(&self) -> bool {
163 cfg_select! {
164 esp32 => self.regs().dma_rstatus().read().dma_out_status().bits() & 0x80000000 != 0,
165 _ => self
166 .regs()
167 .dma_outstatus()
168 .read()
169 .dma_outfifo_empty()
170 .bit_is_set(),
171 }
172 }
173
174 fn set_auto_write_back(&self, enable: bool) {
175 assert!(!enable);
177 }
178
179 fn last_dscr_address(&self) -> usize {
180 self.regs()
181 .out_eof_des_addr()
182 .read()
183 .dma_out_eof_des_addr()
184 .bits() as usize
185 }
186
187 fn peripheral_interrupt(&self) -> Option<Interrupt> {
188 None
189 }
190
191 fn async_handler(&self) -> Option<InterruptHandler> {
192 None
193 }
194}
195
196impl InterruptAccess<DmaTxInterrupt> for SpiDmaTxChannel<'_> {
197 fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
198 self.regs().dma_int_ena().modify(|_, w| {
199 for interrupt in interrupts {
200 match interrupt {
201 DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
202 DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().bit(enable),
203 DmaTxInterrupt::Eof => w.out_eof().bit(enable),
204 DmaTxInterrupt::Done => w.out_done().bit(enable),
205 };
206 }
207 w
208 });
209 }
210
211 fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
212 let mut result = EnumSet::new();
213
214 let int_ena = self.regs().dma_int_ena().read();
215 if int_ena.out_total_eof().bit_is_set() {
216 result |= DmaTxInterrupt::TotalEof;
217 }
218 if int_ena.outlink_dscr_error().bit_is_set() {
219 result |= DmaTxInterrupt::DescriptorError;
220 }
221 if int_ena.out_eof().bit_is_set() {
222 result |= DmaTxInterrupt::Eof;
223 }
224 if int_ena.out_done().bit_is_set() {
225 result |= DmaTxInterrupt::Done;
226 }
227
228 result
229 }
230
231 fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
232 self.regs().dma_int_clr().write(|w| {
233 for interrupt in interrupts.into() {
234 match interrupt {
235 DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
236 DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(),
237 DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
238 DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
239 };
240 }
241 w
242 });
243 }
244
245 fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
246 let mut result = EnumSet::new();
247
248 let int_raw = self.regs().dma_int_raw().read();
249 if int_raw.out_total_eof().bit_is_set() {
250 result |= DmaTxInterrupt::TotalEof;
251 }
252 if int_raw.outlink_dscr_error().bit_is_set() {
253 result |= DmaTxInterrupt::DescriptorError;
254 }
255 if int_raw.out_eof().bit_is_set() {
256 result |= DmaTxInterrupt::Eof;
257 }
258 if int_raw.out_done().bit_is_set() {
259 result |= DmaTxInterrupt::Done;
260 }
261
262 result
263 }
264
265 fn waker(&self) -> &'static AtomicWaker {
266 &self.0.state().tx_waker
267 }
268
269 fn is_async(&self) -> bool {
270 self.0.state().tx_async_flag.load(Ordering::Acquire)
271 }
272
273 fn set_async(&self, is_async: bool) {
274 self.0
275 .state()
276 .tx_async_flag
277 .store(is_async, Ordering::Release);
278 }
279}
280
281impl RegisterAccess for SpiDmaRxChannel<'_> {
282 #[allow(private_interfaces)]
283 fn enable(&self) -> Option<PeripheralGuard> {
284 cfg_select! {
285 esp32 => {
286 let clock = Peripheral::SpiDma;
287 }
288 _ => {
289 let clock = match self.0 {
290 SpiDmaChannel(any::Inner::Spi2(_)) => Peripheral::Spi2Dma,
291 SpiDmaChannel(any::Inner::Spi3(_)) => Peripheral::Spi3Dma,
292 };
293 }
294 }
295
296 Some(PeripheralGuard::new_with(clock, enable_spi_dma))
297 }
298
299 fn reset(&self) {
300 self.regs().dma_conf().toggle(|w, bit| w.in_rst().bit(bit));
301 }
302
303 fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
304
305 fn set_descr_burst_mode(&self, burst_mode: bool) {
306 self.regs()
307 .dma_conf()
308 .modify(|_, w| w.indscr_burst_en().bit(burst_mode));
309 }
310
311 fn set_link_addr(&self, address: u32) {
312 self.regs()
313 .dma_in_link()
314 .modify(|_, w| unsafe { w.inlink_addr().bits(address) });
315 }
316
317 fn start(&self) {
318 self.regs()
319 .dma_in_link()
320 .modify(|_, w| w.inlink_start().set_bit());
321 }
322
323 fn stop(&self) {
324 self.regs()
325 .dma_in_link()
326 .modify(|_, w| w.inlink_stop().set_bit());
327 }
328
329 fn restart(&self) {
330 self.regs()
331 .dma_in_link()
332 .modify(|_, w| w.inlink_restart().set_bit());
333 }
334
335 fn set_check_owner(&self, check_owner: Option<bool>) {
336 if check_owner == Some(true) {
337 panic!("SPI DMA does not support checking descriptor ownership");
338 }
339 }
340
341 #[cfg(dma_ext_mem_configurable_block_size)]
342 fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize) {
343 self.regs()
344 .dma_conf()
345 .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
346 }
347
348 #[cfg(dma_can_access_psram)]
349 fn can_access_psram(&self) -> bool {
350 matches!(self.0, SpiDmaChannel(any::Inner::Spi2(_)))
351 }
352
353 fn compatible_peripherals(&self) -> &[u8] {
354 self.0.info().compatible_peripherals
355 }
356}
357
358impl RxRegisterAccess for SpiDmaRxChannel<'_> {
359 #[cfg(dma_supports_mem2mem)]
360 fn set_mem2mem_mode(&self, en: bool) {
361 self.regs()
362 .dma_conf()
363 .modify(|_, w| w.mem_trans_en().bit(en));
364 }
365
366 fn peripheral_interrupt(&self) -> Option<Interrupt> {
367 Some(self.0.info().peripheral_interrupt)
368 }
369
370 fn async_handler(&self) -> Option<InterruptHandler> {
371 Some(self.0.info().async_handler)
372 }
373}
374
375impl InterruptAccess<DmaRxInterrupt> for SpiDmaRxChannel<'_> {
376 fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
377 self.regs().dma_int_ena().modify(|_, w| {
378 for interrupt in interrupts {
379 match interrupt {
380 DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
381 DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
382 DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().bit(enable),
383 DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().bit(enable),
384 DmaRxInterrupt::Done => w.in_done().bit(enable),
385 };
386 }
387 w
388 });
389 }
390
391 fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
392 let mut result = EnumSet::new();
393
394 let int_ena = self.regs().dma_int_ena().read();
395 if int_ena.inlink_dscr_error().bit_is_set() {
396 result |= DmaRxInterrupt::DescriptorError;
397 }
398 if int_ena.inlink_dscr_empty().bit_is_set() {
399 result |= DmaRxInterrupt::DescriptorEmpty;
400 }
401 if int_ena.in_suc_eof().bit_is_set() {
402 result |= DmaRxInterrupt::SuccessfulEof;
403 }
404 if int_ena.in_err_eof().bit_is_set() {
405 result |= DmaRxInterrupt::ErrorEof;
406 }
407 if int_ena.in_done().bit_is_set() {
408 result |= DmaRxInterrupt::Done;
409 }
410
411 result
412 }
413
414 fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
415 self.regs().dma_int_clr().modify(|_, w| {
416 for interrupt in interrupts.into() {
417 match interrupt {
418 DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
419 DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
420 DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(),
421 DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(),
422 DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
423 };
424 }
425 w
426 });
427 }
428
429 fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
430 let mut result = EnumSet::new();
431
432 let int_raw = self.regs().dma_int_raw().read();
433 if int_raw.inlink_dscr_error().bit_is_set() {
434 result |= DmaRxInterrupt::DescriptorError;
435 }
436 if int_raw.inlink_dscr_empty().bit_is_set() {
437 result |= DmaRxInterrupt::DescriptorEmpty;
438 }
439 if int_raw.in_suc_eof().bit_is_set() {
440 result |= DmaRxInterrupt::SuccessfulEof;
441 }
442 if int_raw.in_err_eof().bit_is_set() {
443 result |= DmaRxInterrupt::ErrorEof;
444 }
445 if int_raw.in_done().bit_is_set() {
446 result |= DmaRxInterrupt::Done;
447 }
448
449 result
450 }
451
452 fn waker(&self) -> &'static AtomicWaker {
453 &self.0.state().rx_waker
454 }
455
456 fn is_async(&self) -> bool {
457 self.0.state().rx_async_flag.load(Ordering::Relaxed)
458 }
459
460 fn set_async(&self, is_async: bool) {
461 self.0
462 .state()
463 .rx_async_flag
464 .store(is_async, Ordering::Relaxed);
465 }
466}
467
468crate::any_peripheral! {
469 pub peripheral SpiDmaChannel<'d> {
471 Spi2(DMA_SPI2<'d>),
472 Spi3(DMA_SPI3<'d>),
473 }
474}
475
476impl<'d> DmaChannel for SpiDmaChannel<'d> {
477 type Rx = SpiDmaRxChannel<'d>;
478 type Tx = SpiDmaTxChannel<'d>;
479
480 unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
481 (
482 SpiDmaRxChannel(unsafe { self.clone_unchecked() }),
483 SpiDmaTxChannel(self),
484 )
485 }
486}
487
488impl SpiDmaChannel<'_> {
489 delegate::delegate! {
490 to match &self.0 {
491 any::Inner::Spi2(channel) => channel,
492 any::Inner::Spi3(channel) => channel,
493 } {
494 fn register_block(&self) -> &SpiRegisterBlock;
495 fn info(&self) -> &'static ChannelInfo;
496 fn state(&self) -> &'static ChannelState;
497 }
498 }
499}
500
501impl<'d> From<SpiDmaChannel<'d>> for SpiDmaRxChannel<'d> {
503 fn from(this: SpiDmaChannel<'d>) -> SpiDmaRxChannel<'d> {
504 SpiDmaRxChannel(this)
505 }
506}
507
508impl<'d> From<SpiDmaChannel<'d>> for SpiDmaTxChannel<'d> {
509 fn from(this: SpiDmaChannel<'d>) -> SpiDmaTxChannel<'d> {
510 SpiDmaTxChannel(this)
511 }
512}
513
514for_each_dma_channel_peri_pair! {
515 ("SPI_DMA", $dma_peri:ident, $peri:ident) => {
516 use $crate::peripherals::$dma_peri;
517 impl $dma_peri<'_> {
518 pub(super) fn info(&self) -> &'static ChannelInfo {
519 #[crate::handler(priority = crate::interrupt::Priority::max())]
520 fn interrupt_handler() {
521 crate::dma::asynch::handle_in_interrupt::<$dma_peri<'static>>();
522 crate::dma::asynch::handle_out_interrupt::<$dma_peri<'static>>();
523 }
524
525 static INFO: ChannelInfo = ChannelInfo {
526 peripheral_interrupt: paste::paste! { Interrupt::[<$peri _DMA>] },
527 async_handler: interrupt_handler,
528 compatible_peripherals: &[crate::dma::DmaPeripheral::$peri.0],
529 };
530
531 &INFO
532 }
533
534 pub(super) fn state(&self) -> &'static ChannelState {
535 static STATE: ChannelState = ChannelState {
536 tx_waker: AtomicWaker::new(),
537 rx_waker: AtomicWaker::new(),
538 tx_async_flag: portable_atomic::AtomicBool::new(false),
539 rx_async_flag: portable_atomic::AtomicBool::new(false),
540 };
541 &STATE
542 }
543 }
544
545 crate::dma::impl_channel_common!(SpiDma, $dma_peri);
546 };
547}
548
549pub(super) fn enable_spi_dma() {
550 #[cfg(esp32)]
551 {
552 use crate::peripherals::DPORT;
558
559 DPORT::regs().spi_dma_chan_sel().modify(|_, w| unsafe {
560 w.spi2_dma_chan_sel().bits(1);
561 w.spi3_dma_chan_sel().bits(2)
562 });
563 }
564}