1use core::{num::NonZeroUsize, ptr::NonNull};
2
3use dma_api::{CoherentArray, DeviceDma, DmaDirection, StreamingMap};
4use log::warn;
5use sdmmc_protocol::{
6 block::{
7 BlockPoll, BlockRequestId, BlockTransferDirection, BlockTransferMode, BlockTransferState,
8 CommandPoll, DataCommandPoll,
9 },
10 cmd::{CMD12, Command, DataDirection, cmd17, cmd18, cmd24, cmd25},
11 error::{Error, ErrorContext, Phase},
12 response::Response,
13};
14
15use crate::{
16 host::{DwMmc, PendingData},
17 regs::RegisterBlockVolatileFieldAccess,
18};
19
20const DESC_OWN: u32 = 1 << 31;
21const DESC_CH: u32 = 1 << 4;
22const DESC_FS: u32 = 1 << 3;
23const DESC_LD: u32 = 1 << 2;
24const DESC_DIC: u32 = 1 << 1;
25
26const BMOD_SWR: u32 = 1 << 0;
27const BMOD_FB: u32 = 1 << 1;
28const BMOD_DE: u32 = 1 << 7;
29
30const DMA_POLL_LIMIT: u32 = 8_000_000;
31pub const IDMAC_DESC_ALIGN: usize = 16;
32pub const IDMAC_DESC_SIZE: usize = core::mem::size_of::<IdmacDesc>();
33const BLOCK_SIZE: usize = 512;
34
35pub type RequestId = BlockRequestId;
36
37#[derive(Default)]
38pub struct BlockRequestSlot {
39 next: usize,
40 state: BlockTransferState,
41}
42
43impl BlockRequestSlot {
44 pub fn start(
45 &mut self,
46 mode: BlockTransferMode,
47 direction: BlockTransferDirection,
48 ) -> Result<RequestId, Error> {
49 if !matches!(self.state, BlockTransferState::Idle) {
50 return Err(Error::UnsupportedCommand);
51 }
52 let id = RequestId::new(self.next);
53 self.next = self.next.wrapping_add(1);
54 self.state = BlockTransferState::Submitted {
55 id,
56 mode,
57 direction,
58 };
59 Ok(id)
60 }
61
62 pub fn complete(&mut self, id: RequestId) -> Result<(), Error> {
63 if self.state.id() != Some(id) {
64 return Err(Error::InvalidArgument);
65 }
66 self.state = BlockTransferState::Idle;
67 Ok(())
68 }
69
70 pub fn state(&self) -> BlockTransferState {
71 self.state
72 }
73}
74
75pub struct BlockRequest {
76 inner: BlockRequestKind,
77}
78
79unsafe impl Send for BlockRequest {}
84
85enum BlockRequestKind {
86 FifoRead {
87 id: RequestId,
88 buffer: NonNull<u8>,
89 len: usize,
90 block_size: usize,
91 offset: usize,
92 cmd_index: u8,
93 phase: Phase,
94 stage: BlockRequestStage,
95 stop_after_complete: bool,
96 response: Option<Response>,
97 },
98 FifoWrite {
99 id: RequestId,
100 buffer: NonNull<u8>,
101 len: usize,
102 block_size: usize,
103 offset: usize,
104 cmd_index: u8,
105 phase: Phase,
106 stage: BlockRequestStage,
107 stop_after_complete: bool,
108 response: Option<Response>,
109 },
110 Read {
111 id: RequestId,
112 map: StreamingMap<u8>,
113 _desc: CoherentArray<IdmacDesc>,
114 cmd_index: u8,
115 phase: Phase,
116 stage: BlockRequestStage,
117 stop_after_complete: bool,
118 response: Option<Response>,
119 },
120 Write {
121 id: RequestId,
122 _map: StreamingMap<u8>,
123 _desc: CoherentArray<IdmacDesc>,
124 cmd_index: u8,
125 phase: Phase,
126 stage: BlockRequestStage,
127 stop_after_complete: bool,
128 response: Option<Response>,
129 },
130}
131
132#[derive(Clone, Copy, Debug, PartialEq, Eq)]
133enum BlockRequestStage {
134 Command,
135 Data,
136 Stop,
137}
138
139impl BlockRequest {
140 pub fn id(&self) -> RequestId {
141 match &self.inner {
142 BlockRequestKind::FifoRead { id, .. }
143 | BlockRequestKind::FifoWrite { id, .. }
144 | BlockRequestKind::Read { id, .. }
145 | BlockRequestKind::Write { id, .. } => *id,
146 }
147 }
148
149 pub fn state(&self) -> BlockTransferState {
150 match &self.inner {
151 BlockRequestKind::FifoRead { id, .. } => BlockTransferState::Submitted {
152 id: *id,
153 mode: BlockTransferMode::Fifo,
154 direction: BlockTransferDirection::Read,
155 },
156 BlockRequestKind::FifoWrite { id, .. } => BlockTransferState::Submitted {
157 id: *id,
158 mode: BlockTransferMode::Fifo,
159 direction: BlockTransferDirection::Write,
160 },
161 BlockRequestKind::Read { id, .. } => BlockTransferState::Submitted {
162 id: *id,
163 mode: BlockTransferMode::Dma,
164 direction: BlockTransferDirection::Read,
165 },
166 BlockRequestKind::Write { id, .. } => BlockTransferState::Submitted {
167 id: *id,
168 mode: BlockTransferMode::Dma,
169 direction: BlockTransferDirection::Write,
170 },
171 }
172 }
173
174 fn response(&self) -> Option<Response> {
175 match &self.inner {
176 BlockRequestKind::FifoRead { response, .. }
177 | BlockRequestKind::FifoWrite { response, .. }
178 | BlockRequestKind::Read { response, .. }
179 | BlockRequestKind::Write { response, .. } => *response,
180 }
181 }
182}
183
184#[repr(C, align(16))]
185#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub struct IdmacDesc {
187 des0: u32,
188 des1: u32,
189 des2: u32,
190 des3: u32,
191}
192
193impl IdmacDesc {
194 pub fn chained(buffer_dma: u32, len: u32, next_desc_dma: u32, first: bool, last: bool) -> Self {
195 let mut des0 = DESC_OWN | DESC_CH | DESC_DIC;
196 if first {
197 des0 |= DESC_FS;
198 }
199 if last {
200 des0 |= DESC_LD;
201 }
202 Self {
203 des0,
204 des1: len,
205 des2: buffer_dma,
206 des3: next_desc_dma,
207 }
208 }
209}
210
211impl DwMmc {
212 pub fn submit_read_blocks(
218 &mut self,
219 start_block: u32,
220 buffer: NonNull<u8>,
221 size: NonZeroUsize,
222 dma: Option<&DeviceDma>,
223 mode: BlockTransferMode,
224 slot: &mut BlockRequestSlot,
225 ) -> Result<BlockRequest, Error> {
226 let id = slot.start(mode, BlockTransferDirection::Read)?;
227 let result = match mode {
228 BlockTransferMode::Dma => {
229 let dma = dma.ok_or(Error::UnsupportedCommand)?;
230 self.build_dma_read_request(start_block, buffer, size, dma, id)
231 }
232 BlockTransferMode::Fifo => self.build_fifo_read_request(start_block, buffer, size, id),
233 _ => Err(Error::UnsupportedCommand),
235 };
236 match result {
237 Ok(request) => Ok(request),
238 Err(err) => {
239 let _ = slot.complete(id);
240 Err(err)
241 }
242 }
243 }
244
245 pub fn submit_write_blocks(
249 &mut self,
250 start_block: u32,
251 buffer: NonNull<u8>,
252 size: NonZeroUsize,
253 dma: Option<&DeviceDma>,
254 mode: BlockTransferMode,
255 slot: &mut BlockRequestSlot,
256 ) -> Result<BlockRequest, Error> {
257 let id = slot.start(mode, BlockTransferDirection::Write)?;
258 let result = match mode {
259 BlockTransferMode::Dma => {
260 let dma = dma.ok_or(Error::UnsupportedCommand)?;
261 self.build_dma_write_request(start_block, buffer, size, dma, id)
262 }
263 BlockTransferMode::Fifo => self.build_fifo_write_request(start_block, buffer, size, id),
264 _ => Err(Error::UnsupportedCommand),
266 };
267 match result {
268 Ok(request) => Ok(request),
269 Err(err) => {
270 let _ = slot.complete(id);
271 Err(err)
272 }
273 }
274 }
275
276 pub fn poll_block_request(
278 &mut self,
279 request: &mut Option<BlockRequest>,
280 id: RequestId,
281 slot: &mut BlockRequestSlot,
282 ) -> Result<BlockPoll, Error> {
283 match self.poll_block_request_response(request, id, slot)? {
284 DataCommandPoll::Pending => Ok(BlockPoll::Pending),
285 DataCommandPoll::Complete(_) => Ok(BlockPoll::Complete),
286 _ => Ok(BlockPoll::Complete),
288 }
289 }
290
291 pub fn poll_block_request_response(
292 &mut self,
293 request: &mut Option<BlockRequest>,
294 id: RequestId,
295 slot: &mut BlockRequestSlot,
296 ) -> Result<DataCommandPoll, Error> {
297 let Some(active) = request.as_ref() else {
298 return Err(Error::InvalidArgument);
299 };
300 if active.id() != id {
301 return Err(Error::InvalidArgument);
302 }
303
304 if matches!(
305 active.inner,
306 BlockRequestKind::FifoRead { .. } | BlockRequestKind::FifoWrite { .. }
307 ) {
308 return self.poll_fifo_request(request, id, slot);
309 }
310
311 let (cmd_index, phase, stage) = match &active.inner {
312 BlockRequestKind::Read {
313 cmd_index,
314 phase,
315 stage,
316 ..
317 }
318 | BlockRequestKind::Write {
319 cmd_index,
320 phase,
321 stage,
322 ..
323 } => (*cmd_index, *phase, *stage),
324 BlockRequestKind::FifoRead { .. } | BlockRequestKind::FifoWrite { .. } => {
325 unreachable!()
326 }
327 };
328
329 if stage == BlockRequestStage::Command {
330 match self.poll_command() {
331 Ok(CommandPoll::Pending) => return Ok(DataCommandPoll::Pending),
332 Ok(CommandPoll::Complete) => {
333 let response = self.take_command_response()?;
334 if let Some(active) = request.as_mut() {
335 match &mut active.inner {
336 BlockRequestKind::Read {
337 stage,
338 response: stored_response,
339 ..
340 }
341 | BlockRequestKind::Write {
342 stage,
343 response: stored_response,
344 ..
345 } => {
346 *stage = BlockRequestStage::Data;
347 *stored_response = Some(response);
348 }
349 BlockRequestKind::FifoRead { .. }
350 | BlockRequestKind::FifoWrite { .. } => unreachable!(),
351 }
352 }
353 return Ok(DataCommandPoll::Pending);
354 }
355 Ok(_) => return Ok(DataCommandPoll::Pending),
357 Err(err) => {
358 self.abort_block_request(request, id, slot, phase);
359 return Err(err);
360 }
361 }
362 }
363
364 if stage == BlockRequestStage::Stop {
365 return self.poll_block_stop(request, id, slot, phase);
366 }
367
368 match self.poll_dma_complete(cmd_index, phase) {
369 Ok(BlockPoll::Pending) => Ok(DataCommandPoll::Pending),
370 Ok(BlockPoll::Complete) => self.finish_dma_data(request, id, slot),
371 Ok(_) => Ok(DataCommandPoll::Pending),
373 Err(err) => {
374 self.abort_block_request(request, id, slot, phase);
375 Err(err)
376 }
377 }
378 }
379
380 fn build_dma_read_request(
381 &mut self,
382 start_block: u32,
383 buffer: NonNull<u8>,
384 size: NonZeroUsize,
385 dma: &DeviceDma,
386 id: RequestId,
387 ) -> Result<BlockRequest, Error> {
388 let block_count = dma_read_block_count(size)?;
389 let map = dma
390 .map_streaming_slice_for_device(
391 unsafe { core::slice::from_raw_parts_mut(buffer.as_ptr(), size.get()) },
392 BLOCK_SIZE,
393 DmaDirection::FromDevice,
394 )
395 .map_err(|err| map_dma_error(err, Phase::DataRead))?;
396 let mut desc = dma
397 .coherent_array_zero_with_align::<IdmacDesc>(block_count as usize, IDMAC_DESC_ALIGN)
398 .map_err(|err| map_dma_error(err, Phase::DataRead))?;
399 let cmd = if block_count == 1 {
400 cmd17(start_block)
401 } else {
402 cmd18(start_block)
403 };
404 self.submit_idmac_transfer_mapped(&cmd, block_count, map.dma_addr().as_u64(), &mut desc)?;
405 Ok(BlockRequest {
406 inner: BlockRequestKind::Read {
407 id,
408 map,
409 _desc: desc,
410 cmd_index: cmd.cmd,
411 phase: Phase::DataRead,
412 stage: BlockRequestStage::Command,
413 stop_after_complete: block_count > 1,
414 response: None,
415 },
416 })
417 }
418
419 fn build_dma_write_request(
420 &mut self,
421 start_block: u32,
422 buffer: NonNull<u8>,
423 size: NonZeroUsize,
424 dma: &DeviceDma,
425 id: RequestId,
426 ) -> Result<BlockRequest, Error> {
427 let block_count = dma_write_block_count(size)?;
428 let map = dma
429 .map_streaming_slice_for_device(
430 unsafe { core::slice::from_raw_parts_mut(buffer.as_ptr(), size.get()) },
431 BLOCK_SIZE,
432 DmaDirection::ToDevice,
433 )
434 .map_err(|err| map_dma_error(err, Phase::DataWrite))?;
435 let mut desc = dma
436 .coherent_array_zero_with_align::<IdmacDesc>(block_count as usize, IDMAC_DESC_ALIGN)
437 .map_err(|err| map_dma_error(err, Phase::DataWrite))?;
438 let cmd = if block_count == 1 {
439 cmd24(start_block)
440 } else {
441 cmd25(start_block)
442 };
443 self.submit_idmac_transfer_mapped(&cmd, block_count, map.dma_addr().as_u64(), &mut desc)?;
444 Ok(BlockRequest {
445 inner: BlockRequestKind::Write {
446 id,
447 _map: map,
448 _desc: desc,
449 cmd_index: cmd.cmd,
450 phase: Phase::DataWrite,
451 stage: BlockRequestStage::Command,
452 stop_after_complete: block_count > 1,
453 response: None,
454 },
455 })
456 }
457
458 fn build_fifo_read_request(
459 &mut self,
460 start_block: u32,
461 buffer: NonNull<u8>,
462 size: NonZeroUsize,
463 id: RequestId,
464 ) -> Result<BlockRequest, Error> {
465 let block_count = dma_read_block_count(size)?;
466 let cmd = if block_count == 1 {
467 cmd17(start_block)
468 } else {
469 cmd18(start_block)
470 };
471 self.build_fifo_data_request(
472 &cmd,
473 buffer,
474 size.get(),
475 BLOCK_SIZE as u32,
476 block_count,
477 id,
478 DataDirection::Read,
479 block_count > 1,
480 )
481 }
482
483 fn build_fifo_write_request(
484 &mut self,
485 start_block: u32,
486 buffer: NonNull<u8>,
487 size: NonZeroUsize,
488 id: RequestId,
489 ) -> Result<BlockRequest, Error> {
490 let block_count = dma_write_block_count(size)?;
491 let cmd = if block_count == 1 {
492 cmd24(start_block)
493 } else {
494 cmd25(start_block)
495 };
496 self.build_fifo_data_request(
497 &cmd,
498 buffer,
499 size.get(),
500 BLOCK_SIZE as u32,
501 block_count,
502 id,
503 DataDirection::Write,
504 block_count > 1,
505 )
506 }
507
508 #[allow(clippy::too_many_arguments)]
509 pub fn submit_fifo_data_request(
510 &mut self,
511 cmd: &Command,
512 buffer: NonNull<u8>,
513 len: usize,
514 block_size: u32,
515 block_count: u32,
516 direction: DataDirection,
517 slot: &mut BlockRequestSlot,
518 ) -> Result<BlockRequest, Error> {
519 let transfer_direction = match direction {
520 DataDirection::Read => BlockTransferDirection::Read,
521 DataDirection::Write => BlockTransferDirection::Write,
522 DataDirection::None => return Err(Error::InvalidArgument),
523 _ => return Err(Error::InvalidArgument),
525 };
526 let id = slot.start(BlockTransferMode::Fifo, transfer_direction)?;
527 match self.build_fifo_data_request(
528 cmd,
529 buffer,
530 len,
531 block_size,
532 block_count,
533 id,
534 direction,
535 false,
536 ) {
537 Ok(request) => Ok(request),
538 Err(err) => {
539 let _ = slot.complete(id);
540 Err(err)
541 }
542 }
543 }
544
545 #[allow(clippy::too_many_arguments)]
546 fn build_fifo_data_request(
547 &mut self,
548 cmd: &Command,
549 buffer: NonNull<u8>,
550 len: usize,
551 block_size: u32,
552 block_count: u32,
553 id: RequestId,
554 direction: DataDirection,
555 stop_after_complete: bool,
556 ) -> Result<BlockRequest, Error> {
557 let block_size_usize = usize::try_from(block_size).map_err(|_| Error::InvalidArgument)?;
558 let block_count_usize = usize::try_from(block_count).map_err(|_| Error::InvalidArgument)?;
559 if block_size_usize == 0
560 || block_count_usize == 0
561 || len != block_size_usize.saturating_mul(block_count_usize)
562 {
563 return Err(Error::InvalidArgument);
564 }
565 let phase = match direction {
566 DataDirection::Read => Phase::DataRead,
567 DataDirection::Write => Phase::DataWrite,
568 DataDirection::None => return Err(Error::InvalidArgument),
569 _ => return Err(Error::InvalidArgument),
571 };
572 self.pending_data = Some(PendingData {
573 direction,
574 block_size,
575 block_count,
576 });
577 self.data_blocks_remaining = block_count;
578 self.submit_command(cmd)?;
579 let inner = match direction {
580 DataDirection::Read => BlockRequestKind::FifoRead {
581 id,
582 buffer,
583 len,
584 block_size: block_size_usize,
585 offset: 0,
586 cmd_index: cmd.cmd,
587 phase,
588 stage: BlockRequestStage::Command,
589 stop_after_complete,
590 response: None,
591 },
592 DataDirection::Write => BlockRequestKind::FifoWrite {
593 id,
594 buffer,
595 len,
596 block_size: block_size_usize,
597 offset: 0,
598 cmd_index: cmd.cmd,
599 phase,
600 stage: BlockRequestStage::Command,
601 stop_after_complete,
602 response: None,
603 },
604 DataDirection::None => return Err(Error::InvalidArgument),
605 _ => return Err(Error::InvalidArgument),
607 };
608 Ok(BlockRequest { inner })
609 }
610
611 fn submit_idmac_transfer_mapped(
612 &mut self,
613 cmd: &Command,
614 block_count: u32,
615 buffer_dma: u64,
616 desc: &mut CoherentArray<IdmacDesc>,
617 ) -> Result<(), Error> {
618 if block_count == 0 {
619 return Err(Error::InvalidArgument);
620 }
621 let direction = cmd.data_direction();
622 let phase = match direction {
623 DataDirection::Read => Phase::DataRead,
624 DataDirection::Write => Phase::DataWrite,
625 DataDirection::None => return Err(Error::InvalidArgument),
626 _ => return Err(Error::InvalidArgument),
628 };
629 let byte_count = block_count
630 .checked_mul(BLOCK_SIZE as u32)
631 .ok_or(Error::InvalidArgument)?;
632 let transfer_end = buffer_dma
633 .checked_add(byte_count as u64)
634 .ok_or(Error::InvalidArgument)?;
635 let desc_bytes = (block_count as usize)
636 .checked_mul(IDMAC_DESC_SIZE)
637 .ok_or(Error::InvalidArgument)?;
638 let desc_dma = desc.dma_addr().as_u64();
639 let desc_end = desc_dma
640 .checked_add(desc_bytes as u64)
641 .ok_or(Error::InvalidArgument)?;
642 if transfer_end > u32::MAX as u64 + 1
643 || desc_end > u32::MAX as u64 + 1
644 || desc.len() < block_count as usize
645 {
646 return Err(Error::InvalidArgument);
647 }
648
649 desc.write_with_cpu(block_count as usize, |descs| {
650 for (index, desc) in descs.iter_mut().enumerate() {
651 let last = index + 1 == block_count as usize;
652 let next = if last {
653 0
654 } else {
655 (desc_dma as u32) + ((index + 1) * IDMAC_DESC_SIZE) as u32
656 };
657 *desc = IdmacDesc::chained(
658 (buffer_dma as u32) + (index * BLOCK_SIZE) as u32,
659 BLOCK_SIZE as u32,
660 next,
661 index == 0,
662 last,
663 );
664 }
665 });
666
667 self.clear_all_int_status();
668 self.irq_pending_status = 0;
669 self.program_data_phase(BLOCK_SIZE as u32, block_count);
670 self.reset_dma_for_phase(phase)?;
671
672 self.regs.dbaddr().write(desc_dma as u32);
673 self.regs.ctrl().update(|r| {
674 r.with_use_internal_dmac(true)
675 .with_dma_enable(true)
676 .with_int_enable(self.completion_irq_enabled)
677 });
678 self.regs.bmod().write(BMOD_FB | BMOD_DE);
679 self.regs.pldmnd().write(1);
680
681 self.pending_data = Some(PendingData {
682 direction,
683 block_size: BLOCK_SIZE as u32,
684 block_count,
685 });
686 self.data_blocks_remaining = block_count;
687 match self.submit_command(cmd) {
688 Ok(()) => Ok(()),
689 Err(err) => {
690 self.disable_idmac();
691 self.recover_after_idmac_error(phase);
692 self.clear_all_int_status();
693 Err(err)
694 }
695 }
696 }
697
698 fn finish_block_request(&mut self, request: BlockRequest) -> Result<(), Error> {
699 match request.inner {
700 BlockRequestKind::FifoRead { .. } | BlockRequestKind::FifoWrite { .. } => {
701 self.pending_data = None;
702 self.data_blocks_remaining = 0;
703 self.data_cmd_index = 0;
704 }
705 BlockRequestKind::Read { stage, .. } => {
706 if stage == BlockRequestStage::Command {
707 let _ = self.take_command_response();
708 }
709 self.disable_idmac();
710 self.clear_all_int_status();
711 self.pending_data = None;
712 self.data_blocks_remaining = 0;
713 self.data_cmd_index = 0;
714 }
715 BlockRequestKind::Write { stage, .. } => {
716 if stage == BlockRequestStage::Command {
717 let _ = self.take_command_response();
718 }
719 self.disable_idmac();
720 self.clear_all_int_status();
721 self.pending_data = None;
722 self.data_blocks_remaining = 0;
723 self.data_cmd_index = 0;
724 }
725 }
726 Ok(())
727 }
728
729 fn finish_dma_data(
730 &mut self,
731 request: &mut Option<BlockRequest>,
732 id: RequestId,
733 slot: &mut BlockRequestSlot,
734 ) -> Result<DataCommandPoll, Error> {
735 let Some(active) = request.as_mut() else {
736 return Err(Error::InvalidArgument);
737 };
738 let stop_after_complete = match &mut active.inner {
739 BlockRequestKind::Read {
740 map,
741 stage,
742 stop_after_complete,
743 ..
744 } => {
745 map.complete_for_cpu_all();
746 *stage = BlockRequestStage::Stop;
747 *stop_after_complete
748 }
749 BlockRequestKind::Write {
750 stage,
751 stop_after_complete,
752 ..
753 } => {
754 *stage = BlockRequestStage::Stop;
755 *stop_after_complete
756 }
757 BlockRequestKind::FifoRead { .. } | BlockRequestKind::FifoWrite { .. } => {
758 return Err(Error::InvalidArgument);
759 }
760 };
761
762 if stop_after_complete {
763 self.submit_command(&CMD12)?;
764 return Ok(DataCommandPoll::Pending);
765 }
766
767 let active = request.take().ok_or(Error::InvalidArgument)?;
768 let response = active.response().ok_or(Error::InvalidArgument)?;
769 self.finish_block_request(active)?;
770 slot.complete(id)?;
771 Ok(DataCommandPoll::Complete(response))
772 }
773
774 fn poll_block_stop(
775 &mut self,
776 request: &mut Option<BlockRequest>,
777 id: RequestId,
778 slot: &mut BlockRequestSlot,
779 phase: Phase,
780 ) -> Result<DataCommandPoll, Error> {
781 match self.poll_command() {
782 Ok(CommandPoll::Pending) => Ok(DataCommandPoll::Pending),
783 Ok(CommandPoll::Complete) => {
784 let _ = self.take_command_response()?;
785 let active = request.take().ok_or(Error::InvalidArgument)?;
786 let response = active.response().ok_or(Error::InvalidArgument)?;
787 self.finish_block_request(active)?;
788 slot.complete(id)?;
789 Ok(DataCommandPoll::Complete(response))
790 }
791 Ok(_) => Ok(DataCommandPoll::Pending),
793 Err(err) => {
794 self.abort_block_request(request, id, slot, phase);
795 Err(err)
796 }
797 }
798 }
799
800 fn poll_fifo_request(
801 &mut self,
802 request: &mut Option<BlockRequest>,
803 id: RequestId,
804 slot: &mut BlockRequestSlot,
805 ) -> Result<DataCommandPoll, Error> {
806 let (cmd_index, phase, stage) = match request.as_ref().map(|request| &request.inner) {
807 Some(BlockRequestKind::FifoRead {
808 cmd_index,
809 phase,
810 stage,
811 ..
812 })
813 | Some(BlockRequestKind::FifoWrite {
814 cmd_index,
815 phase,
816 stage,
817 ..
818 }) => (*cmd_index, *phase, *stage),
819 _ => return Err(Error::InvalidArgument),
820 };
821
822 if stage == BlockRequestStage::Command {
823 match self.poll_command() {
824 Ok(CommandPoll::Pending) => return Ok(DataCommandPoll::Pending),
825 Ok(CommandPoll::Complete) => {
826 let response = self.take_command_response()?;
827 if let Some(active) = request.as_mut() {
828 match &mut active.inner {
829 BlockRequestKind::FifoRead {
830 response: stored_response,
831 ..
832 }
833 | BlockRequestKind::FifoWrite {
834 response: stored_response,
835 ..
836 } => *stored_response = Some(response),
837 _ => return Err(Error::InvalidArgument),
838 }
839 }
840 set_fifo_stage(request, BlockRequestStage::Data)?;
841 return Ok(DataCommandPoll::Pending);
842 }
843 Ok(_) => return Ok(DataCommandPoll::Pending),
845 Err(err) => {
846 self.abort_block_request(request, id, slot, phase);
847 return Err(err);
848 }
849 }
850 }
851
852 let stage = match request.as_ref().map(|request| &request.inner) {
853 Some(BlockRequestKind::FifoRead { stage, .. })
854 | Some(BlockRequestKind::FifoWrite { stage, .. }) => *stage,
855 _ => return Err(Error::InvalidArgument),
856 };
857 if stage == BlockRequestStage::Stop {
858 return self.poll_block_stop(request, id, slot, phase);
859 }
860
861 match self.poll_fifo_data_step(request, cmd_index, phase) {
862 Ok(BlockPoll::Pending) => Ok(DataCommandPoll::Pending),
863 Ok(BlockPoll::Complete) => self.finish_fifo_data(request, id, slot),
864 Ok(_) => Ok(DataCommandPoll::Pending),
866 Err(err) => {
867 self.abort_block_request(request, id, slot, phase);
868 Err(err)
869 }
870 }
871 }
872
873 fn poll_fifo_data_step(
874 &mut self,
875 request: &mut Option<BlockRequest>,
876 cmd_index: u8,
877 phase: Phase,
878 ) -> Result<BlockPoll, Error> {
879 let Some(active) = request.as_mut() else {
880 return Err(Error::InvalidArgument);
881 };
882 match &mut active.inner {
883 BlockRequestKind::FifoRead {
884 buffer,
885 len,
886 block_size,
887 offset,
888 ..
889 } => poll_fifo_read_step(self, *buffer, *len, *block_size, offset, cmd_index, phase),
890 BlockRequestKind::FifoWrite {
891 buffer,
892 len,
893 block_size,
894 offset,
895 ..
896 } => poll_fifo_write_step(self, *buffer, *len, *block_size, offset, cmd_index, phase),
897 _ => Err(Error::InvalidArgument),
898 }
899 }
900
901 fn finish_fifo_data(
902 &mut self,
903 request: &mut Option<BlockRequest>,
904 id: RequestId,
905 slot: &mut BlockRequestSlot,
906 ) -> Result<DataCommandPoll, Error> {
907 let Some(active) = request.as_mut() else {
908 return Err(Error::InvalidArgument);
909 };
910 let stop_after_complete = match &mut active.inner {
911 BlockRequestKind::FifoRead {
912 stage,
913 stop_after_complete,
914 ..
915 }
916 | BlockRequestKind::FifoWrite {
917 stage,
918 stop_after_complete,
919 ..
920 } => {
921 *stage = BlockRequestStage::Stop;
922 *stop_after_complete
923 }
924 _ => return Err(Error::InvalidArgument),
925 };
926 if stop_after_complete {
927 self.submit_command(&CMD12)?;
928 return Ok(DataCommandPoll::Pending);
929 }
930
931 let active = request.take().ok_or(Error::InvalidArgument)?;
932 let response = active.response().ok_or(Error::InvalidArgument)?;
933 self.finish_block_request(active)?;
934 self.pending_data = None;
935 self.data_blocks_remaining = 0;
936 self.data_cmd_index = 0;
937 slot.complete(id)?;
938 Ok(DataCommandPoll::Complete(response))
939 }
940
941 fn abort_block_request(
942 &mut self,
943 request: &mut Option<BlockRequest>,
944 id: RequestId,
945 slot: &mut BlockRequestSlot,
946 phase: Phase,
947 ) {
948 let _ = request.take();
949 self.disable_idmac();
950 self.recover_after_idmac_error(phase);
951 self.clear_all_int_status();
952 let _ = slot.complete(id);
953 }
954
955 fn disable_idmac(&self) {
956 self.regs.ctrl().update(|r| {
957 r.with_use_internal_dmac(false)
958 .with_dma_enable(false)
959 .with_int_enable(false)
960 });
961 self.regs.bmod().write(0);
962 }
963
964 fn recover_after_idmac_error(&mut self, phase: Phase) {
965 let status = self.regs.status().read().into_bits();
966 let rintsts = self.regs.rintsts().read();
967 warn!(
968 "dwmmc: IDMAC {:?} error state rintsts={:#010x} status={:#010x} tcbcnt={} tbbcnt={}",
969 phase,
970 rintsts.into_bits(),
971 status,
972 self.regs.tcbcnt().read(),
973 self.regs.tbbcnt().read()
974 );
975
976 self.regs.ctrl().update(|r| r.with_abort_read_data(true));
977 let _ = self.regs.ctrl().read();
978 let _ = self.reset_fifo();
979 let _ = self.reset_dma();
980 self.regs.ctrl().update(|r| r.with_abort_read_data(false));
981 self.pending_data = None;
982 self.data_blocks_remaining = 0;
983 self.data_cmd_index = 0;
984 }
985
986 fn reset_dma(&self) -> Result<(), Error> {
987 self.reset_dma_for_phase(Phase::DataRead)
988 }
989
990 fn reset_dma_for_phase(&self, phase: Phase) -> Result<(), Error> {
991 self.regs.ctrl().update(|r| r.with_dma_reset(true));
992 for _ in 0..DMA_POLL_LIMIT {
993 if !self.regs.ctrl().read().dma_reset() {
994 self.regs.bmod().write(BMOD_SWR);
995 for _ in 0..DMA_POLL_LIMIT {
996 if self.regs.bmod().read() & BMOD_SWR == 0 {
997 return Ok(());
998 }
999 core::hint::spin_loop();
1000 }
1001 break;
1002 }
1003 core::hint::spin_loop();
1004 }
1005 Err(Error::Timeout(ErrorContext::new(phase)))
1006 }
1007
1008 fn poll_dma_complete(&mut self, cmd_index: u8, phase: Phase) -> Result<BlockPoll, Error> {
1009 let raw_status = self.take_data_irq_status();
1010 let rintsts = crate::regs::RIntSts::from_bits(raw_status);
1011 if rintsts.error() {
1012 return Err(self.translate_int_error(rintsts, phase, cmd_index));
1013 }
1014 if rintsts.data_transfer_over() {
1015 return Ok(BlockPoll::Complete);
1016 }
1017 Ok(BlockPoll::Pending)
1018 }
1019
1020 fn take_data_irq_status(&mut self) -> u32 {
1021 let raw_status = self.regs.rintsts().read().into_bits();
1022 let consume = raw_status
1023 & (crate::DWMMC_INT_DATA_TRANSFER_OVER
1024 | crate::DWMMC_INT_COMMAND_DONE
1025 | crate::DWMMC_INT_ERROR_MASK);
1026 if consume != 0 {
1027 self.regs
1028 .rintsts()
1029 .write(crate::regs::RIntSts::from_bits(consume));
1030 }
1031 let status = self.irq_pending_status | raw_status;
1032 self.irq_pending_status &= !(crate::DWMMC_INT_DATA_TRANSFER_OVER
1033 | crate::DWMMC_INT_COMMAND_DONE
1034 | crate::DWMMC_INT_ERROR_MASK);
1035 status
1036 }
1037}
1038
1039fn set_fifo_stage(
1040 request: &mut Option<BlockRequest>,
1041 next: BlockRequestStage,
1042) -> Result<(), Error> {
1043 let Some(active) = request.as_mut() else {
1044 return Err(Error::InvalidArgument);
1045 };
1046 match &mut active.inner {
1047 BlockRequestKind::FifoRead { stage, .. } | BlockRequestKind::FifoWrite { stage, .. } => {
1048 *stage = next;
1049 Ok(())
1050 }
1051 _ => Err(Error::InvalidArgument),
1052 }
1053}
1054
1055fn poll_fifo_read_step(
1056 host: &mut DwMmc,
1057 buffer: NonNull<u8>,
1058 len: usize,
1059 _block_size: usize,
1060 offset: &mut usize,
1061 cmd_index: u8,
1062 phase: Phase,
1063) -> Result<BlockPoll, Error> {
1064 let raw_status = host.take_data_irq_status();
1065 let rintsts = crate::regs::RIntSts::from_bits(raw_status);
1066 if rintsts.error() {
1067 let err = host.translate_int_error(rintsts, phase, cmd_index);
1068 let _ = host.reset_fifo();
1069 return Err(err);
1070 }
1071
1072 let fifo = host.fifo_ptr();
1073 let mut status = host.regs.status().read();
1074 while *offset < len && status.fifo_count() >= 2 {
1075 let value = unsafe { fifo.read_volatile() };
1076 let end = (*offset + 8).min(len);
1077 let block =
1078 unsafe { core::slice::from_raw_parts_mut(buffer.as_ptr().add(*offset), end - *offset) };
1079 block.copy_from_slice(&value.to_le_bytes()[..block.len()]);
1080 *offset = end;
1081 status = host.regs.status().read();
1082 }
1083
1084 if *offset >= len && rintsts.data_transfer_over() {
1085 return Ok(BlockPoll::Complete);
1086 }
1087 Ok(BlockPoll::Pending)
1088}
1089
1090fn poll_fifo_write_step(
1091 host: &mut DwMmc,
1092 buffer: NonNull<u8>,
1093 len: usize,
1094 _block_size: usize,
1095 offset: &mut usize,
1096 cmd_index: u8,
1097 phase: Phase,
1098) -> Result<BlockPoll, Error> {
1099 let raw_status = host.take_data_irq_status();
1100 let rintsts = crate::regs::RIntSts::from_bits(raw_status);
1101 if rintsts.error() {
1102 let err = host.translate_int_error(rintsts, phase, cmd_index);
1103 let _ = host.reset_fifo();
1104 return Err(err);
1105 }
1106
1107 let fifo = host.fifo_ptr();
1108 while *offset < len && !host.regs.status().read().fifo_full() {
1109 let end = (*offset + 8).min(len);
1110 let block =
1111 unsafe { core::slice::from_raw_parts(buffer.as_ptr().add(*offset), end - *offset) };
1112 let mut bytes = [0u8; 8];
1113 bytes[..block.len()].copy_from_slice(block);
1114 unsafe { fifo.write_volatile(u64::from_le_bytes(bytes)) };
1115 *offset = end;
1116 }
1117
1118 if *offset >= len && rintsts.data_transfer_over() {
1119 return Ok(BlockPoll::Complete);
1120 }
1121 Ok(BlockPoll::Pending)
1122}
1123
1124fn dma_read_block_count(size: NonZeroUsize) -> Result<u32, Error> {
1125 let len = size.get();
1126 if !len.is_multiple_of(BLOCK_SIZE) {
1127 return Err(Error::Misaligned);
1128 }
1129 let blocks = len / BLOCK_SIZE;
1130 u32::try_from(blocks).map_err(|_| Error::InvalidArgument)
1131}
1132
1133fn dma_write_block_count(size: NonZeroUsize) -> Result<u32, Error> {
1134 dma_read_block_count(size)
1135}
1136
1137fn map_dma_error(err: dma_api::DmaError, phase: Phase) -> Error {
1138 match err {
1139 dma_api::DmaError::NoMemory => Error::BusError(ErrorContext::new(phase)),
1140 dma_api::DmaError::LayoutError(_)
1141 | dma_api::DmaError::DmaMaskNotMatch { .. }
1142 | dma_api::DmaError::AlignMismatch { .. }
1143 | dma_api::DmaError::SegmentTooLarge { .. }
1144 | dma_api::DmaError::BoundaryCross { .. }
1145 | dma_api::DmaError::NullPointer
1146 | dma_api::DmaError::ZeroSizedBuffer => Error::InvalidArgument,
1147 }
1148}
1149
1150#[cfg(test)]
1151mod tests {
1152 use super::*;
1153
1154 #[test]
1155 fn first_descriptor_sets_owned_chained_first_read_buffer() {
1156 let desc = IdmacDesc::chained(0x1234_5000, 512, 0x2000, true, false);
1157
1158 assert_eq!(desc.des0, DESC_OWN | DESC_CH | DESC_FS | DESC_DIC);
1159 assert_eq!(desc.des1, 512);
1160 assert_eq!(desc.des2, 0x1234_5000);
1161 assert_eq!(desc.des3, 0x2000);
1162 }
1163
1164 #[test]
1165 fn last_descriptor_sets_last_and_terminates_chain() {
1166 let desc = IdmacDesc::chained(0x1234_5200, 512, 0, false, true);
1167
1168 assert_eq!(desc.des0, DESC_OWN | DESC_CH | DESC_LD | DESC_DIC);
1169 assert_eq!(desc.des1, 512);
1170 assert_eq!(desc.des2, 0x1234_5200);
1171 assert_eq!(desc.des3, 0);
1172 }
1173
1174 #[test]
1175 fn dma_read_plan_rejects_non_block_sized_buffers() {
1176 let size = NonZeroUsize::new(513).unwrap();
1177
1178 assert_eq!(dma_read_block_count(size), Err(Error::Misaligned));
1179 }
1180
1181 #[test]
1182 fn dma_read_plan_reports_block_count() {
1183 let size = NonZeroUsize::new(1024).unwrap();
1184
1185 assert_eq!(dma_read_block_count(size), Ok(2));
1186 }
1187
1188 #[test]
1189 fn dma_write_plan_rejects_non_block_sized_buffers() {
1190 let size = NonZeroUsize::new(513).unwrap();
1191
1192 assert_eq!(dma_write_block_count(size), Err(Error::Misaligned));
1193 }
1194
1195 #[test]
1196 fn block_request_slot_rejects_second_request_until_completed() {
1197 let mut slot = BlockRequestSlot::default();
1198 let first = slot
1199 .start(BlockTransferMode::Dma, BlockTransferDirection::Read)
1200 .unwrap();
1201
1202 assert_eq!(
1203 slot.start(BlockTransferMode::Dma, BlockTransferDirection::Read),
1204 Err(Error::UnsupportedCommand)
1205 );
1206 assert_eq!(
1207 slot.complete(RequestId::new(usize::from(first) + 1)),
1208 Err(Error::InvalidArgument)
1209 );
1210 assert_eq!(slot.complete(first), Ok(()));
1211 assert!(
1212 slot.start(BlockTransferMode::Dma, BlockTransferDirection::Read)
1213 .is_ok()
1214 );
1215 }
1216
1217 #[test]
1218 fn block_request_can_cross_queue_thread_boundary() {
1219 fn assert_send<T: Send>() {}
1220
1221 assert_send::<BlockRequest>();
1222 assert_send::<BlockRequestSlot>();
1223 }
1224}