1#![no_std]
56#![allow(clippy::missing_safety_doc)]
57
58use core::{marker::PhantomData, num::NonZeroUsize, ptr::NonNull};
59
60mod command;
61mod dma;
62mod host;
63mod regs;
64
65pub use sdmmc_protocol::block::{
66 BlockBufferConfig, BlockPoll, BlockRequestId, BlockTransferDirection, BlockTransferMode,
67 BlockTransferState,
68};
69use sdmmc_protocol::{
70 DataCommandPoll,
71 cmd::{Command, DataDirection},
72 error::Error,
73 sdio::{
74 BusWidth, ClockSpeed, HostEvent, HostEventKind, HostEventSource, SdioHost, SignalVoltage,
75 },
76};
77
78use crate::regs::RegisterBlockVolatileFieldAccess;
79pub use crate::{
80 dma::{BlockRequest, BlockRequestSlot, IDMAC_DESC_ALIGN, IDMAC_DESC_SIZE, RequestId},
81 host::{DEFAULT_FIFO_OFFSET, DwMmc},
82};
83
84#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
86pub enum Event {
87 #[default]
89 None,
90 CommandComplete,
92 TransferComplete,
94 ReceiveReady,
96 TransmitReady,
98 Error { raw_status: u32 },
100 Other { raw_status: u32 },
102}
103
104pub struct DataRequest<'a> {
105 id: RequestId,
106 request: Option<BlockRequest>,
107 slot: BlockRequestSlot,
108 _buffer: PhantomData<&'a [u8]>,
109}
110
111pub(crate) const DWMMC_INT_RESPONSE_ERROR: u32 = 1 << 1;
112pub(crate) const DWMMC_INT_COMMAND_DONE: u32 = 1 << 2;
113pub(crate) const DWMMC_INT_DATA_TRANSFER_OVER: u32 = 1 << 3;
114pub(crate) const DWMMC_INT_TXDR: u32 = 1 << 4;
115pub(crate) const DWMMC_INT_RXDR: u32 = 1 << 5;
116pub(crate) const DWMMC_INT_RESPONSE_CRC_ERROR: u32 = 1 << 6;
117pub(crate) const DWMMC_INT_DATA_CRC_ERROR: u32 = 1 << 7;
118pub(crate) const DWMMC_INT_RESPONSE_TIMEOUT: u32 = 1 << 8;
119pub(crate) const DWMMC_INT_DATA_READ_TIMEOUT: u32 = 1 << 9;
120pub(crate) const DWMMC_INT_HOST_TIMEOUT: u32 = 1 << 10;
121pub(crate) const DWMMC_INT_FIFO_UNDER_OVER_RUN: u32 = 1 << 11;
122pub(crate) const DWMMC_INT_HARDWARE_LOCKED_WRITE: u32 = 1 << 12;
123pub(crate) const DWMMC_INT_START_BIT_ERROR: u32 = 1 << 13;
124pub(crate) const DWMMC_INT_END_BIT_ERROR: u32 = 1 << 15;
125pub(crate) const DWMMC_INT_ERROR_MASK: u32 = DWMMC_INT_RESPONSE_ERROR
126 | DWMMC_INT_RESPONSE_CRC_ERROR
127 | DWMMC_INT_DATA_CRC_ERROR
128 | DWMMC_INT_RESPONSE_TIMEOUT
129 | DWMMC_INT_DATA_READ_TIMEOUT
130 | DWMMC_INT_HOST_TIMEOUT
131 | DWMMC_INT_FIFO_UNDER_OVER_RUN
132 | DWMMC_INT_HARDWARE_LOCKED_WRITE
133 | DWMMC_INT_START_BIT_ERROR
134 | DWMMC_INT_END_BIT_ERROR;
135
136impl SdioHost for DwMmc {
137 type Event = Event;
138 type DataRequest<'a> = DataRequest<'a>;
139
140 fn submit_command(&mut self, cmd: &Command) -> Result<(), Error> {
141 DwMmc::submit_command(self, cmd)
142 }
143
144 fn poll_command_response(&mut self) -> Result<sdmmc_protocol::CommandResponsePoll, Error> {
145 DwMmc::poll_command_response(self)
146 }
147
148 fn submit_read_data<'a>(
149 &mut self,
150 cmd: &Command,
151 buf: &'a mut [u8],
152 block_size: u32,
153 block_count: u32,
154 ) -> Result<Self::DataRequest<'a>, Error> {
155 let buffer = NonNull::new(buf.as_mut_ptr()).ok_or(Error::InvalidArgument)?;
156 let mut slot = BlockRequestSlot::default();
157 let request = submit_read_with_dma_fifo_fallback(
158 self,
159 cmd,
160 buffer,
161 buf.len(),
162 block_size,
163 block_count,
164 &mut slot,
165 )?;
166 let id = request.id();
167 Ok(DataRequest {
168 id,
169 request: Some(request),
170 slot,
171 _buffer: PhantomData,
172 })
173 }
174
175 fn submit_write_data<'a>(
176 &mut self,
177 cmd: &Command,
178 buf: &'a [u8],
179 block_size: u32,
180 block_count: u32,
181 ) -> Result<Self::DataRequest<'a>, Error> {
182 let buffer = NonNull::new(buf.as_ptr() as *mut u8).ok_or(Error::InvalidArgument)?;
183 let mut slot = BlockRequestSlot::default();
184 let request = submit_write_with_dma_fifo_fallback(
185 self,
186 cmd,
187 buffer,
188 buf.len(),
189 block_size,
190 block_count,
191 &mut slot,
192 )?;
193 let id = request.id();
194 Ok(DataRequest {
195 id,
196 request: Some(request),
197 slot,
198 _buffer: PhantomData,
199 })
200 }
201
202 fn poll_data_request<'a>(
203 &mut self,
204 request: &mut Self::DataRequest<'a>,
205 ) -> Result<DataCommandPoll, Error> {
206 self.poll_block_request_response(&mut request.request, request.id, &mut request.slot)
207 }
208
209 fn set_bus_width(&mut self, width: BusWidth) -> Result<(), Error> {
210 self.set_card_type(width);
211 Ok(())
212 }
213
214 fn set_clock(&mut self, speed: ClockSpeed) -> Result<(), Error> {
215 let target_hz = clock_hz_for_speed(speed);
216 self.set_uhs_timing(speed);
217 self.program_clock(target_hz)
218 }
219
220 fn switch_voltage(&mut self, voltage: SignalVoltage) -> Result<(), Error> {
221 self.set_signal_voltage(voltage)
222 }
223
224 fn enable_completion_irq(&mut self) -> Result<(), Error> {
225 DwMmc::enable_completion_irq(self);
226 Ok(())
227 }
228
229 fn disable_completion_irq(&mut self) -> Result<(), Error> {
230 DwMmc::disable_completion_irq(self);
231 Ok(())
232 }
233
234 fn handle_irq(&mut self) -> Self::Event {
235 DwMmc::handle_irq(self)
236 }
237}
238
239fn submit_read_with_dma_fifo_fallback(
240 host: &mut DwMmc,
241 cmd: &Command,
242 buffer: NonNull<u8>,
243 len: usize,
244 block_size: u32,
245 block_count: u32,
246 slot: &mut BlockRequestSlot,
247) -> Result<BlockRequest, Error> {
248 if should_try_dma(cmd, block_size, block_count, len, DataDirection::Read)
249 && let Some(dma) = host.dma.clone()
250 {
251 match host.submit_read_blocks(
252 cmd.arg,
253 buffer,
254 NonZeroUsize::new(len).ok_or(Error::InvalidArgument)?,
255 Some(&dma),
256 BlockTransferMode::Dma,
257 slot,
258 ) {
259 Ok(request) => return Ok(request),
260 Err(err) if can_fallback_to_fifo(err) => {}
261 Err(err) => return Err(err),
262 }
263 }
264
265 host.submit_fifo_data_request(
266 cmd,
267 buffer,
268 len,
269 block_size,
270 block_count,
271 DataDirection::Read,
272 slot,
273 )
274}
275
276fn submit_write_with_dma_fifo_fallback(
277 host: &mut DwMmc,
278 cmd: &Command,
279 buffer: NonNull<u8>,
280 len: usize,
281 block_size: u32,
282 block_count: u32,
283 slot: &mut BlockRequestSlot,
284) -> Result<BlockRequest, Error> {
285 if should_try_dma(cmd, block_size, block_count, len, DataDirection::Write)
286 && let Some(dma) = host.dma.clone()
287 {
288 match host.submit_write_blocks(
289 cmd.arg,
290 buffer,
291 NonZeroUsize::new(len).ok_or(Error::InvalidArgument)?,
292 Some(&dma),
293 BlockTransferMode::Dma,
294 slot,
295 ) {
296 Ok(request) => return Ok(request),
297 Err(err) if can_fallback_to_fifo(err) => {}
298 Err(err) => return Err(err),
299 }
300 }
301
302 host.submit_fifo_data_request(
303 cmd,
304 buffer,
305 len,
306 block_size,
307 block_count,
308 DataDirection::Write,
309 slot,
310 )
311}
312
313fn should_try_dma(
314 cmd: &Command,
315 block_size: u32,
316 block_count: u32,
317 len: usize,
318 direction: DataDirection,
319) -> bool {
320 block_size == 512
321 && len == block_count as usize * 512
322 && matches!(
323 (direction, cmd.cmd),
324 (DataDirection::Read, 17 | 18) | (DataDirection::Write, 24 | 25)
325 )
326}
327
328fn can_fallback_to_fifo(err: Error) -> bool {
329 matches!(
330 err,
331 Error::UnsupportedCommand | Error::InvalidArgument | Error::Misaligned
332 )
333}
334
335pub(crate) fn event_from_raw_status(raw_status: u32) -> Event {
336 let status = crate::regs::RIntSts::from_bits(raw_status);
337 if raw_status == 0 {
338 Event::None
339 } else if status.error() {
340 Event::Error { raw_status }
341 } else if status.command_done() {
342 Event::CommandComplete
343 } else if status.data_transfer_over() {
344 Event::TransferComplete
345 } else if status.receive_fifo_data_request() {
346 Event::ReceiveReady
347 } else if status.transmit_fifo_data_request() {
348 Event::TransmitReady
349 } else {
350 Event::Other { raw_status }
351 }
352}
353
354impl HostEvent for Event {
355 fn kind(&self) -> HostEventKind {
356 match self {
357 Event::None => HostEventKind::None,
358 Event::CommandComplete => HostEventKind::CommandComplete,
359 Event::TransferComplete => HostEventKind::TransferComplete,
360 Event::ReceiveReady => HostEventKind::ReceiveReady,
361 Event::TransmitReady => HostEventKind::TransmitReady,
362 Event::Error { .. } => HostEventKind::Error,
363 Event::Other { .. } => HostEventKind::Other,
364 }
365 }
366
367 fn source(&self) -> HostEventSource {
368 match self {
369 Event::CommandComplete => HostEventSource::Command,
370 Event::TransferComplete | Event::ReceiveReady | Event::TransmitReady => {
371 HostEventSource::Data
372 }
373 Event::None | Event::Error { .. } | Event::Other { .. } => HostEventSource::Controller,
374 }
375 }
376
377 fn queue_id(&self) -> Option<BlockRequestId> {
378 match self {
379 Event::TransferComplete | Event::ReceiveReady | Event::TransmitReady => {
380 Some(BlockRequestId::new(0))
381 }
382 Event::None | Event::CommandComplete | Event::Error { .. } | Event::Other { .. } => {
383 None
384 }
385 }
386 }
387}
388
389impl DwMmc {
390 pub fn block_buffer_config(&self, mode: BlockTransferMode) -> BlockBufferConfig {
391 match mode {
392 BlockTransferMode::Fifo => {
393 BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 1, None)
394 }
395 BlockTransferMode::Dma => {
396 BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 512, Some(self.dma_mask))
397 }
398 _ => BlockBufferConfig::new(NonZeroUsize::new(512).unwrap(), 1, None),
400 }
401 }
402
403 pub fn handle_irq(&mut self) -> Event {
406 let raw_status = self.regs.mintsts().read();
407 if raw_status != 0 {
408 self.regs
409 .rintsts()
410 .write(crate::regs::RIntSts::from_bits(raw_status));
411 }
412 self.irq_pending_status |= raw_status;
413 event_from_raw_status(raw_status)
414 }
415}
416
417fn clock_hz_for_speed(speed: ClockSpeed) -> u32 {
418 match speed {
419 ClockSpeed::Identification => 400_000,
420 ClockSpeed::Default | ClockSpeed::Sdr12 => 25_000_000,
421 ClockSpeed::HighSpeed | ClockSpeed::Sdr25 => 50_000_000,
422 ClockSpeed::Sdr50 | ClockSpeed::Ddr50 => 50_000_000,
423 ClockSpeed::Sdr104 => 104_000_000,
424 ClockSpeed::Hs200 => 200_000_000,
425 _ => 0,
427 }
428}
429
430pub(crate) fn ddr_mask_for_speed(speed: ClockSpeed) -> u16 {
431 match speed {
432 ClockSpeed::Ddr50 => 1,
433 _ => 0,
434 }
435}
436
437pub(crate) fn volt_mask_for_signal(voltage: SignalVoltage) -> Result<u16, Error> {
438 match voltage {
439 SignalVoltage::V330 => Ok(0),
440 SignalVoltage::V180 => Ok(1),
441 SignalVoltage::V120 => Err(Error::UnsupportedCommand),
442 _ => Err(Error::UnsupportedCommand),
444 }
445}
446
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub(crate) struct UhsBits {
449 pub ddr: u16,
450 pub volt: u16,
451}
452
453pub(crate) fn uhs_bits_after_speed(cur: UhsBits, speed: ClockSpeed) -> UhsBits {
454 UhsBits {
455 ddr: ddr_mask_for_speed(speed),
456 ..cur
457 }
458}
459
460pub(crate) fn uhs_bits_after_voltage(
461 cur: UhsBits,
462 voltage: SignalVoltage,
463) -> Result<UhsBits, Error> {
464 Ok(UhsBits {
465 volt: volt_mask_for_signal(voltage)?,
466 ..cur
467 })
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn event_reports_command_completion_without_os_wakeup_policy() {
476 let raw = crate::regs::RIntSts::new()
477 .with_command_done(true)
478 .into_bits();
479
480 assert_eq!(event_from_raw_status(raw), Event::CommandComplete);
481 }
482
483 #[test]
484 fn event_reports_transfer_completion_without_os_wakeup_policy() {
485 let raw = crate::regs::RIntSts::new()
486 .with_data_transfer_over(true)
487 .into_bits();
488
489 assert_eq!(event_from_raw_status(raw), Event::TransferComplete);
490 }
491
492 #[test]
493 fn event_reports_error_status_without_translating_to_os_action() {
494 let raw = crate::regs::RIntSts::new()
495 .with_response_timeout(true)
496 .into_bits();
497
498 assert_eq!(event_from_raw_status(raw), Event::Error { raw_status: raw });
499 }
500
501 #[test]
502 fn event_reports_data_completion_source_for_runtime_wakeup() {
503 use sdmmc_protocol::sdio::{HostEvent, HostEventKind, HostEventSource};
504
505 let raw = crate::regs::RIntSts::new()
506 .with_data_transfer_over(true)
507 .into_bits();
508 let event = event_from_raw_status(raw);
509
510 assert_eq!(event.kind(), HostEventKind::TransferComplete);
511 assert_eq!(event.source(), HostEventSource::Data);
512 assert_eq!(event.queue_id(), Some(BlockRequestId::new(0)));
513 }
514
515 #[test]
516 fn exposes_block_buffer_constraints() {
517 let host = unsafe { DwMmc::new_from_addr(0x1000_0000) };
518
519 let dma = host.block_buffer_config(BlockTransferMode::Dma);
520 assert_eq!(dma.block_size.get(), 512);
521 assert_eq!(dma.align, 512);
522 assert_eq!(dma.dma_mask, Some(u32::MAX as u64));
523 }
524
525 #[test]
526 fn uhs_i_sdr_modes_keep_ddr_disabled() {
527 let cur = UhsBits { ddr: 1, volt: 1 };
528
529 assert_eq!(uhs_bits_after_speed(cur, ClockSpeed::Sdr50).ddr, 0);
530 assert_eq!(uhs_bits_after_speed(cur, ClockSpeed::Sdr104).ddr, 0);
531 assert_eq!(uhs_bits_after_speed(cur, ClockSpeed::Hs200).ddr, 0);
532 }
533
534 #[test]
535 fn ddr50_enables_ddr_mode_for_card0() {
536 let cur = UhsBits { ddr: 0, volt: 1 };
537
538 assert_eq!(
539 uhs_bits_after_speed(cur, ClockSpeed::Ddr50),
540 UhsBits { ddr: 1, volt: 1 }
541 );
542 }
543
544 #[test]
545 fn uhs_i_voltage_switch_selects_1v8_for_card0() {
546 let cur = UhsBits { ddr: 1, volt: 0 };
547
548 assert_eq!(
549 uhs_bits_after_voltage(cur, SignalVoltage::V180).unwrap(),
550 UhsBits { ddr: 1, volt: 1 }
551 );
552 assert_eq!(
553 uhs_bits_after_voltage(cur, SignalVoltage::V330).unwrap(),
554 UhsBits { ddr: 1, volt: 0 }
555 );
556 }
557
558 #[test]
559 fn unsupported_1v2_voltage_is_rejected() {
560 assert_eq!(
561 volt_mask_for_signal(SignalVoltage::V120).unwrap_err(),
562 Error::UnsupportedCommand
563 );
564 }
565
566 #[test]
567 fn data_command_index_is_recorded_for_diagnostics() {
568 let mut host = unsafe { DwMmc::new_from_addr(0x1000_0000) };
569 host.data_cmd_index = 6;
570
571 assert_eq!(host.data_cmd_index, 6);
572 }
573}