Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

phytium-mci

A no_std Rust driver for SD/MMC cards on Phytium E2000 series SoCs.

RustCrates.ioDocumentationLicense

Overview

phytium-mci is a comprehensive SD/MMC host controller driver designed for Phytium SoC platforms (specifically the E2000 series used in Phytium Pi development boards). It implements the full SD/MMC protocol stack from hardware register manipulation to high-level card operations, supporting both SD and eMMC cards with DMA and PIO transfer modes.

Key Features

  • Full SD Specification Support: SDSC, SDHC, SDXC (Specification versions 1.0-3.0)
  • eMMC Support: MMC protocol implementation
  • Flexible Transfer Modes: DMA (high-performance) and PIO (simple) transfers
  • Voltage Support: 3.3V (default) and 1.8V (UHS-I modes)
  • Bus Widths: 1-bit, 4-bit, and 8-bit (eMMC) data bus
  • High-Speed Modes: SDR12, SDR25, SDR50, SDR104, DDR50
  • Clock Speed Support: From 400 KHz (initialization) up to 208 MHz (SDR104)
  • Card Detection: GPIO-based and host-based card detection
  • Interrupt Support: Command completion, data transfer, and card detection interrupts
  • Platform Abstraction: Clean separation through the Kernel trait

Architecture

The driver is organized into distinct layers:

Application Layer (SdCard, MCIHost - High-level API)
↓
Protocol Layer (Command/Data transfer, Card initialization)
↓
Hardware Abstraction (Register access, DMA/PIO control)
↓
Hardware Support (IoPad pin configuration, OSA memory/timing)

Module Structure

  • mci/ - Hardware controller driver (register access, DMA/PIO, interrupts)
  • mci_host/ - Host controller protocol layer (SD/MMC protocol implementation)
  • iopad/ - I/O pad configuration for pin multiplexing
  • osa/ - OS abstraction layer (memory management, event flags)

Requirements

  • Rust 2024 edition
  • Phytium E2000 series SoC or compatible platform
  • no_std environment (bare-metal or custom OS)

Dependencies

tock-registers = "0.9.0"# Type-safe register accesslog = "0.4"# Logging facadenb = "1.1"# Non-blocking I/Obitflags = "2.8"# Bit flagsbytemuck = "1.22.0"# Safe byte castinglazy_static = "1.5.0"# Global statespin = "0.10.0"# Spin locksrlsf = "0.2.1"# Memory allocator

Features

FeatureDescriptionDefault
dmaEnable DMA transfersNo
pioEnable PIO transfersYes
pollEnable polling modeYes
irqEnable interrupt modeNo
# Default: PIO + Poll mode (simpler, good for debugging)
[dependencies]
phytium-mci = { version = "0.1.0" }
# Recommended: DMA + IRQ (high performance)
[dependencies]
phytium-mci = { version = "0.1.0", features = ["dma", "irq"] }

Usage

1. Platform Integration

Implement the Kernel trait to provide platform-specific functionality:

use phytium_mci::{Kernel, set_impl};use core::{ptr::NonNull, time::Duration};structMyPlatform;implKernelforMyPlatform{fnsleep(duration:Duration){// Platform-specific delay implementationplatform_delay(duration);}#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64{// Virtual to physical address translation for DMAplatform_virt_to_phys(virt_addr)}fnflush(addr:NonNull<u8>,size:usize){// Cache clean for DMAplatform_cache_clean(addr, size);}fninvalidate(addr:NonNull<u8>,size:usize){// Cache invalidate for DMAplatform_cache_invalidate(addr, size);}}// Register your implementationset_impl!(MyPlatform);

2. Basic SD Card Initialization

use phytium_mci::{sd::SdCard,IoPad};use core::ptr::NonNull;fnmain(){// Get register base addresses from device tree or platform configlet mci_reg_base = 0x2800_1000as*mutu8;let iopad_reg_base = 0x2800_0000as*mutu8;// Initialize IOPAD for pin configurationlet iopad = unsafe{IoPad::new(NonNull::new_unchecked(iopad_reg_base))};// Create SD card instanceletmut sdcard = unsafe{SdCard::new(NonNull::new_unchecked(mci_reg_base),
iopad
)};// Initialize the cardifletErr(e) = sdcard.init(NonNull::new_unchecked(mci_reg_base)){panic!("SD card init failed: {:?}", e);}println!("Card initialized!");println!("Block size: {} bytes", sdcard.block_size());println!("Total blocks: {}", sdcard.block_count());println!("Total capacity: {} MB", sdcard.capacity() / (1024*1024));}

3. Reading Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnread_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){letmut buffer = Vec::new();
sdcard.read_blocks(&mut buffer, start_block, block_count).expect("Read failed");println!("Read {} blocks ({} bytes)", block_count, buffer.len()*4);}

