1#![no_std]
72#![allow(clippy::missing_safety_doc)]
73
74use core::{marker::PhantomData, num::NonZeroUsize, ptr::NonNull};
75
76mod command;
77mod dma;
78mod host;
79mod regs;
80
81pub use dma::{ADMA2_DESC_ALIGN, ADMA2_DESC_COUNT, BlockRequest, BlockRequestSlot, RequestId};
82pub use host::{HostClock, Sdhci};
83pub use sdmmc_protocol::block::{
84 BlockBufferConfig, BlockPoll, BlockRequestId, BlockTransferDirection, BlockTransferMode,
85 BlockTransferState,
86};
87use sdmmc_protocol::{
88 DataCommandPoll,
89 cmd::{Command, DataDirection},
90 error::{Error, ErrorContext, Phase},
91 sdio::{
92 BusWidth, ClockSpeed, HostEvent, HostEventKind, HostEventSource, SdioHost, SignalVoltage,
93 },
94};
95
96use crate::regs::*;
97
98#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
100pub enum Event {
101 #[default]
103 None,
104 CommandComplete,
106 TransferComplete,
108 Error { normal: u16, error: u16 },
110 Other { normal: u16, error: u16 },
112}
113
114pub struct DataRequest<'a> {
115 id: RequestId,
116 request: Option<BlockRequest>,
117 slot: BlockRequestSlot,
118 _buffer: PhantomData<&'a [u8]>,
119}
120
121impl SdioHost for Sdhci {
122 type Event = Event;
123 type DataRequest<'a> = DataRequest<'a>;
124
125 fn submit_command(&mut self, cmd: &Command) -> Result<(), Error> {
126 Sdhci::submit_command(self, cmd)
127 }
128
129 fn poll_command_response(&mut self) -> Result<sdmmc_protocol::CommandResponsePoll, Error> {
130 Sdhci::poll_command_response(self)
131 }
132
133 fn submit_read_data<'a>(
134 &mut self,
135 cmd: &Command,
136 buf: &'a mut [u8],
137 block_size: u32,
138 block_count: u32,
139 ) -> Result<Self::DataRequest<'a>, Error> {
140 let buffer = NonNull::new(buf.as_mut_ptr()).ok_or(Error::InvalidArgument)?;
141 let mut slot = BlockRequestSlot::default();
142 let request = submit_read_with_dma_fifo_fallback(
143 self,
144 cmd,
145 buffer,
146 buf.len(),
147 block_size,
148 block_count,
149 &mut slot,
150 )?;
151 let id = request.id();
152 Ok(DataRequest {
153 id,
154 request: Some(request),
155 slot,
156 _buffer: PhantomData,
157 })
158 }
159
160 fn submit_write_data<'a>(
161 &mut self,
162 cmd: &Command,
163 buf: &'a [u8],
164 block_size: u32,
165 block_count: u32,
166 ) -> Result<Self::DataRequest<'a>, Error> {
167 let buffer = NonNull::new(buf.as_ptr() as *mut u8).ok_or(Error::InvalidArgument)?;
168 let mut slot = BlockRequestSlot::default();
169 let request = submit_write_with_dma_fifo_fallback(
170 self,
171 cmd,
172 buffer,
173 buf.len(),
174 block_size,
175 block_count,
176 &mut slot,
177 )?;
178 let id = request.id();
179 Ok(DataRequest {
180 id,
181 request: Some(request),
182 slot,
183 _buffer: PhantomData,
184 })
185 }
186
187 fn poll_data_request<'a>(
188 &mut self,
189 request: &mut Self::DataRequest<'a>,
190 ) -> Result<DataCommandPoll, Error> {
191 self.poll_block_request_response(&mut request.request, request.id, &mut request.slot)
192 }
193
194 fn set_bus_width(&mut self, width: BusWidth) -> Result<(), Error> {
195 let mut ctrl = self.read_u8(REG_HOST_CONTROL1);
196 ctrl &= !(HOST_CTRL1_4BIT | HOST_CTRL1_8BIT);
197 match width {
198 BusWidth::Bit1 => {}
199 BusWidth::Bit4 => ctrl |= HOST_CTRL1_4BIT,
200 BusWidth::Bit8 => return Err(Error::UnsupportedCommand),
205 _ => return Err(Error::UnsupportedCommand),
207 }
208 self.write_u8(REG_HOST_CONTROL1, ctrl);
209 Ok(())
210 }
211
212 fn set_clock(&mut self, speed: ClockSpeed) -> Result<(), Error> {
213 let target_hz = match speed {
214 ClockSpeed::Identification => 400_000,
215 ClockSpeed::Default | ClockSpeed::Sdr12 => 25_000_000,
216 ClockSpeed::HighSpeed | ClockSpeed::Sdr25 => 50_000_000,
217 ClockSpeed::Sdr50 | ClockSpeed::Ddr50 => 50_000_000,
218 ClockSpeed::Sdr104 => 104_000_000,
219 ClockSpeed::Hs200 => 200_000_000,
220 _ => return Err(Error::UnsupportedCommand),
222 };
223
224 let mut ctrl = self.read_u8(REG_HOST_CONTROL1);
228 if matches!(
229 speed,
230 ClockSpeed::Identification | ClockSpeed::Default | ClockSpeed::Sdr12
231 ) {
232 ctrl &= !HOST_CTRL1_HIGH_SPEED;
233 } else {
234 ctrl |= HOST_CTRL1_HIGH_SPEED;
235 }
236 self.write_u8(REG_HOST_CONTROL1, ctrl);
237
238 if let Some(cb) = self.ext_clock {
241 self.disable_sd_clock();
242 cb.set_clock(target_hz)?;
243 return self.enable_clock_external();
244 }
245
246 let base = self.base_clock_hz();
247 if base == 0 {
248 return Err(Error::BadResponse(ErrorContext::new(Phase::Init)));
249 }
250 self.enable_clock(base, target_hz)
251 }
252
253 fn switch_voltage(&mut self, voltage: SignalVoltage) -> Result<(), Error> {
254 if matches!(voltage, SignalVoltage::V180) && !self.support_1v8 {
269 return Err(Error::UnsupportedCommand);
270 }
271
272 self.disable_sd_clock();
273
274 let mut ctrl2 = self.read_u16(REG_HOST_CONTROL2);
278 match voltage {
279 SignalVoltage::V330 => {
280 ctrl2 &= !HOST_CTRL2_1V8_SIGNALING;
281 self.set_power(POWER_330);
282 }
283 SignalVoltage::V180 => {
284 ctrl2 |= HOST_CTRL2_1V8_SIGNALING;
285 self.set_power(POWER_180);
286 }
287 SignalVoltage::V120 => return Err(Error::UnsupportedCommand),
288 _ => return Err(Error::UnsupportedCommand),
290 }
291 self.write_u16(REG_HOST_CONTROL2, ctrl2);
292
293 let cur = self.read_u16(REG_CLOCK_CONTROL);
297 self.write_u16(REG_CLOCK_CONTROL, cur | CLOCK_SD_ENABLE);
298
299 Ok(())
305 }
306
307 fn execute_tuning(&mut self, cmd_index: u8) -> Result<(), Error> {
308 if cmd_index != 19 && cmd_index != 21 {
312 return Err(Error::InvalidArgument);
313 }
314
315 let block_size: u16 =
320 if cmd_index == 21 && self.read_u8(REG_HOST_CONTROL1) & HOST_CTRL1_8BIT != 0 {
321 128
322 } else {
323 64
324 };
325
326 self.write_u16(REG_BLOCK_SIZE, block_size & 0x0FFF);
330 self.write_u16(REG_BLOCK_COUNT, 1);
331 self.write_u8(REG_TIMEOUT_CONTROL, 0x0E);
332 self.write_u16(
334 REG_TRANSFER_MODE,
335 XFER_MODE_BLOCK_COUNT_ENABLE | XFER_MODE_READ,
336 );
337
338 let mut ctrl2 = self.read_u16(REG_HOST_CONTROL2);
344 ctrl2 |= HOST_CTRL2_EXECUTE_TUNING;
345 self.write_u16(REG_HOST_CONTROL2, ctrl2);
346
347 const TUNING_POLLS: u32 = 1_000_000;
351 let mut last_status = 0u16;
352 for _ in 0..TUNING_POLLS {
353 last_status = self.read_u16(REG_HOST_CONTROL2);
354 if last_status & HOST_CTRL2_EXECUTE_TUNING == 0 {
355 if last_status & HOST_CTRL2_SAMPLING_CLOCK_SELECT != 0 {
358 return Ok(());
359 }
360 return Err(Error::BadResponse(ErrorContext::for_cmd(
361 Phase::Init,
362 cmd_index,
363 )));
364 }
365 core::hint::spin_loop();
366 }
367
368 let cleared = last_status & !HOST_CTRL2_EXECUTE_TUNING;
371 self.write_u16(REG_HOST_CONTROL2, cleared);
372 Err(Error::Timeout(ErrorContext::for_cmd(
373 Phase::Init,
374 cmd_index,
375 )))
376 }
377
378 fn enable_completion_irq(&mut self) -> Result<(), Error> {
379 Sdhci::enable_completion_irq(self);
380 Ok(())
381 }
382
383 fn disable_completion_irq(&mut self) -> Result<(), Error> {
384 Sdhci::disable_completion_irq(self);
385 Ok(())
386 }
387
388 fn handle_irq(&mut self) -> Self::Event {
389 Sdhci::handle_irq(self)
390 }
391}
392
393fn submit_read_with_dma_fifo_fallback(
394 host: &mut Sdhci,
395 cmd: &Command,
396 buffer: NonNull<u8>,
397 len: usize,
398 block_size: u32,
399 block_count: u32,
400 slot: &mut BlockRequestSlot,
401) -> Result<BlockRequest, Error> {
402 if should_try_dma(cmd, block_size, block_count, len, DataDirection::Read)
403 && let Some(dma) = host.dma.clone()
404 {
405 match host.submit_read_blocks(
406 cmd.arg,
407 buffer,
408 NonZeroUsize::new(len).ok_or(Error::InvalidArgument)?,
409 Some(&dma),
410 BlockTransferMode::Dma,
411 slot,
412 ) {
413 Ok(request) => return Ok(request),
414 Err(err) if can_fallback_to_fifo(err) => {}
415 Err(err) => return Err(err),
416 }
417 }
418
419 host.submit_fifo_data_request(
420 cmd,
421 buffer,
422 len,
423 block_size,
424 block_count,
425 DataDirection::Read,
426 slot,
427 )
428}
429
430fn submit_write_with_dma_fifo_fallback(
431 host: &mut Sdhci,
432 cmd: &Command,
433 buffer: NonNull<u8>,
434 len: usize,
435 block_size: u32,
436 block_count: u32,
437 slot: &mut BlockRequestSlot,
438) -> Result<BlockRequest, Error> {
439 if should_try_dma(cmd, block_size, block_count, len, DataDirection::Write)
440 && let Some(dma) = host.dma.clone()
441 {
442 match host.submit_write_blocks(
443 cmd.arg,
444 buffer,
445 NonZeroUsize::new(len).ok_or(Error::InvalidArgument)?,
446 Some(&dma),
447 BlockTransferMode::Dma,
448 slot,
449 ) {
450 Ok(request) => return Ok(request),
451 Err(err) if can_fallback_to_fifo(err) => {}
452 Err(err) => return Err(err),
453 }
454 }
455
456 host.submit_fifo_data_request(
457 cmd,
458 buffer,
459 len,
460 block_size,
461 block_count,
462 DataDirection::Write,
463 slot,
464 )
465}
466
467fn should_try_dma(
468 cmd: &Command,
469 block_size: u32,
470 block_count: u32,
471 len: usize,
472 direction: DataDirection,
473) -> bool {
474 block_size == 512
475 && len == block_count as usize * 512
476 && matches!(
477 (direction, cmd.cmd),
478 (DataDirection::Read, 17 | 18) | (DataDirection::Write, 24 | 25)
479 )
480}
481
482fn can_fallback_to_fifo(err: Error) -> bool {
483 matches!(
484 err,
485 Error::UnsupportedCommand | Error::InvalidArgument | Error::Misaligned
486 )
487}
488
489pub(crate) fn event_from_status(normal: u16, error: u16) -> Event {
490 if normal & NORMAL_INT_ERROR != 0 {
491 Event::Error { normal, error }
492 } else if normal & NORMAL_INT_CMD_COMPLETE != 0 {
493 Event::CommandComplete
494 } else if normal & NORMAL_INT_XFER_COMPLETE != 0 {
495 Event::TransferComplete
496 } else if normal != 0 || error != 0 {
497 Event::Other { normal, error }
498 } else {
499 Event::None
500 }
501}
502
503impl HostEvent for Event {
504 fn kind(&self) -> HostEventKind {
505 match self {
506 Event::None => HostEventKind::None,
507 Event::CommandComplete => HostEventKind::CommandComplete,
508 Event::TransferComplete => HostEventKind::TransferComplete,
509 Event::Error { .. } => HostEventKind::Error,
510 Event::Other { .. } => HostEventKind::Other,
511 }
512 }
513
514 fn source(&self) -> HostEventSource {
515 match self {
516 Event::CommandComplete => HostEventSource::Command,
517 Event::TransferComplete => HostEventSource::Data,
518 Event::None | Event::Error { .. } | Event::Other { .. } => HostEventSource::Controller,
519 }
520 }
521
522 fn queue_id(&self) -> Option<BlockRequestId> {
523 match self {
524 Event::TransferComplete => Some(BlockRequestId::new(0)),
525 Event::None | Event::CommandComplete | Event::Error { .. } | Event::Other { .. } => {
526 None
527 }
528 }
529 }
530}
531
532impl Sdhci {
533 pub fn block_buffer_config(&self, mode: BlockTransferMode) -> BlockBufferConfig {
534 match mode {
535 BlockTransferMode::Fifo => {
536 BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 1, None)
537 }
538 BlockTransferMode::Dma => {
539 BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 512, Some(self.dma_mask))
540 }
541 _ => BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 1, None),
543 }
544 }
545
546 pub fn handle_irq(&mut self) -> Event {
549 let normal = self.read_u16(REG_NORMAL_INT_STATUS);
550 let error = if normal & NORMAL_INT_ERROR != 0 {
551 self.read_u16(REG_ERROR_INT_STATUS)
552 } else {
553 0
554 };
555
556 if normal != 0 {
557 self.write_u16(REG_NORMAL_INT_STATUS, normal);
558 }
559 if error != 0 {
560 self.write_u16(REG_ERROR_INT_STATUS, error);
561 }
562 self.irq_pending_normal |= normal;
563 self.irq_pending_error |= error;
564
565 event_from_status(normal, error)
566 }
567}
568
569#[cfg(test)]
570mod tests {
571 use super::*;
572
573 #[test]
574 fn event_reports_command_completion_without_os_wakeup_policy() {
575 assert_eq!(
576 event_from_status(NORMAL_INT_CMD_COMPLETE, 0),
577 Event::CommandComplete
578 );
579 }
580
581 #[test]
582 fn event_reports_data_completion_without_os_wakeup_policy() {
583 assert_eq!(
584 event_from_status(NORMAL_INT_XFER_COMPLETE, 0),
585 Event::TransferComplete
586 );
587 }
588
589 #[test]
590 fn event_reports_error_status_without_translating_to_os_action() {
591 assert_eq!(
592 event_from_status(NORMAL_INT_ERROR, ERROR_INT_DATA_TIMEOUT),
593 Event::Error {
594 normal: NORMAL_INT_ERROR,
595 error: ERROR_INT_DATA_TIMEOUT,
596 }
597 );
598 }
599
600 #[test]
601 fn event_reports_data_completion_source_for_runtime_wakeup() {
602 use sdmmc_protocol::sdio::{HostEvent, HostEventKind, HostEventSource};
603
604 let event = event_from_status(NORMAL_INT_XFER_COMPLETE, 0);
605
606 assert_eq!(event.kind(), HostEventKind::TransferComplete);
607 assert_eq!(event.source(), HostEventSource::Data);
608 assert_eq!(event.queue_id(), Some(BlockRequestId::new(0)));
609 }
610
611 #[test]
612 fn exposes_block_buffer_constraints() {
613 let host = unsafe { Sdhci::new_from_addr(0x1000_0000) };
614
615 let dma = host.block_buffer_config(BlockTransferMode::Dma);
616 assert_eq!(dma.block_size.get(), 512);
617 assert_eq!(dma.align, 512);
618 assert_eq!(dma.dma_mask, Some(u32::MAX as u64));
619 }
620}