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