4. Writing Blocks

use phytium_mci::sd::SdCard;use alloc::vec::Vec;fnwrite_blocks(sdcard:&mutSdCard,start_block:u32,block_count:u32){// Prepare data buffer (blocks are in 32-bit words)letmut buffer:Vec<u32> = Vec::with_capacity((block_count *128)asusize);
buffer.resize((block_count *128)asusize,0);// Fill with patternfor i in0..buffer.len(){
buffer[i] = i asu32;}// Write blocks
sdcard.write_blocks(&mut buffer, start_block, block_count).expect("Write failed");println!("Written {} blocks starting at {}", block_count, start_block);}

5. Configuration for Different Modes

use phytium_mci::mci::MCIConfig;use phytium_mci::mci_host::MCIHostConfig;use phytium_mci::mci_host::MCIHostType;use phytium_mci::mci_host::MCIHostCardType;use phytium_mci::mci_host::MCIHostEndianMode;// For DMA mode (high performance)let host_config = MCIHostConfig{host_type:MCIHostType::SDIF,card_type:MCIHostCardType::SDCard,card_clock:50_000_000,// 50 MHzmax_trans_size:512*1024,// 512KB max transferdef_block_size:512,enable_dma:true,is_uhs_card:true,// Enable UHS-I supportendian_mode:MCIHostEndianMode::Little,};

Hardware Details

Target Hardware

ComponentDescription
SoCPhytium E2000 series (ARMv8-A architecture)
BoardPhytium Pi development board
ControllerPhytium SDIF (Synopsys DesignWare-based)
MCI0 Base0x2800_1000
MCI1 Base0x2800_2000
IOPAD Base0x2800_0000

Clock Configuration

  • Source Clock: 1.2 GHz
  • Initialization: 400 KHz (for card detection and initialization)
  • Default Speed: 25 MHz
  • High Speed: 50 MHz
  • UHS-I SDR104: Up to 208 MHz

Voltage Modes

ModeVoltageBus WidthMax Clock
Default3.3V1-bit/4-bit25 MHz
High Speed3.3V4-bit50 MHz
SDR121.8V4-bit25 MHz
SDR251.8V4-bit50 MHz
SDR501.8V4-bit100 MHz
SDR1041.8V4-bit208 MHz
DDR501.8V4-bit50 MHz

API Reference

Main Types

SdCard

High-level SD card interface.

implSdCard{pubunsafefnnew(reg_base:NonNull<u8>,io_pad:IoPad) -> Self;pubfninit(&mutself,reg_base:NonNull<u8>) -> Result<(),MCIHostError>;pubfnread_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnwrite_blocks(&mutself,buf:&mutVec<u32>,start:u32,cnt:u32) -> Result<(),MCIHostError>;pubfnblock_size(&self) -> u32;pubfnblock_count(&self) -> u32;pubfncapacity(&self) -> u64;pubfncid(&self) -> &SdCid;pubfncsd(&self) -> &SdCsd;pubfnscr(&self) -> &SdScr;}

MCIHost

Host controller abstraction.

implMCIHost{pubfnnew(dev:Box<SDIFDev>,config:MCIHostConfig) -> Self;pubfninit(&mutself) -> Result<(),MCIHostError>;pubfntransfer(&mutself,content:&mutMCICmdData) -> Result<(),MCIHostError>;pubfnset_card_bus_width(&mutself,width:MCIHostBusWdith) -> Result<(),MCIHostError>;pubfnset_card_clock(&mutself,freq:u32) -> Result<(),MCIHostError>;}

IoPad

I/O pad configuration for pin multiplexing.

implIoPad{pubunsafefnnew(reg:NonNull<u8>) -> Self;pubfninit(&mutself) -> Result<(),IoPadError>;pubfnset_pin_function(&mutself,pin:u8,func:PinFunction) -> Result<(),IoPadError>;pubfnset_pin_pull(&mutself,pin:u8,pull:PinPull) -> Result<(),IoPadError>;}

