Building an SDMMC HAL - Part 1 of N: 2025-06-16


The last month or so has been an interesting journey into implementing embedded-hal-inspired traits for SD/MMC peripherals. It started with what seemed like a simple task: "load the Linux kernel from an SD/MMC card into RAM". On a baremetal with peripherals with wildly varying quality of documentation, it has been anything but simple.

I have written a somewhat makeshift bootloader for JH7110-based boards. I’m using a VisionFive2 as a dev board, but it should also work on any of the now numerous JH7110 boards. The bootloader is a standalone RustSBI implementation that I wrote for the JH7110: rustsbi-jh7110. The rustsbi-jh7110 binary brings up the basic peripherals of the board, and (will eventually) hand off control to Supervisor software, like the Linux kernel.

Theoretically, rustsbi-jh7110 is already capable of performing this handoff. If the Linux kernel could fit inside SRAM along with rustsbi-jh7110 (unlikely, SRAM is only 2MB), then rustsbi-jh7110 could easily hand control to the kernel. In fact, I’ve written a test "kernel" that already does this. The hard part is reading kernel from one of the storage peripherals with sufficient capacity: an SD card and/or eMMC module.

Data structures

As a first attempt to write generic software supporting SD/MMC peripherals, I wrote sdmmc-core with common data types used in the SD/SDIO + eMMC protocols. Luckily, the simplified SD/SDIO specifications and eMMC specification are publicly available (even if the full/latest versions are behind a high paywall). The freely available versions are enough to get started. sdmmc-core provides the core data types for things like commands, responses, and common registers. Development is still early stages, but enough is implemented to start building on.

Drivers

To build something I could experiment with, I created sdmmc-driver as an extension of sdmmc-core. The crate provides traits heavily influenced by embedded-hal, and some early experiments from jh7xx-hal to support its sdmmc peripherals. Ultimately, the mature driver may end up in embedded-sdmmc-rs, or remain separate for users who just want an SD/MMC driver without a bundled FATFS filesystem driver, TBD.

Regardless, through experimenting I clarified the delineation for the HAL-based traits, and what fit better into the driver-specific traits. So, I opened discussion with members of the rust-embedded working group, and began work on the initial `embedded_hal::sdmmc` module to host SD/MMC HAL traits.

As part of a suggestion to prove the traits, I set out to implement the traits on at least two devices. Since I started with the JH7110, and have an ESP32-C6 dev board, those are the devices I’m using to prove the traits.

Luckily, the JH7110 has an SD/MMC host controller, and the ESP32-C6 has an SD/MMC device controller.

You can follow my current work on the initial ESP32 SDIO HAL implementation, if you’re interested. As part of that work, it was suggested to break out the SD/MMC HAL traits into their own crate, instead of directly implementing the traits in embedded-hal. Following this advice, I created embedded-hal-sdmmc. Hopefully, this will allow for faster development iteration, and avoid causing conflicts with existing embedded-hal workspace dependencies inside esp-hal.

Documentation

This has probably been the single biggest roadblock on the entire path to a functioning SD/MMC driver. The documentation for the JH7110, the JH7110 Technical Reference Manual is very detailed for the SoC as a whole. However, a number of peripherals are still proprietary hardware IP, notably the Synopsys DesignWare SD/MMC host controller. Synopsys maintains documentation for the controller, but absolutely DO NOT offer that to open-hardware, FOSS developers like yours truly (believe me, I tried). Unless you are a hardware manufacturer integrating their IP, and shipping millions of units, forget about it. Which means gleaning as much information about registers, initialization, read/write operations, etc. from available open-source implementations.

I chose to consult the Linux & U-Boot drivers, with the U-Boot driver being a bit simpler. The source code is readable, high-quality C code, which is nice. However, because they are OS-level drivers with decades of peripherals to support across numerous platforms, the generic code is still difficult to follow. Let alone extracting the necessary information specifically for the device itself, and not the code used to manipulate OS-internals like state machines. This has been an absolute slog. The side-benefit, I’m now much more familiar with Linux + U-Boot internals :)

At the exact opposite end of the spectrum is the Espressif documentation for the ESP32-C6. It’s all open, accompanied by example implementations in their esp-idf and esp-hal repositories. Their work on the ESSL, their device-side SDIO driver, was even split out from the main esp-idf implementation. Reading their accompanying ESP SDIO Protocol, and ESP SDIO Driver documentation has been so helpful in continuing to develop the SDIO driver.

Please, PLEASE more companies be like Espressif. Open your documentation to the world, allowing passionate developers to build great software on top of your hardware.

Next steps

Currently, I’m working on a bit banging GPIO driver for the JH7110 host to drive the ESP32-C6 device SDIO peripheral. Thankfully, the ESP32-C6 dev boards have the SDIO pins broken out into the GPIO pins, which should make it easy to set up a breadboard DUT circuit (or just messy jumper cables).

I’ll continue this series with updates on my progress in Part 2.

Wish me luck :)