sdhci-host
no_std SD Host Controller Interface (SDHCI v3.x) backend for
sdmmc-protocol.
This crate plugs SDHCI register programming into the
sdmmc_protocol::sdio::SdioHost trait so SdioSdmmc can drive a real
controller. Platform code is still responsible for MMIO mapping, clock/reset
tree setup, power rails, pinmux, IRQ routing, and DMA cache coherency.
Status
- Compiles as a
no_stdcontroller backend. - Intended for use through
sdmmc_protocol::sdio::SdioSdmmc. - Board-specific clock, power, pinmux, and DMA policy must be supplied by the caller.
- Real hardware bring-up still depends on the surrounding SoC integration.
Scope
| Area | Implemented |
|---|---|
| PIO read / write | ✅ |
| ADMA2 (32-bit) read / write | ✅ |
| 1-bit / 4-bit bus | ✅ |
| Default speed | ✅ |
| High Speed (50 MHz) | ✅ |
| 32-bit / 136-bit responses | ✅ |
| Software reset / clock setup | ✅ |
| External platform-clock callback | ✅ |
| 1.8 V signaling bit path | ✅ (board validation required) |
| Controller tuning entry points | ✅ (board validation required) |
| ADMA2 (64-bit / v4) | ❌ |
| 8-bit eMMC bus | ❌ (returns UnsupportedCommand) |
| eMMC EXT_CSD path | ❌ |
Usage
use NonNull;
use ;
use Sdhci;
// SAFETY: 0xFE31_0000 must point at a valid SDHCI register file the
// caller has exclusive access to.
let mmio = new.unwrap;
let mut host = unsafe ;
host.reset_all?;
host.set_power;
host.enable_interrupts;
host.enable_clock?;
let mut card = new;
let mut scratch = new;
let mut request = card.submit_init?;
while let Pending = card.poll_init_request?
# Ok::
Construction is unsafe because the caller must guarantee that the supplied
address is a valid, exclusively-owned SDHCI register file for the lifetime of
the driver.
Block Request Usage
Use Sdhci::submit_read_blocks / Sdhci::submit_write_blocks, then drive
completion with Sdhci::poll_block_request. BlockTransferMode::Dma uses
ADMA2 and owns request-buffer mapping, descriptor allocation, descriptor
cache sync, and completion sync. BlockTransferMode::Fifo uses the FIFO
path with the same submit/poll shape, so platform code can fall back when DMA
is unavailable.
use ;
use DeviceDma;
use ;
# use DmaImpl;
let dma = new;
let mut host = unsafe ;
let mut block = ;
let ptr = new.unwrap;
let mut slot = default;
let mut request = Some;
let id = new;
while matches!
Platform code should implement dma_api::DmaOp and keep OS-specific mapping
and cache maintenance there. Sdhci::block_buffer_config exposes the FIFO or
ADMA2 queue constraints so adapters can translate them into their runtime's
block-buffer contract.
Bring-up checklist
- Map the SDHCI register file (RK3568:
0xFE31_0000). - Configure the platform clock so the controller has a viable reference
clock before calling
Sdhci::new(RK3568 needs the CRU bringingCLK_EMMC_COREup at ≥ 25 MHz). host.reset_all()?— clears CMD/DAT inhibits and the interrupt registers.host.set_power(POWER_330)(or whatever your card needs).host.enable_interrupts()— enables status flags. The driver polls; it does NOT enable signal-level IRQ delivery.host.enable_clock(base_hz, 400_000)— start at 400 kHz for identification.- Build
SdioSdmmc::new(host), submit initialization withsubmit_init, and drive it withpoll_init_request. The protocol layer will ramp the clock up to 25 MHz / 50 MHz viaset_clock; platform/runtime code chooses whether pending work spins, yields, or waits for an IRQ.
If the SoC requires external clock-tree programming for each SD speed, implement
sdhci_host::HostClock in platform glue and register it with
Sdhci::set_external_clock; the driver will gate the SD clock, call that clock
capability with the target frequency, and re-enable external-clock mode.
Testing
From this crate directory:
In this workspace, prefer the project xtask flow for final validation:
License
Licensed under the Apache License, Version 2.0.