Card Information Structures

pubstructSdCid{pubmanufacturer_id:u8,puboem_id:[u8;2],pubproduct_name:[u8;5],pubproduct_revision:u8,pubserial_number:u32,pubmonth:u8,pubyear:u16,}pubstructSdCsd{pubcard_capacity:u64,pubread_block_length:u32,pubwrite_speed:u32,}pubstructSdScr{pubsd_spec:u8,pubbus_widths:[bool;3],}

Error Handling

The crate provides comprehensive error types:

// MCI (Hardware) errorspubenumMCIError{Timeout,// Operation timeoutNotInit,// Controller not initializedShortBuf,// Buffer too smallNotSupport,// Operation not supportedInvalidState,// Invalid controller stateTransTimeout,// Transfer timeoutCmdTimeout,// Command timeoutNoCard,// No card detectedBusy,// Card busyDmaBufUnalign,// DMA buffer misalignedInvalidTiming,// Invalid timing configuration}// Host (Protocol) errorspubenumMCIHostError{Fail,TransferFailed,Timeout,Busy,NoData,NotSupportYet,CardNotSupport,HostNotSupport,SwitchVoltageFail,TuningFail,CardInitFailed,// ... 60+ specific error variants}

Memory Management

The crate includes a custom TLSF-based memory pool allocator for DMA operations:

use phytium_mci::osa::{FMemp,PoolBuffer};// Initialize the global memory poolunsafe{FMemp::init(pool_base, pool_size);}// Allocate aligned buffer for DMAlet buffer = PoolBuffer::alloc(4096,512).expect("Allocation failed");// Use buffer...// Buffer is automatically freed when dropped

Testing

⚠️Hardware integration tests require physical Phytium Pi hardware.

This project provides bare-metal integration tests that run on actual Phytium Pi hardware to verify SD/MMC card functionality.

Prerequisites

  1. Phytium Pi Hardware

    • Phytium Pi development board
    • SD card inserted
    • Serial port connected
  2. Install ostool:

    cargo install ostool
  3. Configure device tree (use firmware/phytium.dtb)

Running Hardware Tests

# Build and run on Phytium Pi
cargo test --test test --target aarch64-unknown-none -- --show-output uboot
# PIO mode only
cargo test --test test --target aarch64-unknown-none --no-default-features --features pio -- --show-output uboot

IMPORTANT: Hardware integration tests CANNOT run on:

  • ❌ Virtual machines or emulators
  • ❌ x86_64 or other non-ARM platforms
  • ❌ Systems without SD/MMC hardware

The tests communicate via serial port and require:

  • U-Boot with TFTP support
  • Physical Phytium Pi hardware
  • Working SD card interface

Platform Abstraction

The Kernel trait provides a clean abstraction for platform-specific operations:

pubtraitKernel{fnsleep(duration:Duration);#[cfg(feature = "dma")]fnmmap(virt_addr:NonNull<u8>) -> u64;fnflush(addr:NonNull<u8>,size:usize);fninvalidate(addr:NonNull<u8>,size:usize);}

This allows the driver to work with:

  • Bare-metal applications
  • Custom operating systems
  • Embedded frameworks

Card Information

The driver provides detailed card information:

let sdcard:&SdCard = /* ... */;// Basic informationprintln!("Capacity: {} MB", sdcard.capacity() / (1024*1024));println!("Block size: {} bytes", sdcard.block_size());println!("Block count: {}", sdcard.block_count());// Card identificationprintln!("Manufacturer ID: {:#02x}", sdcard.cid().manufacturer_id);println!("Product name: {}", sdcard.cid().product_name);println!("Serial number: {:#x}", sdcard.cid().serial_number);println!("Manufacturing date: {}/{}", sdcard.cid().month, sdcard.cid().year);// Card specific dataprintln!("Card capacity: {}", sdcard.csd().card_capacity());println!("Read block length: {}", sdcard.csd().read_block_length());println!("Write speed: {}", sdcard.csd().write_speed());// SD configurationprintln!("SD version: {}", sdcard.scr().sd_spec());println!("Bus width support: {:#?}", sdcard.scr().bus_widths());

License

This project is licensed under MIT.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions related to Phytium hardware, please visit:

Releases

Packages

Used by

Contributors

Languages