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