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