Skip to main content

esp_hal/sdmmc/
mod.rs

1//! # Secure Digital / MultiMedia Card host (SDMMC / SDIO)
2//!
3//! ## Overview
4//!
5//! Driver for the SDMMC/SDIO host controller (`SDHOST`). The controller exposes
6//! up to two independent card slots that share a single transfer engine.
7//!
8//! Blocking engine operations return [`BlockingError`]; [`BlockingError::Busy`]
9//! if another slot or an async transfer holds the engine. Async ops await the
10//! engine mutex instead. Bus width and card clock are cached and applied on
11//! engine acquire (lock order: engine, then settings).
12//!
13//! ## Configuration
14//!
15//! [`SdHostController::new`] takes a [`Config`] for the shared module clock.
16//! Each slot is obtained via [`SdHostController::slot`] with a [`SlotConfig`],
17//! then wired with `with_clk`, `with_cmd`, `with_data*`, and optional
18//! card-detect / write-protect pins.
19//!
20//! ## Usage
21//!
22//! Blocking commands and block transfers are available on [`Slot`] in
23//! [`Blocking`] mode. [`Slot::into_async`] enables interrupt-driven operation
24//! and implements [`sdio::MmcBus`] for the `sdio` crate stack.
25//!
26//! ## Implementation State
27//!
28//! SDIO device (I/O) mode is supported via [`sdio::MmcBus`]. SPI mode and
29//! UHS-I (1.8 V switching) are not implemented.
30
31use core::{
32    cell::UnsafeCell,
33    future::poll_fn,
34    marker::PhantomData,
35    pin::Pin,
36    sync::atomic::Ordering,
37    task::{Context, Poll},
38};
39
40use embassy_futures::yield_now;
41use embassy_sync::{mutex::MutexGuard, waitqueue::WakerRegistration};
42use esp_sync::{NonReentrantMutex, RawMutex};
43use portable_atomic::AtomicBool;
44use procmacros::{BuilderLite, handler};
45use sdio::{self as _, MmcError};
46
47#[cfg(sdmmc_has_gpio_matrix)]
48use crate::gpio::{OutputSignal, PinGuard, Pull};
49use crate::{
50    Async,
51    Blocking,
52    DriverMode,
53    asynch::AtomicWaker,
54    dma::{
55        DmaBufError,
56        aligned::{DmaAlignedMut, DmaAlignedRef, InternalMemory},
57    },
58    gpio::{
59        InputSignal,
60        OutputConfig,
61        interconnect::{self, PeripheralInput, PeripheralOutput},
62    },
63    peripherals::{Interrupt, SDHOST},
64    private::DropGuard,
65    system::{Peripheral, PeripheralGuard},
66    time::{Duration, Instant},
67};
68
69#[cfg_attr(esp32, path = "esp32.rs")]
70#[cfg_attr(esp32s3, path = "esp32s3.rs")]
71#[cfg_attr(esp32p4, path = "esp32p4.rs")]
72#[cfg_attr(esp32s31, path = "esp32s31.rs")]
73mod chip_specific;
74
75#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
76mod bounce;
77
78/// Card clock source feeding the controller's divider.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81pub enum ClockSource {
82    /// 160 MHz PLL.
83    #[cfg(not(esp32s31))]
84    Pll160m,
85    /// 500 MHz PLL.
86    #[cfg(esp32s31)]
87    Mpll,
88    /// Crystal oscillator.
89    #[cfg(not(esp32p4))]
90    Xtal,
91}
92
93/// Clock input sampling phase used for high-speed tuning.
94#[cfg(sdmmc_delay_phase_num_is_set)]
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub enum DelayPhase {
98    /// 0°.
99    #[default]
100    _0,
101    /// 90°.
102    _1,
103    /// 180°.
104    _2,
105    /// 270°.
106    _3,
107}
108
109/// Card data bus width.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub enum BusWidth {
113    /// 1-bit bus (DAT0 only).
114    #[default]
115    Bit1,
116    /// 4-bit bus (DAT0–DAT3).
117    Bit4,
118    /// 8-bit bus (DAT0–DAT7, Card slot 0 only).
119    Bit8,
120}
121
122/// Controller-wide (engine) configuration.
123///
124/// These settings drive the shared module clock and therefore apply to the
125/// whole controller, not an individual slot.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, BuilderLite)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128#[non_exhaustive]
129pub struct Config {
130    /// The clock source for the module clock.
131    clock_source: ClockSource,
132
133    /// The module-clock divider (valid range `2..=16`).
134    module_div: u8,
135}
136
137impl Default for Config {
138    fn default() -> Self {
139        Self::const_default()
140    }
141}
142
143impl Config {
144    pub(crate) const fn const_default() -> Self {
145        cfg_select! {
146            esp32s31 => Self {
147                clock_source: ClockSource::Mpll,
148                module_div: 8,
149            },
150            _ => Self {
151                clock_source: ClockSource::Pll160m,
152                module_div: 2,
153            },
154        }
155    }
156
157    /// Validates field ranges.
158    fn validate(&self) -> Result<(), ConfigError> {
159        if !(2..=16).contains(&self.module_div) {
160            return Err(ConfigError::InvalidModuleDivider);
161        }
162        Ok(())
163    }
164}
165
166/// Per-slot configuration.
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, BuilderLite)]
168#[cfg_attr(feature = "defmt", derive(defmt::Format))]
169#[non_exhaustive]
170pub struct SlotConfig {
171    /// Input sampling delay phase used for high-speed tuning.
172    #[cfg(sdmmc_delay_phase_num_is_set)]
173    input_delay_phase: DelayPhase,
174
175    /// Write-protect signal polarity (active-high or active-low).
176    wp_active_high: bool,
177}
178
179/// Length of the response a command expects.
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub enum ResponseLen {
183    /// No response.
184    None,
185    /// Short 48-bit response (`resp[0]`).
186    Short,
187    /// Long 136-bit response (`resp[0..4]`).
188    Long,
189}
190
191/// Per-command engine flags.
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub struct CommandFlags {
195    /// Wait for the data line to be free before issuing.
196    pub wait_complete: bool,
197    /// Stop/abort command (CMD12, CMD52 abort).
198    pub stop_abort: bool,
199    /// Poll DAT0 until the card releases busy (R1b).
200    pub busy: bool,
201}
202
203/// Error returned by host operations.
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205#[cfg_attr(feature = "defmt", derive(defmt::Format))]
206#[non_exhaustive]
207#[expect(clippy::enum_variant_names)]
208pub enum Error {
209    /// A hardware operation did not complete in time.
210    Timeout,
211    /// Response CRC check failed.
212    ResponseCrc,
213    /// Data CRC or end-bit error.
214    DataCrc,
215    /// Card did not respond to the command.
216    ResponseTimeout,
217    /// Data transfer timed out.
218    DataTimeout,
219    /// FIFO under- or overrun during a transfer.
220    FifoOverrun,
221    /// Data start-bit error.
222    StartBitError,
223    /// Command could not be loaded (hardware locked).
224    HardwareLocked,
225    /// Controller flagged a response error.
226    ResponseError,
227    /// IDMAC transfer error.
228    DmaError,
229    /// No card present in the slot.
230    NoCard,
231    /// Buffer lies in a region the IDMAC cannot reach.
232    BufferNotDmaCapable,
233    /// Operation not supported.
234    Unsupported,
235}
236
237impl core::error::Error for Error {}
238
239impl core::fmt::Display for Error {
240    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241        match self {
242            Error::Timeout => write!(f, "A hardware operation did not complete in time"),
243            Error::ResponseCrc => write!(f, "Response CRC check failed"),
244            Error::DataCrc => write!(f, "Data CRC or end-bit error"),
245            Error::ResponseTimeout => write!(f, "Card did not respond to the command"),
246            Error::DataTimeout => write!(f, "Data transfer timed out"),
247            Error::FifoOverrun => write!(f, "FIFO under- or overrun during a transfer"),
248            Error::StartBitError => write!(f, "Data start-bit error"),
249            Error::HardwareLocked => write!(f, "Command could not be loaded (hardware locked)"),
250            Error::ResponseError => write!(f, "Controller flagged a response error"),
251            Error::DmaError => write!(f, "IDMAC transfer error"),
252            Error::NoCard => write!(f, "No card present in the slot"),
253            Error::BufferNotDmaCapable => {
254                write!(f, "Buffer lies in a region the IDMAC cannot reach")
255            }
256            Error::Unsupported => write!(f, "Operation not supported"),
257        }
258    }
259}
260
261/// Error returned when applying a [`Config`].
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263#[cfg_attr(feature = "defmt", derive(defmt::Format))]
264#[non_exhaustive]
265pub enum ConfigError {
266    /// Module-clock divider is outside `2..=16`.
267    InvalidModuleDivider,
268    /// The slot is already in use.
269    SlotInUse,
270    /// The clock or command pin was not connected.
271    MissingClkOrCmd,
272    /// Data line 0 was not connected.
273    NoData0,
274}
275
276impl core::error::Error for ConfigError {}
277
278impl core::fmt::Display for ConfigError {
279    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280        match self {
281            ConfigError::InvalidModuleDivider => {
282                write!(f, "Module-clock divider is outside 2..=16")
283            }
284            ConfigError::SlotInUse => write!(f, "The slot is already in use"),
285            ConfigError::MissingClkOrCmd => write!(f, "The clock or command pin was not connected"),
286            ConfigError::NoData0 => write!(f, "Data line 0 was not connected"),
287        }
288    }
289}
290
291/// Error from blocking engine operations ([`BlockingError::Busy`] is mutex contention, not CIU
292/// HLE).
293#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
294#[cfg_attr(feature = "defmt", derive(defmt::Format))]
295#[non_exhaustive]
296pub enum BlockingError {
297    /// The shared engine is held by another slot or an async transfer.
298    Busy,
299    /// The operation failed after the engine was acquired.
300    Op(Error),
301}
302
303impl core::error::Error for BlockingError {}
304
305impl core::fmt::Display for BlockingError {
306    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307        match self {
308            BlockingError::Busy => write!(f, "The shared SDMMC engine is busy"),
309            BlockingError::Op(e) => write!(f, "{e}"),
310        }
311    }
312}
313
314impl From<Error> for BlockingError {
315    fn from(error: Error) -> Self {
316        BlockingError::Op(error)
317    }
318}
319
320impl From<BlockingError> for MmcError {
321    fn from(error: BlockingError) -> Self {
322        match error {
323            BlockingError::Busy => MmcError::Other,
324            BlockingError::Op(e) => e.into(),
325        }
326    }
327}
328
329impl From<Error> for MmcError {
330    fn from(error: Error) -> Self {
331        warn!("{:?}", error);
332        match error {
333            Error::ResponseTimeout | Error::DataTimeout | Error::Timeout | Error::NoCard => {
334                MmcError::Timeout
335            }
336            Error::ResponseCrc | Error::DataCrc | Error::StartBitError => MmcError::Crc,
337            Error::FifoOverrun
338            | Error::DmaError
339            | Error::ResponseError
340            | Error::BufferNotDmaCapable => MmcError::Io,
341            Error::HardwareLocked => MmcError::Other,
342            Error::Unsupported => MmcError::Unsupported,
343        }
344    }
345}
346
347impl From<DmaBufError> for MmcError {
348    fn from(error: DmaBufError) -> Self {
349        warn!("{:?}", error);
350        match error {
351            DmaBufError::InvalidAlignment(_) => MmcError::Other,
352            _ => MmcError::Io,
353        }
354    }
355}
356
357/// Spin-poll bound for self-clearing hardware bits.
358const POLL_LIMIT: u32 = 1_000_000;
359
360// `rintsts` event bits (see `sdmmc_ll.h` `SDMMC_LL_EVENT_*`).
361const EVT_RESP_ERR: u32 = 1 << 1;
362const EVT_CMD_DONE: u32 = 1 << 2;
363const EVT_DATA_OVER: u32 = 1 << 3;
364const EVT_RCRC: u32 = 1 << 6;
365const EVT_DCRC: u32 = 1 << 7;
366const EVT_RTO: u32 = 1 << 8;
367const EVT_DTO: u32 = 1 << 9;
368const EVT_HTO: u32 = 1 << 10;
369const EVT_FRUN: u32 = 1 << 11;
370const EVT_HLE: u32 = 1 << 12;
371const EVT_SBE: u32 = 1 << 13;
372const EVT_ACD: u32 = 1 << 14;
373const EVT_EBE: u32 = 1 << 15;
374
375// `ctrl` IDMAC-enable bits not modeled by the PAC (raw writes only).
376const CTRL_DMA_ENABLE: u32 = 1 << 5;
377const CTRL_USE_INTERNAL_DMA: u32 = 1 << 25;
378
379/// IDMAC descriptor count in the driver-owned ring.
380const RING_LEN: usize = 4;
381/// Maximum bytes per descriptor (`SDMMC_DMA_MAX_BUF_LEN`).
382const DMA_MAX_BUF_LEN: usize = 4096;
383
384// `Desc.flags` bits (see `sdmmc_desc_t`).
385const DESC_LAST: u32 = 1 << 2;
386const DESC_FIRST: u32 = 1 << 3;
387const DESC_CHAINED: u32 = 1 << 4;
388const DESC_OWN: u32 = 1 << 31;
389
390/// One 16-byte IDMAC linked-list descriptor.
391#[repr(C, align(4))]
392#[derive(Clone, Copy)]
393struct Desc {
394    flags: u32,
395    sizes: u32,
396    buf1: u32,
397    next: u32,
398}
399
400impl Desc {
401    const ZERO: Self = Desc {
402        flags: 0,
403        sizes: 0,
404        buf1: 0,
405        next: 0,
406    };
407}
408
409/// One contiguous DMA segment: `(buffer address, remaining bytes)`.
410type Seg = (u32, usize);
411
412/// Tracks progress while filling the descriptor ring.
413///
414/// A transfer is described as up to three back-to-back segments so that a
415/// cache-unaligned caller buffer can still be DMA'd: the aligned middle is
416/// transferred in place, while the unaligned head and tail are bounced
417/// through an aligned scratch buffer. A fully aligned buffer uses a single
418/// segment ([`Transfer::single`]) and the other two are left empty.
419#[derive(Clone, Copy)]
420struct Transfer {
421    segs: [Seg; 3],
422    /// Index of the segment currently being linked into descriptors.
423    seg: usize,
424    next_desc: usize,
425}
426
427impl Transfer {
428    /// A single, already-DMA-capable contiguous segment.
429    fn single(ptr: u32, len: usize) -> Self {
430        Transfer {
431            segs: [(ptr, len), (0, 0), (0, 0)],
432            seg: 0,
433            next_desc: 0,
434        }
435    }
436
437    /// Head / middle / tail segments; any zero-length entry is skipped while
438    /// filling descriptors.
439    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
440    fn split(head: Seg, middle: Seg, tail: Seg) -> Self {
441        Transfer {
442            segs: [head, middle, tail],
443            seg: 0,
444            next_desc: 0,
445        }
446    }
447
448    /// Bytes not yet linked into a descriptor. Before any descriptor is
449    /// filled this equals the total transfer length.
450    fn remaining(&self) -> usize {
451        self.segs.iter().map(|(_, len)| *len).sum()
452    }
453}
454
455/// Driver-owned descriptor ring in internal DMA-capable RAM.
456struct DescRing(UnsafeCell<InternalMemory<[Desc; RING_LEN]>>);
457// Access only while `ENGINE` is held.
458unsafe impl Sync for DescRing {}
459
460static DESC_RING: DescRing = DescRing(UnsafeCell::new(InternalMemory::new([Desc::ZERO; RING_LEN])));
461
462fn ring() -> DmaAlignedMut<'static, [Desc; RING_LEN]> {
463    unsafe { &mut *DESC_RING.0.get() }.get_mut()
464}
465
466/// Exclusive engine session: bounce scratch and last HW-programmed slot.
467struct EngineSession {
468    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
469    bounce: bounce::Bounce,
470    active_slot: Option<SlotId>,
471}
472
473impl EngineSession {
474    const INIT: Self = EngineSession {
475        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
476        bounce: bounce::Bounce::INIT,
477        active_slot: None,
478    };
479
480    fn select_and_apply(&mut self, slot: SlotId) -> Result<(), Error> {
481        let wait = SETTINGS.with(|s| {
482            let idx = slot.index() as usize;
483            let cached = &mut s.slots[idx];
484            if self.active_slot == Some(slot) && !cached.dirty {
485                return Ok(false);
486            }
487            self.program_slot_hw(slot, cached, s.module)?;
488            cached.dirty = false;
489            self.active_slot = Some(slot);
490            Ok(true)
491        })?;
492
493        if wait {
494            crate::rom::ets_delay_us(10);
495        }
496        Ok(())
497    }
498
499    fn program_slot_hw(
500        &mut self,
501        slot: SlotId,
502        cached: &SlotSettings,
503        module: Config,
504    ) -> Result<(), Error> {
505        self.set_card_width(slot, cached.width);
506        let card_div =
507            freq_to_card_div(module_hz(module.clock_source, module.module_div), cached.hz);
508        self.set_card_clock(slot, card_div)?;
509        #[cfg(sdmmc_delay_phase_num_is_set)]
510        if cached.hz > 25_000_000 {
511            chip_specific::set_input_delay_phase(cached.input_delay_phase, cached.hz);
512        }
513        Ok(())
514    }
515
516    fn set_card_width(&mut self, id: SlotId, width: BusWidth) {
517        let slot = id.index();
518        SDHOST::regs().ctype().modify(|rd, w| unsafe {
519            let mut w4 = rd.card_width4().bits() & !(1 << slot);
520            let mut w8 = rd.card_width8().bits() & !(1 << slot);
521            match width {
522                BusWidth::Bit8 => w8 |= 1 << slot,
523                BusWidth::Bit4 => w4 |= 1 << slot,
524                BusWidth::Bit1 => {}
525            }
526            w.card_width4().bits(w4);
527            w.card_width8().bits(w8)
528        });
529    }
530
531    fn set_card_clock(&mut self, id: SlotId, card_div: u8) -> Result<(), Error> {
532        let slot = id.index();
533        let r = SDHOST::regs();
534
535        r.clkena().modify(|rd, w| unsafe {
536            w.cclk_enable().bits(rd.cclk_enable().bits() & !(1 << slot))
537        });
538        self.apply_clock_update(id)?;
539
540        r.clksrc().modify(|rd, w| unsafe {
541            let mut v = rd.clksrc().bits();
542            match id {
543                SlotId::_0 => v &= !0b11,
544                SlotId::_1 => {
545                    v &= !0b1100;
546                    v |= 1 << 2;
547                }
548            }
549            w.clksrc().bits(v)
550        });
551        r.clkdiv().modify(|_, w| unsafe {
552            match id {
553                SlotId::_0 => w.clk_divider0().bits(card_div),
554                SlotId::_1 => w.clk_divider1().bits(card_div),
555            }
556        });
557
558        r.clkena().modify(|rd, w| unsafe {
559            w.cclk_enable().bits(rd.cclk_enable().bits() | (1 << slot));
560            w.lp_enable().bits(rd.lp_enable().bits() | (1 << slot))
561        });
562        self.apply_clock_update(id)
563    }
564
565    fn apply_clock_update(&mut self, id: SlotId) -> Result<(), Error> {
566        let r = SDHOST::regs();
567        r.cmdarg().write(|w| unsafe { w.bits(0) });
568        r.cmd().write(|w| unsafe {
569            w.update_clock_registers_only().set_bit();
570            w.wait_prvdata_complete().set_bit();
571            w.card_number().bits(id.index());
572            w.start_cmd().set_bit()
573        });
574        wait_command_accepted()
575    }
576
577    fn send_init_sequence(&mut self, slot: SlotId) -> Result<(), Error> {
578        let r = SDHOST::regs();
579        r.rintsts().write(|w| unsafe { w.bits(EVT_CMD_DONE) });
580        r.cmdarg().write(|w| unsafe { w.bits(0) });
581        r.cmd().write(|w| unsafe {
582            w.send_initialization().set_bit();
583            w.wait_prvdata_complete().set_bit();
584            w.card_number().bits(slot.index());
585            w.start_cmd().set_bit()
586        });
587        wait_command_accepted()?;
588
589        let result = poll_until(|| r.rintsts().read().bits() & EVT_CMD_DONE != 0);
590        if result.is_ok() {
591            r.rintsts().write(|w| unsafe { w.bits(EVT_CMD_DONE) });
592        }
593        result
594    }
595
596    fn send_command_blocking(
597        &mut self,
598        slot: SlotId,
599        index: u8,
600        arg: u32,
601        resp_len: ResponseLen,
602        check_crc: bool,
603        flags: CommandFlags,
604    ) -> Result<[u32; 4], Error> {
605        let r = SDHOST::regs();
606        let consume = EVT_CMD_DONE | EVT_RTO | EVT_RCRC | EVT_RESP_ERR | EVT_HLE;
607
608        r.rintsts().write(|w| unsafe { w.bits(consume) });
609
610        self.issue_command(slot, index, arg, resp_len, check_crc, flags)?;
611
612        let done = EVT_CMD_DONE | EVT_RTO | EVT_RCRC | EVT_RESP_ERR;
613        let mut sts = 0;
614        poll_until(|| {
615            sts = r.rintsts().read().bits();
616            sts & done != 0
617        })?;
618        map_rintsts(sts)?;
619
620        let resp = read_response(resp_len);
621        r.rintsts().write(|w| unsafe { w.bits(consume) });
622
623        if flags.busy {
624            wait_busy_cleared()?;
625        }
626        Ok(resp)
627    }
628
629    #[allow(clippy::too_many_arguments)]
630    fn transfer_blocking(
631        &mut self,
632        slot: SlotId,
633        index: u8,
634        arg: u32,
635        write: bool,
636        mut t: Transfer,
637        block_size: u16,
638        block_count: u32,
639    ) -> Result<[u32; 4], Error> {
640        let r = SDHOST::regs();
641
642        reset_transfer()?;
643        let total_len = t.remaining();
644        r.blksiz().write(|w| unsafe { w.bits(block_size as u32) });
645        r.bytcnt().write(|w| unsafe { w.bits(total_len as u32) });
646
647        let mut ring = ring();
648        *ring.reborrow().into_inner() = [Desc::ZERO; RING_LEN];
649        ring[0].flags |= DESC_FIRST;
650        fill_descriptors(ring.reborrow(), &mut t, RING_LEN);
651        enable_idmac(ring.as_ptr() as u32);
652        r.pldmnd().write(|w| unsafe { w.bits(1) });
653
654        let auto_stop = needs_auto_stop(index, block_count);
655        self.issue_data_command(slot, index, arg, write, auto_stop)?;
656
657        let result = run_data_phase(&mut t, ring, write, auto_stop);
658
659        disable_idmac();
660        r.rintsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
661        r.idsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
662
663        result?;
664        Ok(read_response(ResponseLen::Short))
665    }
666
667    fn issue_command(
668        &mut self,
669        slot: SlotId,
670        index: u8,
671        arg: u32,
672        resp_len: ResponseLen,
673        check_crc: bool,
674        flags: CommandFlags,
675    ) -> Result<(), Error> {
676        let r = SDHOST::regs();
677        r.cmdarg().write(|w| unsafe { w.bits(arg) });
678        r.cmd().write(|w| unsafe {
679            w.index().bits(index);
680            w.response_expect()
681                .bit(!matches!(resp_len, ResponseLen::None));
682            w.response_length()
683                .bit(matches!(resp_len, ResponseLen::Long));
684            w.check_response_crc().bit(check_crc);
685            w.wait_prvdata_complete().bit(flags.wait_complete);
686            w.stop_abort_cmd().bit(flags.stop_abort);
687            w.use_hole().set_bit();
688            w.card_number().bits(slot.index());
689            w.start_cmd().set_bit()
690        });
691        wait_command_accepted()
692    }
693
694    fn issue_data_command(
695        &mut self,
696        slot: SlotId,
697        index: u8,
698        arg: u32,
699        write: bool,
700        auto_stop: bool,
701    ) -> Result<(), Error> {
702        let r = SDHOST::regs();
703        r.cmdarg().write(|w| unsafe { w.bits(arg) });
704        r.cmd().write(|w| unsafe {
705            w.index().bits(index);
706            w.response_expect().set_bit();
707            w.check_response_crc().set_bit();
708            w.data_expected().set_bit();
709            w.read_write().bit(write);
710            w.send_auto_stop().bit(auto_stop);
711            w.wait_prvdata_complete().set_bit();
712            w.use_hole().set_bit();
713            w.card_number().bits(slot.index());
714            w.start_cmd().set_bit()
715        });
716        wait_command_accepted()
717    }
718
719    async fn send_command_async(
720        &mut self,
721        slot: SlotId,
722        index: u8,
723        arg: u32,
724        resp_len: ResponseLen,
725        check_crc: bool,
726        flags: CommandFlags,
727    ) -> Result<[u32; 4], Error> {
728        let r = SDHOST::regs();
729        let guard = DropGuard::new((), |()| abort_transfer());
730
731        r.rintsts().write(|w| unsafe { w.bits(INTMASK_CMD) });
732        arm_transfer(false, false, None);
733        r.intmask()
734            .write(|w| unsafe { w.bits(idle_intmask() | INTMASK_CMD) });
735
736        self.issue_command(slot, index, arg, resp_len, check_crc, flags)?;
737        wait_result().await?;
738        let resp = read_response(resp_len);
739        if flags.busy {
740            wait_busy_poll().await?;
741        }
742        guard.defuse();
743        Ok(resp)
744    }
745
746    #[allow(clippy::too_many_arguments)]
747    async fn transfer_async(
748        &mut self,
749        slot: SlotId,
750        index: u8,
751        arg: u32,
752        write: bool,
753        mut t: Transfer,
754        block_size: u16,
755        auto_stop: bool,
756    ) -> Result<[u32; 4], Error> {
757        let guard = DropGuard::new((), |()| abort_transfer());
758        let r = SDHOST::regs();
759
760        reset_transfer()?;
761        let total_len = t.remaining();
762        r.blksiz().write(|w| unsafe { w.bits(block_size as u32) });
763        r.bytcnt().write(|w| unsafe { w.bits(total_len as u32) });
764
765        let mut ring = ring();
766        *ring.reborrow().into_inner() = [Desc::ZERO; RING_LEN];
767        ring[0].flags |= DESC_FIRST;
768        fill_descriptors(ring.reborrow(), &mut t, RING_LEN);
769        enable_idmac(ring.as_ptr() as u32);
770        r.pldmnd().write(|w| unsafe { w.bits(1) });
771
772        arm_transfer(true, auto_stop, Some(t));
773        r.idinten().write(|w| unsafe { w.bits(IDINTEN_ALL) });
774        r.intmask()
775            .write(|w| unsafe { w.bits(idle_intmask() | INTMASK_DATA) });
776
777        self.issue_data_command(slot, index, arg, write, auto_stop)?;
778        wait_result().await?;
779        if write {
780            wait_busy_async().await?;
781        }
782        disable_idmac();
783        let resp = read_response(ResponseLen::Short);
784        guard.defuse();
785        Ok(resp)
786    }
787
788    async fn read_async(
789        &mut self,
790        slot: SlotId,
791        index: u8,
792        arg: u32,
793        buf: &mut [u8],
794        block_size: u16,
795        auto_stop: bool,
796    ) -> Result<[u32; 4], MmcError> {
797        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
798        if let Some(split) = bounce::Bounce::split(buf) {
799            let transfer = self
800                .bounce
801                .read_dma_setup(buf, split)
802                .map_err(MmcError::from)?;
803
804            let resp = self
805                .transfer_async(slot, index, arg, false, transfer, block_size, auto_stop)
806                .await?;
807
808            self.bounce.read_finish(buf, split);
809            return Ok(resp);
810        }
811
812        let mut dma = DmaAlignedMut::new(buf)?;
813        let ptr = dma_ptr(dma.reborrow())?;
814        let total = dma.len();
815        let resp = self
816            .transfer_async(
817                slot,
818                index,
819                arg,
820                false,
821                Transfer::single(ptr, total),
822                block_size,
823                auto_stop,
824            )
825            .await?;
826        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
827        dma.invalidate();
828        Ok(resp)
829    }
830
831    async fn write_async(
832        &mut self,
833        slot: SlotId,
834        index: u8,
835        arg: u32,
836        buf: &[u8],
837        block_size: u16,
838        auto_stop: bool,
839    ) -> Result<[u32; 4], MmcError> {
840        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
841        if let Some(split) = bounce::Bounce::split(buf) {
842            let transfer = self
843                .bounce
844                .write_dma_setup(buf, split)
845                .map_err(MmcError::from)?;
846
847            return Ok(self
848                .transfer_async(slot, index, arg, true, transfer, block_size, auto_stop)
849                .await?);
850        }
851
852        let dma = DmaAlignedRef::new(buf)?;
853
854        // Flush the caller's buffer before the IDMAC reads it, else stale
855        // memory is written to the card. Reached only when `Bounce::split`
856        // returned `None`, so `buf` is region-aligned in address and length.
857        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
858        dma.writeback();
859
860        let total = dma.len();
861        let ptr = dma_ptr_ref(dma)?;
862        Ok(self
863            .transfer_async(
864                slot,
865                index,
866                arg,
867                true,
868                Transfer::single(ptr, total),
869                block_size,
870                auto_stop,
871            )
872            .await?)
873    }
874}
875
876// `rintsts` card-detect change bit.
877const EVT_CD: u32 = 1 << 0;
878// `rintsts` SDIO card-interrupt bits (one per slot).
879const EVT_IO_SLOT0: u32 = 1 << 16;
880const EVT_IO_SLOT1: u32 = 1 << 17;
881
882// `idsts` fatal DMA bits (fatal bus error / descriptor unavailable).
883const IDSTS_FBE: u32 = 1 << 2;
884const IDSTS_DU: u32 = 1 << 4;
885
886/// Interrupt mask kept armed while idle (card-detect only).
887const INTMASK_IDLE: u32 = EVT_CD;
888/// Command-phase interrupt mask.
889const INTMASK_CMD: u32 = EVT_CMD_DONE | EVT_RTO | EVT_RCRC | EVT_RESP_ERR | EVT_HLE;
890/// Data-phase interrupt mask (command bits plus data events).
891const INTMASK_DATA: u32 = INTMASK_CMD
892    | EVT_DATA_OVER
893    | EVT_DCRC
894    | EVT_DTO
895    | EVT_HTO
896    | EVT_SBE
897    | EVT_EBE
898    | EVT_FRUN
899    | EVT_ACD;
900
901// `idinten` bits: TX/RX done, fatal/unavailable, normal/abnormal summaries.
902const IDINTEN_ALL: u32 = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 8) | (1 << 9);
903
904/// ISR/task transfer handshake (distinct from CIU access via `EngineSession`).
905struct TransferState {
906    transfer: Option<Transfer>,
907    result: Option<Result<(), Error>>,
908    expect_data: bool,
909    multiblock: bool,
910    over_seen: bool,
911    acd_seen: bool,
912    wait_busy: bool,
913    waker: WakerRegistration,
914}
915
916impl TransferState {
917    const IDLE: Self = TransferState {
918        transfer: None,
919        result: None,
920        expect_data: false,
921        multiblock: false,
922        over_seen: false,
923        acd_seen: false,
924        wait_busy: false,
925        waker: WakerRegistration::new(),
926    };
927}
928
929/// Persistent controller/slot settings consulted on slot selection.
930struct Settings {
931    module: Config,
932    slots: [SlotSettings; SLOT_COUNT],
933}
934
935impl Settings {
936    const INIT: Self = Settings {
937        module: Config::const_default(),
938        slots: [SlotSettings::INIT; SLOT_COUNT],
939    };
940
941    fn set_slot_bus(&mut self, slot: SlotId, width: BusWidth, hz: u32) -> Result<(), Error> {
942        if hz == 0 || hz > 40_000_000 {
943            return Err(Error::Unsupported);
944        }
945        let idx = slot.index() as usize;
946        self.slots[idx].hz = hz;
947        self.slots[idx].width = width;
948        self.slots[idx].dirty = true;
949        Ok(())
950    }
951}
952
953#[derive(Clone, Copy)]
954struct SlotSettings {
955    hz: u32,
956    width: BusWidth,
957    dirty: bool,
958    #[cfg(sdmmc_delay_phase_num_is_set)]
959    input_delay_phase: DelayPhase,
960}
961
962impl SlotSettings {
963    const INIT: Self = SlotSettings {
964        hz: 25_000_000,
965        width: BusWidth::Bit1,
966        dirty: true,
967        #[cfg(sdmmc_delay_phase_num_is_set)]
968        input_delay_phase: DelayPhase::_0,
969    };
970}
971
972static ENGINE: embassy_sync::mutex::Mutex<RawMutex, EngineSession> =
973    embassy_sync::mutex::Mutex::new(EngineSession::INIT);
974static SETTINGS: NonReentrantMutex<Settings> = NonReentrantMutex::new(Settings::INIT);
975static TRANSFER: NonReentrantMutex<TransferState> = NonReentrantMutex::new(TransferState::IDLE);
976
977fn with_engine_try<R>(
978    slot: SlotId,
979    f: impl FnOnce(&mut EngineSession) -> Result<R, Error>,
980) -> Result<R, BlockingError> {
981    let mut session = ENGINE.try_lock().map_err(|_| BlockingError::Busy)?;
982    session.select_and_apply(slot).map_err(BlockingError::Op)?;
983    f(&mut session).map_err(BlockingError::Op)
984}
985
986async fn lock_engine(slot: SlotId) -> Result<MutexGuard<'static, RawMutex, EngineSession>, Error> {
987    let mut guard = ENGINE.lock().await;
988    guard.select_and_apply(slot)?;
989    Ok(guard)
990}
991
992/// Matrix-routed pin guards owned by the slot for its whole lifetime.
993///
994/// Held here (rather than leaked) so re-running a `with_*` builder method
995/// drops the previous routing. IO_MUX chips drive fixed pads and need none.
996#[cfg(sdmmc_has_gpio_matrix)]
997struct SlotPins {
998    clk: PinGuard,
999    cmd: PinGuard,
1000    data: [PinGuard; 4],
1001}
1002
1003#[cfg(sdmmc_has_gpio_matrix)]
1004impl SlotPins {
1005    const fn new() -> Self {
1006        Self {
1007            clk: PinGuard::new_unconnected(),
1008            cmd: PinGuard::new_unconnected(),
1009            data: [const { PinGuard::new_unconnected() }; 4],
1010        }
1011    }
1012}
1013
1014/// Mutable per-slot runtime state.
1015struct SlotState {
1016    io_waker: AtomicWaker,
1017    cd_waker: AtomicWaker,
1018    /// Edge latch: set by the ISR when a card interrupt fires, consumed
1019    /// (swapped to `false`) by the waiter. Decouples the edge from the await.
1020    io_pending: AtomicBool,
1021    #[cfg(sdmmc_has_gpio_matrix)]
1022    pins: UnsafeCell<SlotPins>,
1023}
1024
1025// `pins` is only touched while building a slot (never by the ISR); the slot
1026// value owns its `State`, mirroring the SPI driver.
1027#[cfg(sdmmc_has_gpio_matrix)]
1028unsafe impl Sync for SlotState {}
1029
1030impl SlotState {
1031    const fn new() -> Self {
1032        Self {
1033            io_waker: AtomicWaker::new(),
1034            cd_waker: AtomicWaker::new(),
1035            io_pending: AtomicBool::new(false),
1036            #[cfg(sdmmc_has_gpio_matrix)]
1037            pins: UnsafeCell::new(SlotPins::new()),
1038        }
1039    }
1040}
1041
1042static SLOT_STATE: [SlotState; SLOT_COUNT] = [const { SlotState::new() }; SLOT_COUNT];
1043
1044/// Mutable runtime state for a slot.
1045#[cfg(sdmmc_has_gpio_matrix)]
1046fn slot_state(id: SlotId) -> &'static SlotState {
1047    &SLOT_STATE[id.index() as usize]
1048}
1049
1050/// Exclusive access to a slot's pin guards (builder-time only).
1051#[cfg(sdmmc_has_gpio_matrix)]
1052fn slot_pins(id: SlotId) -> &'static mut SlotPins {
1053    unsafe { &mut *slot_state(id).pins.get() }
1054}
1055
1056/// Immutable per-slot routing: SDIO card-interrupt bit plus the input/output
1057/// signals the slot's pins connect to through the GPIO matrix.
1058///
1059/// Signals a slot does not route through the matrix (IO_MUX-routed bus
1060/// signals, or chips without a GPIO matrix at all) are `None`/empty, so one
1061/// table shape serves every chip. On IO_MUX-only chips none of the signal
1062/// fields are read, hence the conditional `allow(dead_code)`.
1063///
1064/// Populated from chip metadata by the `for_each_sdmmc!` invocation below.
1065struct SlotInfo {
1066    io_event: u32,
1067    #[cfg(sdmmc_has_gpio_matrix)]
1068    clk_out: Option<OutputSignal>,
1069    #[cfg(sdmmc_has_gpio_matrix)]
1070    cmd_in: Option<InputSignal>,
1071    #[cfg(sdmmc_has_gpio_matrix)]
1072    cmd_out: Option<OutputSignal>,
1073    #[cfg(sdmmc_has_gpio_matrix)]
1074    data_in: &'static [InputSignal],
1075    #[cfg(sdmmc_has_gpio_matrix)]
1076    data_out: &'static [OutputSignal],
1077    cd_in: Option<InputSignal>,
1078    wp_in: Option<InputSignal>,
1079}
1080
1081// Wrap an optional metadata signal name in `Some(..)`, or yield `None` when
1082// the slot does not route that signal through the GPIO matrix.
1083macro_rules! opt_in {
1084    () => {
1085        None
1086    };
1087    ($signal:ident) => {
1088        Some(InputSignal::$signal)
1089    };
1090}
1091
1092#[cfg(sdmmc_has_gpio_matrix)]
1093macro_rules! opt_out {
1094    () => {
1095        None
1096    };
1097    ($signal:ident) => {
1098        Some(OutputSignal::$signal)
1099    };
1100}
1101
1102// The SDIO card-interrupt bit is positional (`rintsts` bit `16 + slot`); the
1103// signal names come straight from chip metadata.
1104for_each_sdmmc! {
1105    (all $( (
1106        $slot:ident, $idx:literal, $iomux:literal,
1107        [$($clk:ident)?], [$($cmd_in:ident)?], [$($cmd_out:ident)?],
1108        [$($data_in:ident),*], [$($data_out:ident),*],
1109        [$($cd:ident)?], [$($wp:ident)?], [$($card_int:ident)?],
1110        [$($data_strobe:ident)?], [$($rst:ident)?]
1111    ) ),*) => {
1112        const SLOT_COUNT: usize = 0 $(+ { crate::ignore!($slot); 1 })*;
1113        static SLOT_INFO: [SlotInfo; SLOT_COUNT] = [ $(
1114            SlotInfo {
1115                io_event: 1u32 << (16 + $idx),
1116
1117                #[cfg(sdmmc_has_gpio_matrix)]
1118                clk_out: opt_out!($($clk)?),
1119                #[cfg(sdmmc_has_gpio_matrix)]
1120                cmd_in: opt_in!($($cmd_in)?),
1121                #[cfg(sdmmc_has_gpio_matrix)]
1122                cmd_out: opt_out!($($cmd_out)?),
1123                #[cfg(sdmmc_has_gpio_matrix)]
1124                data_in: &[ $(InputSignal::$data_in),* ],
1125                #[cfg(sdmmc_has_gpio_matrix)]
1126                data_out: &[ $(OutputSignal::$data_out),* ],
1127
1128                cd_in: opt_in!($($cd)?),
1129                wp_in: opt_in!($($wp)?),
1130            }
1131        ),* ];
1132
1133        paste::paste! {
1134            /// Selects one of the controller's card slots.
1135            #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1136            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1137            pub enum SlotId {
1138                $(
1139                    #[doc = concat!("Slot ", stringify!($idx), ".")]
1140                    [<_ $idx>],
1141                )*
1142            }
1143
1144            impl SlotId {
1145                /// Zero-based slot index.
1146                fn index(self) -> u8 {
1147                    match self {
1148                        $(
1149                            SlotId::[<_ $idx >] => $idx,
1150                        )*
1151                    }
1152                }
1153            }
1154
1155            /// Maps a const slot index to its [`SlotId`].
1156            const fn slot_id(s: u8) -> SlotId {
1157                match s {
1158                    $($idx => SlotId::[<_ $idx>],)*
1159                    _ => ::core::unreachable!(),
1160                }
1161            }
1162        }
1163    };
1164}
1165
1166/// Static routing data for a slot.
1167fn slot_info(id: SlotId) -> &'static SlotInfo {
1168    &SLOT_INFO[id.index() as usize]
1169}
1170
1171/// Interrupt mask kept armed while idle: card-detect plus the SDIO card
1172/// interrupt of any slot that is listening. Preserves listening across
1173/// transfers (the transfer paths OR their bits onto this base).
1174fn idle_intmask() -> u32 {
1175    SDHOST::regs().intmask().read().bits() & (EVT_IO_SLOT0 | EVT_IO_SLOT1) | INTMASK_IDLE
1176}
1177
1178/// Arms `TRANSFER` for a new operation and clears stale status.
1179fn arm_transfer(expect_data: bool, multiblock: bool, transfer: Option<Transfer>) {
1180    TRANSFER.with(|ts| {
1181        *ts = TransferState {
1182            transfer,
1183            result: None,
1184            expect_data,
1185            multiblock,
1186            over_seen: false,
1187            acd_seen: false,
1188            wait_busy: false,
1189            waker: WakerRegistration::new(),
1190        };
1191    });
1192}
1193
1194/// SDMMC interrupt handler: refills the IDMAC ring and wakes the task.
1195#[handler]
1196fn on_interrupt() {
1197    let r = SDHOST::regs();
1198    let pending = r.mintsts().read().bits();
1199    let idsts = r.idsts().read().bits();
1200
1201    TRANSFER.with(|ts| {
1202        if ts.wait_busy {
1203            // For an R1b/no-data command the SBE bit doubles as the Busy
1204            // Clear Interrupt, fired when the card releases DAT0.
1205            if pending & EVT_SBE != 0 {
1206                ts.result = Some(Ok(()));
1207            }
1208        } else {
1209            // Refill descriptors the engine has released mid-transfer.
1210            if let Some(t) = ts.transfer.as_mut()
1211                && t.remaining() > 0
1212            {
1213                let mut ring = ring();
1214                let free = free_descriptors(ring.reborrow(), t.next_desc);
1215                if free > 0 {
1216                    fill_descriptors(ring.reborrow(), t, free);
1217                    r.pldmnd().write(|w| unsafe { w.bits(1) });
1218                }
1219            }
1220
1221            if ts.result.is_none() {
1222                let err = map_rintsts(pending)
1223                    .err()
1224                    .or(if idsts & (IDSTS_FBE | IDSTS_DU) != 0 {
1225                        Some(Error::DmaError)
1226                    } else {
1227                        None
1228                    });
1229                if let Some(e) = err {
1230                    ts.result = Some(Err(e));
1231                } else if ts.expect_data {
1232                    if pending & EVT_DATA_OVER != 0 {
1233                        ts.over_seen = true;
1234                    }
1235                    if pending & EVT_ACD != 0 {
1236                        ts.acd_seen = true;
1237                    }
1238                    if ts.over_seen && (!ts.multiblock || ts.acd_seen) {
1239                        ts.result = Some(Ok(()));
1240                    }
1241                } else if pending & EVT_CMD_DONE != 0 {
1242                    ts.result = Some(Ok(()));
1243                }
1244            }
1245        }
1246
1247        if ts.result.is_some() {
1248            ts.wait_busy = false;
1249            ts.transfer = None;
1250            r.intmask().write(|w| unsafe { w.bits(idle_intmask()) });
1251            r.idinten().write(|w| unsafe { w.bits(0) });
1252            ts.waker.wake();
1253        }
1254    });
1255
1256    // SDIO card interrupt (edge-triggered): latch it and wake. The write-1-clear
1257    // of `rintsts` below disarms the edge, so it won't refire until the card
1258    // signals again; no need to mask. The waiter consumes the latch on wake.
1259    if pending & EVT_IO_SLOT0 != 0 {
1260        SLOT_STATE[0].io_pending.store(true, Ordering::Release);
1261        SLOT_STATE[0].io_waker.wake();
1262    }
1263    if pending & EVT_IO_SLOT1 != 0 {
1264        SLOT_STATE[1].io_pending.store(true, Ordering::Release);
1265        SLOT_STATE[1].io_waker.wake();
1266    }
1267
1268    // Card-detect change: wake both slot listeners.
1269    if pending & EVT_CD != 0 {
1270        SLOT_STATE[0].cd_waker.wake();
1271        SLOT_STATE[1].cd_waker.wake();
1272    }
1273
1274    // Write-1-clear the bits handled this pass.
1275    r.rintsts().write(|w| unsafe { w.bits(pending) });
1276    r.idsts().write(|w| unsafe { w.bits(idsts) });
1277}
1278
1279/// SDMMC / SDIO host controller driver.
1280///
1281/// Owns the shared transfer engine and hands out per-slot handles.
1282pub struct SdHostController<'d> {
1283    _peri: SDHOST<'d>,
1284    _guard: PeripheralGuard,
1285    taken: [AtomicBool; SLOT_COUNT],
1286}
1287
1288impl<'d> SdHostController<'d> {
1289    /// Creates the controller, enabling its bus clock and configuring the
1290    /// shared (engine-wide) module clock from `config`.
1291    pub fn new(peri: SDHOST<'d>, config: Config) -> Result<Self, ConfigError> {
1292        config.validate()?;
1293        let guard = PeripheralGuard::new(Peripheral::SdioHost);
1294        chip_specific::chip_setup();
1295        let this = Self {
1296            _peri: peri,
1297            _guard: guard,
1298            taken: [const { AtomicBool::new(false) }; SLOT_COUNT],
1299        };
1300
1301        // Module clock first, then the DesignWare reset, then quiesce
1302        // interrupts. The module clock is engine-wide, so it is programmed once
1303        // here and the config is stashed for slots to derive their card
1304        // dividers from. The clock setup must precede `reset_engine` because
1305        // the module clock drives the FIFO reset.
1306        chip_specific::set_module_clock(config.clock_source, config.module_div);
1307        this.reset_engine();
1308
1309        SETTINGS.with(|s| s.module = config);
1310        let r = SDHOST::regs();
1311        r.tmout().write(|w| unsafe {
1312            w.response_timeout().bits(0xFF);
1313            w.data_timeout().bits(0xFF_FFFF)
1314        });
1315        r.rintsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
1316        r.ctrl().modify(|_, w| w.int_enable().clear_bit());
1317
1318        Ok(this)
1319    }
1320
1321    /// Resets the controller, FIFO and DMA blocks, waiting for self-clear.
1322    fn reset_engine(&self) {
1323        let r = SDHOST::regs();
1324        r.ctrl().modify(|_, w| {
1325            w.controller_reset().set_bit();
1326            w.fifo_reset().set_bit();
1327            w.dma_reset().set_bit()
1328        });
1329
1330        // A reset timeout means the controller never left reset; there is no
1331        // matching `ConfigError` and the first card command would surface it
1332        // as a timeout regardless, so this is best-effort.
1333        let _ = poll_until_timeout(Duration::from_millis(100), || {
1334            let c = r.ctrl().read();
1335            !c.controller_reset().bit_is_set()
1336                && !c.fifo_reset().bit_is_set()
1337                && !c.dma_reset().bit_is_set()
1338        });
1339    }
1340
1341    /// Returns a builder for the given slot in blocking mode.
1342    ///
1343    /// Both slots can be taken (once each); the shared engine serializes their
1344    /// transactions. Requesting the same slot twice returns
1345    /// [`ConfigError::SlotInUse`]. Blocking engine ops return
1346    /// [`BlockingError::Busy`] on contention.
1347    ///
1348    /// The returned slot borrows the controller, so the controller cannot be
1349    /// dropped while any of its slots are alive.
1350    pub fn slot<const S: u8>(
1351        &self,
1352        config: SlotConfig,
1353    ) -> Result<Slot<'_, S, Blocking>, ConfigError> {
1354        const { ::core::assert!(S < 2, "SDMMC has only slots 0 and 1") };
1355        let idx = S as usize;
1356        if self.taken[idx].swap(true, Ordering::Relaxed) {
1357            return Err(ConfigError::SlotInUse);
1358        }
1359        #[cfg(sdmmc_delay_phase_num_is_set)]
1360        SETTINGS.with(|s| {
1361            s.slots[idx].input_delay_phase = config.input_delay_phase;
1362            s.slots[idx].dirty = true;
1363        });
1364        Ok(Slot {
1365            config,
1366            data_pins: 0,
1367            clk_connected: false,
1368            cmd_connected: false,
1369            cd_connected: false,
1370            wp_connected: false,
1371            _guard: PeripheralGuard::new(Peripheral::SdioHost),
1372            power: None,
1373            _pd: PhantomData,
1374        })
1375    }
1376}
1377
1378/// Card-clock output pin for slot `S`.
1379pub trait SlotClk<'d, const S: u8> {
1380    #[doc(hidden)]
1381    fn configure(self);
1382}
1383
1384/// Command (bidirectional) pin for slot `S`.
1385pub trait SlotCmd<'d, const S: u8> {
1386    #[doc(hidden)]
1387    fn configure(self);
1388}
1389
1390/// Data line `L` (bidirectional) for slot `S`.
1391pub trait SlotData<'d, const S: u8, const L: u8> {
1392    #[doc(hidden)]
1393    fn configure(self);
1394}
1395
1396// GPIO-matrix-routed slots (all of S3; slot 1 on P4): any pin can carry any of
1397// the slot's signals, routed through the interconnect matrix. The impls accept
1398// any GPIO and stash the routing guard in the slot's `State` for its lifetime.
1399//
1400// Generated per matrix slot (`iomux = false`) rather than blanket over the slot
1401// index: on P4 the IO_MUX slot's bus signals are implemented for fixed pins
1402// below, and a blanket `impl<const S>` would collide with those.
1403#[cfg(sdmmc_has_gpio_matrix)]
1404for_each_sdmmc! {
1405    (
1406        $slot:ident, $idx:literal, false,
1407        [$($clk:ident)?], [$($cmd_in:ident)?], [$($cmd_out:ident)?],
1408        [$($data_in:ident),*], [$($data_out:ident),*],
1409        [$($cd:ident)?], [$($wp:ident)?], [$($card_int:ident)?],
1410        [$($data_strobe:ident)?], [$($rst:ident)?]
1411    ) => {
1412        impl<'d, P: PeripheralOutput<'d>> SlotClk<'d, $idx> for P {
1413            fn configure(self) {
1414                let pin = self.into();
1415                pin.apply_output_config(&OutputConfig::default());
1416                pin.set_output_enable(true);
1417                slot_pins(slot_id($idx)).clk = interconnect::OutputSignal::connect_with_guard(
1418                    pin,
1419                    slot_info(slot_id($idx)).clk_out.unwrap(),
1420                );
1421            }
1422        }
1423
1424        impl<'d, P: PeripheralInput<'d> + PeripheralOutput<'d>> SlotCmd<'d, $idx> for P {
1425            fn configure(self) {
1426                slot_pins(slot_id($idx)).cmd = connect_bidir(
1427                    self.into(),
1428                    slot_info(slot_id($idx)).cmd_in.unwrap(),
1429                    slot_info(slot_id($idx)).cmd_out.unwrap(),
1430                );
1431            }
1432        }
1433
1434        impl<'d, const L: u8, P: PeripheralInput<'d> + PeripheralOutput<'d>>
1435            SlotData<'d, $idx, L> for P
1436        {
1437            fn configure(self) {
1438                slot_pins(slot_id($idx)).data[L as usize] = connect_bidir(
1439                    self.into(),
1440                    slot_info(slot_id($idx)).data_in[L as usize],
1441                    slot_info(slot_id($idx)).data_out[L as usize],
1442                );
1443            }
1444        }
1445    };
1446
1447    // IO_MUX-routed slots (P4 slot 0) get their bus impls from the fixed-pin
1448    // block below; nothing to generate here.
1449    (
1450        $slot:ident, $idx:literal, true,
1451        [$($clk:ident)?], [$($cmd_in:ident)?], [$($cmd_out:ident)?],
1452        [$($data_in:ident),*], [$($data_out:ident),*],
1453        [$($cd:ident)?], [$($wp:ident)?], [$($card_int:ident)?],
1454        [$($data_strobe:ident)?], [$($rst:ident)?]
1455    ) => {};
1456}
1457
1458// IO_MUX-routed slots (both slots on ESP32; slot 0 on P4): each bus signal
1459// lives on a fixed pad selected by an IO_MUX function. The traits are
1460// implemented only for the mandated `(gpio, af)` pairs, so a wrong pin is a
1461// compile error. ESP32 names them `HS1_*` (slot 0) / `HS2_*` (slot 1); P4
1462// names slot 0's pads `SD1_*`.
1463#[cfg(sdmmc_has_iomux)]
1464fn configure_iomux_pad(pin: u8, af: crate::gpio::AlternateFunction) {
1465    crate::gpio::io_mux_reg(pin).modify(|_, w| {
1466        unsafe { w.mcu_sel().bits(af as u8) };
1467        w.fun_ie().set_bit();
1468        w.fun_wpu().set_bit();
1469        // esp-idf bumps the SD pads to the strongest drive on every chip
1470        // except ESP32 (where the default of 2 is sufficient). Matches
1471        // `configure_pin_iomux`.
1472        #[cfg(not(esp32))]
1473        unsafe {
1474            w.fun_drv().bits(3)
1475        };
1476        w
1477    });
1478}
1479
1480#[cfg(sdmmc_has_iomux)]
1481macro_rules! impl_signal_trait {
1482    ($gpio:ident, $trait:ident, $af:ident, $s:literal) => {
1483        impl<'d> $trait<'d, $s> for crate::peripherals::$gpio<'d> {
1484            fn configure(self) {
1485                configure_iomux_pad(
1486                    crate::gpio::Pin::number(&self),
1487                    crate::gpio::AlternateFunction::$af,
1488                );
1489            }
1490        }
1491    };
1492
1493    ($gpio:ident, $trait:ident, $af:ident, $s:literal, $l:literal) => {
1494        impl<'d> $trait<'d, $s, $l> for crate::peripherals::$gpio<'d> {
1495            fn configure(self) {
1496                configure_iomux_pad(
1497                    crate::gpio::Pin::number(&self),
1498                    crate::gpio::AlternateFunction::$af,
1499                );
1500            }
1501        }
1502    };
1503}
1504
1505// Arms for functions absent on a given chip simply never match (the generated macro ignores
1506// unmatched functions). P4 wires only slot 0 (`SD1_*`) to fixed pads; slot 1 is matrix.
1507#[cfg(sdmmc_has_iomux)]
1508for_each_iomux_function! {
1509    (SD1_CLK, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotClk, $af, 0); };
1510    (SD1_CMD, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotCmd, $af, 0); };
1511    (SD1_DATA0, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 0); };
1512    (SD1_DATA1, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 1); };
1513    (SD1_DATA2, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 2); };
1514    (SD1_DATA3, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 3); };
1515    (SD1_DATA4, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 4); };
1516    (SD1_DATA5, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 5); };
1517    (SD1_DATA6, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 6); };
1518    (SD1_DATA7, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 0, 7); };
1519
1520    (SD2_CLK, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotClk, $af, 1); };
1521    (SD2_CMD, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotCmd, $af, 1); };
1522    (SD2_DATA0, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 1, 0); };
1523    (SD2_DATA1, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 1, 1); };
1524    (SD2_DATA2, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 1, 2); };
1525    (SD2_DATA3, $gpio:ident, $af:ident) => { impl_signal_trait!($gpio, SlotData, $af, 1, 3); };
1526}
1527
1528/// A configured card slot, generic over the slot index and driver mode.
1529pub struct Slot<'d, const S: u8, Dm: DriverMode> {
1530    config: SlotConfig,
1531    data_pins: u8,
1532    clk_connected: bool,
1533    cmd_connected: bool,
1534    cd_connected: bool,
1535    wp_connected: bool,
1536    _guard: PeripheralGuard,
1537    power: Option<interconnect::OutputSignal<'d>>,
1538    _pd: PhantomData<(&'d mut (), Dm)>,
1539}
1540
1541impl<'d, const S: u8, Dm: DriverMode> Slot<'d, S, Dm> {
1542    /// Connects the card clock output.
1543    pub fn with_clk(mut self, clk: impl SlotClk<'d, S>) -> Self {
1544        clk.configure();
1545        self.clk_connected = true;
1546        self
1547    }
1548
1549    /// Connects the bidirectional command line.
1550    pub fn with_cmd(mut self, cmd: impl SlotCmd<'d, S>) -> Self {
1551        cmd.configure();
1552        self.cmd_connected = true;
1553        self
1554    }
1555
1556    /// Connects data line 0 (required for any transfer).
1557    pub fn with_data0(mut self, d0: impl SlotData<'d, S, 0>) -> Self {
1558        d0.configure();
1559        self.note_data_pin(1);
1560        self
1561    }
1562
1563    /// Connects data line 1.
1564    pub fn with_data1(mut self, d1: impl SlotData<'d, S, 1>) -> Self {
1565        d1.configure();
1566        self.note_data_pin(2);
1567        self
1568    }
1569
1570    /// Connects data line 2.
1571    pub fn with_data2(mut self, d2: impl SlotData<'d, S, 2>) -> Self {
1572        d2.configure();
1573        self.note_data_pin(3);
1574        self
1575    }
1576
1577    /// Connects data line 3 (completes the 4-bit bus).
1578    pub fn with_data3(mut self, d3: impl SlotData<'d, S, 3>) -> Self {
1579        d3.configure();
1580        self.note_data_pin(4);
1581        self
1582    }
1583
1584    /// Connects data line 4.
1585    pub fn with_data4(mut self, d4: impl SlotData<'d, S, 4>) -> Self {
1586        d4.configure();
1587        self.note_data_pin(5);
1588        self
1589    }
1590
1591    /// Connects data line 5.
1592    pub fn with_data5(mut self, d5: impl SlotData<'d, S, 5>) -> Self {
1593        d5.configure();
1594        self.note_data_pin(6);
1595        self
1596    }
1597
1598    /// Connects data line 6.
1599    pub fn with_data6(mut self, d6: impl SlotData<'d, S, 6>) -> Self {
1600        d6.configure();
1601        self.note_data_pin(7);
1602        self
1603    }
1604
1605    /// Connects data line 7 (completes the 8-bit bus).
1606    pub fn with_data7(mut self, d7: impl SlotData<'d, S, 7>) -> Self {
1607        d7.configure();
1608        self.note_data_pin(8);
1609        self
1610    }
1611
1612    /// Connects the card-detect input.
1613    pub fn with_card_detect(mut self, cd: impl PeripheralInput<'d>) -> Self {
1614        let pin = cd.into();
1615        pin.set_input_enable(true);
1616        slot_info(slot_id(S)).cd_in.unwrap().connect_to(&pin);
1617        self.cd_connected = true;
1618        self
1619    }
1620
1621    /// Connects the write-protect input.
1622    pub fn with_write_protect(mut self, wp: impl PeripheralInput<'d>) -> Self {
1623        let pin = wp.into();
1624        pin.set_input_enable(true);
1625        slot_info(slot_id(S)).wp_in.unwrap().connect_to(&pin);
1626        self.wp_connected = true;
1627        self
1628    }
1629
1630    /// Connects a GPIO used to switch card power. Driven high here.
1631    pub fn with_power_enable(mut self, power: impl PeripheralOutput<'d>) -> Self {
1632        let pin = power.into();
1633        pin.set_output_high(true);
1634        pin.apply_output_config(&OutputConfig::default());
1635        pin.set_output_enable(true);
1636        self.power = Some(pin);
1637        self
1638    }
1639
1640    /// Returns `true` if a card is detected, or if no card-detect pin is
1641    /// wired (assume present).
1642    pub fn is_card_present(&self) -> bool {
1643        if !self.cd_connected {
1644            return true;
1645        }
1646        (SDHOST::regs().cdetect().read().card_detect_n().bits() & (1 << S)) == 0
1647    }
1648
1649    /// Returns `true` if the card reports write protection. Returns `false`
1650    /// if no write-protect pin is wired. Polarity follows
1651    /// [`SlotConfig::with_wp_active_high`].
1652    pub fn is_write_protected(&self) -> bool {
1653        if !self.wp_connected {
1654            return false;
1655        }
1656        let level = (SDHOST::regs().wrtprt().read().write_protect().bits() & (1 << S)) != 0;
1657        level == self.config.wp_active_high
1658    }
1659
1660    /// Caches bus width and card clock; HW is programmed on the next engine op.
1661    pub fn set_bus_low_level(&mut self, width: BusWidth, hz: u32) -> Result<(), Error> {
1662        SETTINGS.with(|s| s.set_slot_bus(slot_id(S), width, hz))
1663    }
1664
1665    /// Checks that the mandatory pins for a transfer were connected.
1666    fn validate_pins(&self) -> Result<(), ConfigError> {
1667        if !self.clk_connected || !self.cmd_connected {
1668            return Err(ConfigError::MissingClkOrCmd);
1669        }
1670        if self.data_pins < 1 {
1671            return Err(ConfigError::NoData0);
1672        }
1673        Ok(())
1674    }
1675
1676    /// Issues the 80-clock SD init sequence (no command index).
1677    pub fn send_init_sequence(&mut self) -> Result<(), BlockingError> {
1678        with_engine_try(slot_id(S), |session| session.send_init_sequence(slot_id(S)))
1679    }
1680
1681    /// Issues a no-data command and returns its raw response words.
1682    pub fn command_blocking(
1683        &mut self,
1684        index: u8,
1685        arg: u32,
1686        resp_len: ResponseLen,
1687        check_crc: bool,
1688        flags: CommandFlags,
1689    ) -> Result<[u32; 4], BlockingError> {
1690        if !self.is_card_present() {
1691            return Err(BlockingError::Op(Error::NoCard));
1692        }
1693        let slot = slot_id(S);
1694        with_engine_try(slot, |session| {
1695            session.send_command_blocking(slot, index, arg, resp_len, check_crc, flags)
1696        })
1697    }
1698
1699    /// Reads `block_count` blocks into a DMA-capable buffer (CMD17/CMD18).
1700    pub fn read_blocks_blocking(
1701        &mut self,
1702        cmd_index: u8,
1703        arg: u32,
1704        mut buf: DmaAlignedMut<'_, [u8]>,
1705        block_size: u16,
1706        block_count: u32,
1707    ) -> Result<[u32; 4], BlockingError> {
1708        let slot = slot_id(S);
1709        with_engine_try(slot, |session| {
1710            let total = buf.len();
1711            let ptr = dma_ptr(buf.reborrow())?;
1712            let resp = session.transfer_blocking(
1713                slot,
1714                cmd_index,
1715                arg,
1716                false,
1717                Transfer::single(ptr, total),
1718                block_size,
1719                block_count,
1720            )?;
1721            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1722            buf.invalidate();
1723            Ok(resp)
1724        })
1725    }
1726
1727    /// Writes `block_count` blocks from a DMA-capable buffer (CMD24/CMD25).
1728    pub fn write_blocks_blocking(
1729        &mut self,
1730        cmd_index: u8,
1731        arg: u32,
1732        mut buf: DmaAlignedMut<'_, [u8]>,
1733        block_size: u16,
1734        block_count: u32,
1735    ) -> Result<[u32; 4], BlockingError> {
1736        let slot = slot_id(S);
1737        with_engine_try(slot, |session| {
1738            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1739            buf.writeback();
1740            let total = buf.len();
1741            let ptr = dma_ptr(buf.reborrow())?;
1742            session.transfer_blocking(
1743                slot,
1744                cmd_index,
1745                arg,
1746                true,
1747                Transfer::single(ptr, total),
1748                block_size,
1749                block_count,
1750            )
1751        })
1752    }
1753
1754    fn note_data_pin(&mut self, count: u8) {
1755        self.data_pins = self.data_pins.max(count);
1756    }
1757}
1758
1759impl<'d, const S: u8> Slot<'d, S, Blocking> {
1760    /// Reconfigures the slot to operate in [`Async`] mode.
1761    ///
1762    /// Binds the `SDHOST` interrupt handler and enables interrupt delivery.
1763    pub fn into_async(self) -> Slot<'d, S, Async> {
1764        let r = SDHOST::regs();
1765        r.rintsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
1766        r.idsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
1767        r.intmask().write(|w| unsafe { w.bits(idle_intmask()) });
1768        r.idinten().write(|w| unsafe { w.bits(0) });
1769        r.ctrl().modify(|_, w| w.int_enable().set_bit());
1770        crate::interrupt::bind_handler(Interrupt::SDIO_HOST, on_interrupt);
1771
1772        Slot {
1773            config: self.config,
1774            data_pins: self.data_pins,
1775            clk_connected: self.clk_connected,
1776            cmd_connected: self.cmd_connected,
1777            _guard: self._guard,
1778            power: self.power,
1779            cd_connected: self.cd_connected,
1780            wp_connected: self.wp_connected,
1781            _pd: PhantomData,
1782        }
1783    }
1784}
1785
1786impl<'d, const S: u8> Slot<'d, S, Async> {
1787    /// Reconfigures the slot back to [`Blocking`] mode.
1788    pub fn into_blocking(self) -> Slot<'d, S, Blocking> {
1789        let r = SDHOST::regs();
1790        r.ctrl().modify(|_, w| w.int_enable().clear_bit());
1791        r.intmask().write(|w| unsafe { w.bits(0) });
1792        r.idinten().write(|w| unsafe { w.bits(0) });
1793        crate::interrupt::disable(crate::system::Cpu::current(), Interrupt::SDIO_HOST);
1794
1795        Slot {
1796            config: self.config,
1797            data_pins: self.data_pins,
1798            clk_connected: self.clk_connected,
1799            cmd_connected: self.cmd_connected,
1800            _guard: self._guard,
1801            power: self.power,
1802            cd_connected: self.cd_connected,
1803            wp_connected: self.wp_connected,
1804            _pd: PhantomData,
1805        }
1806    }
1807
1808    /// Waits for the SDIO card interrupt (card pulls DAT1 low).
1809    #[must_use = "futures do nothing unless you `.await` or poll them"]
1810    pub fn wait_for_sdio_interrupt(&mut self) -> impl Future<Output = ()> {
1811        let slot_id = slot_id(S);
1812        let slot = slot_id.index() as usize;
1813        let bit = slot_info(slot_id).io_event;
1814
1815        WaitForInterruptFuture { slot, bit }
1816    }
1817
1818    /// Issues a control command, mapping protocol types to the engine core.
1819    async fn cmd_async<'a, C: sdio::ControlCommand + 'a>(
1820        &mut self,
1821        session: &mut EngineSession,
1822        cmd: C,
1823    ) -> Result<C::Resp<'a>, MmcError> {
1824        let slot = slot_id(S);
1825        let resp_len = match <C::Resp<'a> as sdio::Response>::LEN {
1826            sdio::ResponseLen::Zero => ResponseLen::None,
1827            sdio::ResponseLen::R48 => ResponseLen::Short,
1828            sdio::ResponseLen::R136 => ResponseLen::Long,
1829        };
1830        let flags = CommandFlags {
1831            wait_complete: !is_stop_or_abort(C::INDEX),
1832            stop_abort: is_stop_or_abort(C::INDEX),
1833            busy: <C::Resp<'a> as sdio::Response>::BUSY,
1834        };
1835        let crc = <C::Resp<'a> as sdio::Response>::CRC;
1836        let words = session
1837            .send_command_async(slot, C::INDEX, cmd.arg(), resp_len, crc, flags)
1838            .await?;
1839        Ok(<C::Resp<'a> as sdio::Response>::from_words(&words))
1840    }
1841}
1842
1843impl<'d, const S: u8> sdio::MmcBus for Slot<'d, S, Async> {
1844    async fn wait_for_event(&mut self) -> Result<(), MmcError> {
1845        self.wait_for_sdio_interrupt().await;
1846        Ok(())
1847    }
1848
1849    async fn send_command<'a, C>(&mut self, cmd: C) -> Result<C::Resp<'a>, MmcError>
1850    where
1851        C: sdio::ControlCommand + 'a,
1852    {
1853        let mut session = lock_engine(slot_id(S)).await?;
1854        self.cmd_async(&mut session, cmd).await
1855    }
1856
1857    async fn read_blocks<'a, C>(
1858        &mut self,
1859        mut cmd: C,
1860        auto_stop: bool,
1861    ) -> Result<C::Resp<'a>, MmcError>
1862    where
1863        C: sdio::BlockReadCommand + 'a,
1864    {
1865        let mut session = lock_engine(slot_id(S)).await?;
1866        let slot = slot_id(S);
1867        let (bs, arg) = (cmd.block_size().len() as u16, cmd.arg());
1868        let words = session
1869            .read_async(slot, C::INDEX, arg, cmd.buf(), bs, auto_stop)
1870            .await?;
1871        Ok(<C::Resp<'a> as sdio::Response>::from_words(&words))
1872    }
1873
1874    async fn write_blocks<'a, C>(
1875        &mut self,
1876        cmd: C,
1877        auto_stop: bool,
1878    ) -> Result<C::Resp<'a>, MmcError>
1879    where
1880        C: sdio::BlockWriteCommand + 'a,
1881    {
1882        let mut session = lock_engine(slot_id(S)).await?;
1883        let slot = slot_id(S);
1884        let (bs, arg) = (cmd.block_size().len() as u16, cmd.arg());
1885        let words = session
1886            .write_async(slot, C::INDEX, arg, cmd.buf(), bs, auto_stop)
1887            .await?;
1888        Ok(<C::Resp<'a> as sdio::Response>::from_words(&words))
1889    }
1890
1891    async fn read_bytes<'a, C>(&mut self, mut cmd: C) -> Result<C::Resp<'a>, MmcError>
1892    where
1893        C: sdio::ByteReadCommand + 'a,
1894    {
1895        let mut session = lock_engine(slot_id(S)).await?;
1896        let slot = slot_id(S);
1897        let (n, arg) = (cmd.byte_count(), cmd.arg());
1898        let words = session
1899            .read_async(slot, C::INDEX, arg, cmd.buf(), n as u16, false)
1900            .await?;
1901        Ok(<C::Resp<'a> as sdio::Response>::from_words(&words))
1902    }
1903
1904    async fn write_bytes<'a, C>(&mut self, cmd: C) -> Result<C::Resp<'a>, MmcError>
1905    where
1906        C: sdio::ByteWriteCommand + 'a,
1907    {
1908        let mut session = lock_engine(slot_id(S)).await?;
1909        let slot = slot_id(S);
1910        let (n, arg) = (cmd.byte_count(), cmd.arg());
1911        let words = session
1912            .write_async(slot, C::INDEX, arg, cmd.buf(), n as u16, false)
1913            .await?;
1914        Ok(<C::Resp<'a> as sdio::Response>::from_words(&words))
1915    }
1916
1917    async fn init_idle(&mut self, hz: u32) -> Result<(), MmcError> {
1918        let mut session = lock_engine(slot_id(S)).await?;
1919        self.validate_pins().map_err(|_| MmcError::Other)?;
1920        self.set_bus_low_level(BusWidth::Bit1, hz)?;
1921        session.send_init_sequence(slot_id(S))?;
1922        Ok(())
1923    }
1924
1925    fn set_bus(&mut self, width: sdio::BusWidth, hz: u32) -> Result<(), MmcError> {
1926        let w = match width {
1927            sdio::BusWidth::W1 => BusWidth::Bit1,
1928            sdio::BusWidth::W4 => BusWidth::Bit4,
1929            sdio::BusWidth::W8 => BusWidth::Bit8,
1930        };
1931        if matches!(w, BusWidth::Bit4) && self.data_pins < 4 {
1932            return Err(MmcError::Unsupported);
1933        }
1934        if matches!(w, BusWidth::Bit8) && self.data_pins < 8 {
1935            return Err(MmcError::Unsupported);
1936        }
1937        if hz > 40_000_000 {
1938            return Err(MmcError::Unsupported);
1939        }
1940        self.set_bus_low_level(w, hz)?;
1941        Ok(())
1942    }
1943
1944    fn supports_mmc(&self) -> bool {
1945        // Native parallel SD/MMC host (not SPI mode).
1946        true
1947    }
1948
1949    fn supports_auto_stop(&self) -> bool {
1950        true
1951    }
1952
1953    fn supports_bus_width(&self) -> sdio::BusWidth {
1954        if self.data_pins >= 8 {
1955            sdio::BusWidth::W8
1956        } else if self.data_pins >= 4 {
1957            sdio::BusWidth::W4
1958        } else {
1959            sdio::BusWidth::W1
1960        }
1961    }
1962
1963    fn supports_frequency(&self) -> u32 {
1964        40_000_000
1965    }
1966}
1967
1968/// Maps the engine's [`ResponseLen`] from the crate's typed response.
1969/// CMD12 (and SDIO abort) are stop/abort commands.
1970fn is_stop_or_abort(index: u8) -> bool {
1971    index == 12
1972}
1973
1974/// Whether the controller should auto-issue CMD12 (STOP_TRANSMISSION) after the
1975/// data phase.
1976///
1977/// Auto-stop is only valid for the open-ended SD/MMC memory multi-block
1978/// transfers (CMD18 `READ_MULTIPLE_BLOCK` / CMD25 `WRITE_MULTIPLE_BLOCK`).
1979/// SDIO CMD53 (`IO_RW_EXTENDED`) carries its own block count in the command and
1980/// must NOT be followed by CMD12: the card would not respond to it, surfacing as
1981/// a spurious `ResponseTimeout` at the end of the data phase.
1982fn needs_auto_stop(index: u8, block_count: u32) -> bool {
1983    block_count > 1 && matches!(index, 18 | 25)
1984}
1985
1986#[cfg(sdmmc_has_gpio_matrix)]
1987fn connect_bidir(
1988    pin: interconnect::OutputSignal<'_>,
1989    input: InputSignal,
1990    output: OutputSignal,
1991) -> PinGuard {
1992    pin.set_output_high(true);
1993    pin.apply_output_config(&OutputConfig::default().with_pull(Pull::Up));
1994    pin.set_output_enable(true);
1995    pin.set_input_enable(true);
1996    input.connect_to(&pin);
1997    interconnect::OutputSignal::connect_with_guard(pin, output)
1998}
1999
2000/// Spins up to `POLL_LIMIT` times until `ready` holds, else times out.
2001fn poll_until(mut ready: impl FnMut() -> bool) -> Result<(), Error> {
2002    for _ in 0..POLL_LIMIT {
2003        if ready() {
2004            return Ok(());
2005        }
2006    }
2007    Err(Error::Timeout)
2008}
2009
2010fn poll_until_timeout(timeout: Duration, mut ready: impl FnMut() -> bool) -> Result<(), Error> {
2011    let start = Instant::now();
2012    while !ready() {
2013        if start.elapsed() > timeout {
2014            return Err(Error::Timeout);
2015        }
2016    }
2017    Ok(())
2018}
2019
2020/// Spins until the CIU accepts the command (`start_cmd` self-clears).
2021fn wait_command_accepted() -> Result<(), Error> {
2022    let r = SDHOST::regs();
2023    for _ in 0..POLL_LIMIT {
2024        if !r.cmd().read().start_cmd().bit_is_set() {
2025            return Ok(());
2026        }
2027        // Hardware-locked error: clear and report.
2028        const HW_LOCKED: u32 = 1 << 12;
2029        if (r.rintsts().read().bits() & HW_LOCKED) != 0 {
2030            r.rintsts().write(|w| unsafe { w.bits(HW_LOCKED) });
2031            return Err(Error::Timeout);
2032        }
2033    }
2034    Err(Error::Timeout)
2035}
2036
2037/// Masks interrupts, tears down the IDMAC and clears transfer state.
2038fn abort_transfer() {
2039    let r = SDHOST::regs();
2040    r.intmask().write(|w| unsafe { w.bits(idle_intmask()) });
2041    r.idinten().write(|w| unsafe { w.bits(0) });
2042    disable_idmac();
2043    let _ = reset_transfer();
2044    TRANSFER.with(|ts| *ts = TransferState::IDLE);
2045}
2046
2047/// Awaits the terminal result the interrupt handler records.
2048#[must_use = "futures do nothing unless you `.await` or poll them"]
2049fn wait_result() -> impl Future<Output = Result<(), Error>> {
2050    poll_fn(|cx| {
2051        TRANSFER.with(|ts| match ts.result.take() {
2052            Some(res) => Poll::Ready(res),
2053            None => {
2054                ts.waker.register(cx.waker());
2055                Poll::Pending
2056            }
2057        })
2058    })
2059}
2060
2061/// Waits for the card to release DAT0 after a write via the Busy Clear
2062/// Interrupt.
2063///
2064/// The controller only raises the BCI (which shares the SBE status bit) for
2065/// data-write commands, and only when generation is enabled in `cardthrctl`;
2066/// the bit is left enabled solely for the duration of the wait so it cannot
2067/// be mistaken for a start-bit error during a multi-block transfer.
2068async fn wait_busy_async() -> Result<(), Error> {
2069    let r = SDHOST::regs();
2070
2071    // Fast path: the card may already be ready.
2072    if !r.status().read().data_busy().bit_is_set() {
2073        return Ok(());
2074    }
2075
2076    // Enable Busy Clear Interrupt generation, then arm and unmask it.
2077    r.cardthrctl().modify(|_, w| w.cardclrinten().set_bit());
2078    TRANSFER.with(|ts| {
2079        *ts = TransferState {
2080            wait_busy: true,
2081            ..TransferState::IDLE
2082        };
2083    });
2084    r.rintsts().write(|w| unsafe { w.bits(EVT_SBE) });
2085    r.intmask()
2086        .write(|w| unsafe { w.bits(idle_intmask() | EVT_SBE) });
2087
2088    // Close the arm race: if busy cleared between the check above and the
2089    // unmask, the rising edge is already gone, so bail out instead of
2090    // waiting for an interrupt that will never fire.
2091    let res = if !r.status().read().data_busy().bit_is_set() {
2092        TRANSFER.with(|ts| *ts = TransferState::IDLE);
2093        Ok(())
2094    } else {
2095        wait_result().await
2096    };
2097
2098    // Restore: stop generating the BCI and re-mask the shared SBE bit.
2099    r.cardthrctl().modify(|_, w| w.cardclrinten().clear_bit());
2100    r.intmask().write(|w| unsafe { w.bits(idle_intmask()) });
2101    res
2102}
2103
2104/// Polls DAT0 while the card signals busy (R1b).
2105///
2106/// Used after no-data commands (e.g. `CMD7`, `MMC_SWITCH`): the controller
2107/// does not generate a Busy Clear Interrupt for those, so polling is the
2108/// only option, mirroring ESP-IDF's `wait_for_busy_cleared`.
2109async fn wait_busy_poll() -> Result<(), Error> {
2110    for _ in 0..POLL_LIMIT {
2111        if !SDHOST::regs().status().read().data_busy().bit_is_set() {
2112            return Ok(());
2113        }
2114        yield_now().await;
2115    }
2116    Err(Error::Timeout)
2117}
2118
2119/// Reads response registers into spec word order (`words[3]` is the MSW).
2120fn read_response(resp_len: ResponseLen) -> [u32; 4] {
2121    let r = SDHOST::regs();
2122    match resp_len {
2123        ResponseLen::None => [0; 4],
2124        ResponseLen::Short => [r.resp0().read().bits(), 0, 0, 0],
2125        ResponseLen::Long => [
2126            r.resp0().read().bits(),
2127            r.resp1().read().bits(),
2128            r.resp2().read().bits(),
2129            r.resp3().read().bits(),
2130        ],
2131    }
2132}
2133
2134/// Maps `rintsts` error bits to an [`Error`]; errors take priority.
2135fn map_rintsts(sts: u32) -> Result<(), Error> {
2136    if sts & EVT_HLE != 0 {
2137        return Err(Error::HardwareLocked);
2138    }
2139    if sts & EVT_RTO != 0 {
2140        return Err(Error::ResponseTimeout);
2141    }
2142    if sts & EVT_RCRC != 0 {
2143        return Err(Error::ResponseCrc);
2144    }
2145    if sts & EVT_RESP_ERR != 0 {
2146        return Err(Error::ResponseError);
2147    }
2148    if sts & (EVT_DTO | EVT_HTO) != 0 {
2149        return Err(Error::DataTimeout);
2150    }
2151    if sts & (EVT_DCRC | EVT_EBE) != 0 {
2152        return Err(Error::DataCrc);
2153    }
2154    if sts & EVT_SBE != 0 {
2155        return Err(Error::StartBitError);
2156    }
2157    if sts & EVT_FRUN != 0 {
2158        return Err(Error::FifoOverrun);
2159    }
2160    Ok(())
2161}
2162
2163/// Polls DAT0 until the card releases the busy signal.
2164fn wait_busy_cleared() -> Result<(), Error> {
2165    poll_until(|| !SDHOST::regs().status().read().data_busy().bit_is_set())
2166}
2167
2168/// Validates a buffer address for the SDMMC IDMAC and returns its DMA pointer.
2169///
2170/// The IDMAC reaches PSRAM only on chips with `sdmmc_psram_dma`.
2171fn dma_ptr(buf: DmaAlignedMut<'_, [u8]>) -> Result<u32, Error> {
2172    dma_ptr_from_raw(buf.as_ptr())
2173}
2174
2175/// Validates a buffer address for the SDMMC IDMAC and returns its DMA pointer.
2176///
2177/// The IDMAC reaches PSRAM only on chips with `sdmmc_psram_dma`.
2178fn dma_ptr_ref(buf: DmaAlignedRef<'_, [u8]>) -> Result<u32, Error> {
2179    dma_ptr_from_raw(buf.as_ptr())
2180}
2181
2182fn dma_ptr_from_raw(addr: *const u8) -> Result<u32, Error> {
2183    // Not in some weird region like flash or RTC memory.
2184    if crate::soc::is_valid_ram_address(addr as usize) {
2185        return Ok(addr as u32);
2186    }
2187    #[cfg(all(soc_has_psram, sdmmc_psram_dma))]
2188    if crate::soc::is_valid_psram_address(addr as usize) {
2189        return Ok(addr as u32);
2190    }
2191
2192    Err(Error::BufferNotDmaCapable)
2193}
2194
2195/// Waits for the command response then the data phase, refilling the ring.
2196fn run_data_phase(
2197    t: &mut Transfer,
2198    mut ring: DmaAlignedMut<'_, [Desc; RING_LEN]>,
2199    write: bool,
2200    auto_stop: bool,
2201) -> Result<(), Error> {
2202    let r = SDHOST::regs();
2203    let consume = EVT_CMD_DONE | EVT_RTO | EVT_RCRC | EVT_RESP_ERR | EVT_HLE;
2204
2205    // Command response phase.
2206    let done = EVT_CMD_DONE | EVT_RTO | EVT_RCRC | EVT_RESP_ERR;
2207    let mut sts = 0;
2208    poll_until(|| {
2209        sts = r.rintsts().read().bits();
2210        sts & done != 0
2211    })?;
2212    map_rintsts(sts)?;
2213    r.rintsts().write(|w| unsafe { w.bits(consume) });
2214
2215    // Data phase: wait for completion, refilling descriptors as the engine
2216    // frees them. No CPU-side iteration cap: a stalled card or host-side
2217    // FIFO starvation is terminated by the controller's data timeout
2218    // (DRTO/HTO), and a DMA fault by IDMAC FBE/DU, so the loop can only spin
2219    // while the transfer is genuinely making progress.
2220    let data_err = EVT_DCRC | EVT_DTO | EVT_HTO | EVT_SBE | EVT_EBE | EVT_FRUN;
2221    loop {
2222        let sts = r.rintsts().read().bits();
2223        if sts & data_err != 0 {
2224            map_rintsts(sts)?;
2225        }
2226        let id_sts = r.idsts().read();
2227        if id_sts.fbe().bit_is_set() || id_sts.du().bit_is_set() {
2228            return Err(Error::DmaError);
2229        }
2230        if t.remaining() > 0 {
2231            let free = free_descriptors(ring.reborrow(), t.next_desc);
2232            if free > 0 {
2233                fill_descriptors(ring.reborrow(), t, free);
2234                r.pldmnd().write(|w| unsafe { w.bits(1) });
2235            }
2236        }
2237        if sts & EVT_DATA_OVER != 0 {
2238            break;
2239        }
2240    }
2241
2242    // Open-ended memory multi-block transfers append an auto-stop (CMD12);
2243    // wait for its completion. This follows DATA_OVER promptly, so a bounded
2244    // wait is fine, but a missing completion must be reported rather than
2245    // silently ignored. SDIO CMD53 sends no CMD12, so there is nothing to wait
2246    // for.
2247    if auto_stop {
2248        poll_until(|| r.rintsts().read().bits() & EVT_ACD != 0)?;
2249    }
2250    if write {
2251        wait_busy_cleared()?;
2252    }
2253    Ok(())
2254}
2255
2256/// Counts descriptors the engine has released, starting at `next`.
2257fn free_descriptors(ring: DmaAlignedMut<'_, [Desc; RING_LEN]>, next: usize) -> usize {
2258    let mut count = 0;
2259    for i in 0..RING_LEN {
2260        let d = &ring[(next + i) % RING_LEN];
2261        if d.flags & DESC_OWN != 0 {
2262            break;
2263        }
2264        count += 1;
2265        if d.next == 0 {
2266            break;
2267        }
2268    }
2269    count
2270}
2271
2272/// Fills up to `count` descriptors from the remaining transfer (shared
2273/// blocking/async refill; mirrors `sd_host_fill_dma_descriptors`).
2274fn fill_descriptors(mut ring: DmaAlignedMut<'_, [Desc; RING_LEN]>, t: &mut Transfer, count: usize) {
2275    for _ in 0..count {
2276        // Skip exhausted (or empty) segments to find the next bytes to link.
2277        while t.seg < t.segs.len() && t.segs[t.seg].1 == 0 {
2278            t.seg += 1;
2279        }
2280        if t.seg >= t.segs.len() {
2281            break;
2282        }
2283
2284        let (ptr, rem) = t.segs[t.seg];
2285        let i = t.next_desc;
2286        let size = rem.min(DMA_MAX_BUF_LEN);
2287        // The transfer ends here only if this chunk drains the current segment
2288        // and no later segment still has data to link.
2289        let exhausts_seg = size == rem;
2290        let later_data = t.segs[t.seg + 1..].iter().any(|(_, len)| *len > 0);
2291        let last = exhausts_seg && !later_data;
2292        let next_ptr = if last {
2293            0
2294        } else {
2295            &ring[(i + 1) % RING_LEN] as *const Desc as u32
2296        };
2297        let first = ring[i].flags & DESC_FIRST;
2298        let d = &mut ring[i];
2299        d.flags = DESC_OWN | DESC_CHAINED | first | if last { DESC_LAST } else { 0 };
2300        d.sizes = ((size + 3) & !3) as u32;
2301        d.buf1 = ptr;
2302        d.next = next_ptr;
2303        t.segs[t.seg].0 = ptr + size as u32;
2304        t.segs[t.seg].1 = rem - size;
2305        t.next_desc = (i + 1) % RING_LEN;
2306    }
2307
2308    #[cfg(soc_internal_memory_cached)]
2309    ring.writeback();
2310}
2311
2312fn reset_transfer() -> Result<(), Error> {
2313    let r = SDHOST::regs();
2314    r.ctrl().modify(|_, w| w.fifo_reset().set_bit());
2315    let res = poll_until(|| !r.ctrl().read().fifo_reset().bit_is_set());
2316
2317    r.rintsts()
2318        .write(|w| unsafe { w.bits(!(EVT_IO_SLOT0 | EVT_IO_SLOT1)) });
2319    r.idsts().write(|w| unsafe { w.bits(0xFFFF_FFFF) });
2320
2321    res
2322}
2323
2324/// Enables the IDMAC engine for a transfer and programs the descriptor base.
2325///
2326/// `BMOD.SWR` resets the IDMAC's internal registers (including `DBADDR`) on
2327/// this IP, so the descriptor base must be written *after* the reset,
2328/// matching the ordering ESP-IDF uses in `sdmmc_host_dma_prepare`.
2329fn enable_idmac(dbaddr: u32) {
2330    let r = SDHOST::regs();
2331    r.ctrl()
2332        .modify(|rd, w| unsafe { w.bits(rd.bits() | CTRL_DMA_ENABLE | CTRL_USE_INTERNAL_DMA) });
2333    r.bmod().modify(|_, w| w.swr().set_bit());
2334    r.idinten().write(|w| unsafe { w.bits(0) });
2335    r.dbaddr().write(|w| unsafe { w.bits(dbaddr) });
2336    r.bmod().modify(|_, w| {
2337        w.de().set_bit();
2338        w.fb().set_bit()
2339    });
2340}
2341
2342/// Disables the IDMAC engine after a transfer.
2343fn disable_idmac() {
2344    let r = SDHOST::regs();
2345    r.ctrl()
2346        .modify(|rd, w| unsafe { w.bits(rd.bits() & !CTRL_USE_INTERNAL_DMA) });
2347    r.bmod().modify(|_, w| {
2348        w.de().clear_bit();
2349        w.fb().clear_bit()
2350    });
2351    r.ctrl().modify(|_, w| w.dma_reset().set_bit());
2352}
2353
2354/// Module clock in Hz for a given source and divider.
2355fn module_hz(source: ClockSource, div: u8) -> u32 {
2356    let base = match source {
2357        #[cfg(esp32s31)]
2358        ClockSource::Mpll => 500_000_000,
2359        #[cfg(not(esp32s31))]
2360        ClockSource::Pll160m => 160_000_000,
2361        //#[cfg(esp32p4)]
2362        // ClockSource::Apll => unimplemented!(),
2363        #[cfg(not(esp32p4))]
2364        ClockSource::Xtal => 40_000_000,
2365    };
2366    base / (div as u32)
2367}
2368
2369/// Computes the per-card divider for a target frequency.
2370///
2371/// `card_clk = module / (2 * div)`; `div == 0` bypasses the divider.
2372fn freq_to_card_div(module_hz: u32, target_hz: u32) -> u8 {
2373    if target_hz == 0 || module_hz <= target_hz {
2374        return 0;
2375    }
2376    let div = module_hz.div_ceil(2 * target_hz);
2377    div.clamp(1, 255) as u8
2378}
2379
2380#[must_use = "futures do nothing unless you `.await` or poll them"]
2381struct WaitForInterruptFuture {
2382    slot: usize,
2383    bit: u32,
2384}
2385
2386impl Future for WaitForInterruptFuture {
2387    type Output = ();
2388    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2389        // Register before consuming so an edge racing the check wakes us.
2390        SLOT_STATE[self.slot].io_waker.register(cx.waker());
2391
2392        SDHOST::regs()
2393            .intmask()
2394            .modify(|rd, w| unsafe { w.bits(rd.bits() | self.bit) });
2395
2396        if SLOT_STATE[self.slot]
2397            .io_pending
2398            .swap(false, Ordering::AcqRel)
2399        {
2400            Poll::Ready(())
2401        } else {
2402            Poll::Pending
2403        }
2404    }
2405}
2406
2407impl Drop for WaitForInterruptFuture {
2408    fn drop(&mut self) {
2409        SDHOST::regs()
2410            .intmask()
2411            .modify(|rd, w| unsafe { w.bits(rd.bits() & !self.bit) });
2412    }
2413}