Skip to content

sdmmc: add HAL traits for SD/MMC peripherals #662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions embedded-hal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
pub mod delay;
pub mod digital;
pub mod i2c;
pub mod mmc;
pub mod pwm;
pub mod spi;

108 changes: 108 additions & 0 deletions embedded-hal/src/mmc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Types and traits for SD/MMC peripherals.
mod bus_width;
mod card_mode;
mod card_type;
mod fifo_status;
mod reset;

pub mod command;
pub mod response;
pub mod tuning;

pub use bus_width::BusWidth;
pub use card_mode::CardMode;
pub use card_type::CardType;
pub use fifo_status::FifoStatus;
pub use reset::Reset;

use command::MmcCommand;
use response::MmcResponse;
use tuning::{TuningMode, TuningWidth};

/// Common operations for SD/MMC peripherals.
pub trait MmcCommon {
/// Associated error type for the SD/MMC trait.
type Error;

/// Gets the device [CardType].
fn card_type(&self) -> CardType;

/// Gets the device [CardMode].
fn card_mode(&self) -> CardMode;

/// Performs bus setup for the SD/MMC device.
fn setup_bus(&mut self) -> Result<(), Self::Error>;

/// Performs device initialization sequence.
fn init(&mut self) -> Result<(), Self::Error>;

/// Waits for the CMD line to reset (usually during power-up).
fn wait_for_reset(&mut self, reset: Reset, timeout: u64) -> Result<(), Self::Error>;

/// Waits for the busy signal to clear for maximum `timeout_us` microseconds.
fn wait_while_busy(&mut self, timout_us: u64) -> Result<(), Self::Error>;

/// Reads data from the MMC data lines.
fn read_data(&mut self, data: &mut [u8]) -> Result<(), Self::Error>;

/// Writes data to the MMC data lines.
fn write_data(&mut self, data: &[u8]) -> Result<(), Self::Error>;

/// Sets the sample phase for the MMC controller.
fn set_sample_phase(&mut self, sample_phase: u8);

/// Waits for the FIFO to indicate readiness for read/write operations.
fn fifo_ready(&self, fifo_status: FifoStatus) -> Result<(), Self::Error>;

/// Handles tuning block requests.
///
/// For hosts:
///
/// - requests the device to send a tuning block
///
/// For devices:
///
/// - sends the host the requested tuning block
fn send_tuning(&mut self, mode: TuningMode, width: TuningWidth) -> Result<(), Self::Error>;

/// Gets the interrupts status as a 32-bit bitfield.
fn interrupt(&self) -> u32;

/// Sets the interrupts based on a 32-bit bitfield.
fn set_interrupt(&mut self, int: u32);

/// Clear all interrupts.
fn clear_all_interrupt(&mut self);

/// Gets the response interrupts status as a 32-bit bitfield.
fn response_interrupt(&self) -> u32;

/// Sets the response interrupts based on a 32-bit bitfield.
fn set_response_interrupt(&mut self, int: u32);

/// Clear all interrupts.
fn clear_all_response_interrupt(&mut self);
}

/// Common operations for SD/MMC host peripherals.
pub trait MmcHost: MmcCommon {
/// Writes a SD/MMC command to the card.
fn write_command<C: MmcCommand>(&mut self, cmd: &C) -> Result<(), Self::Error>;

/// Reads a SD/MMC response based on the provided command argument.
///
/// # Note
///
/// `cmd` should match the last call to `write_command`.
fn read_response<C: MmcCommand, R: MmcResponse>(&mut self, cmd: &C) -> Result<R, Self::Error>;
}

/// Common operations for SD/MMC device peripherals.
pub trait MmcDevice: MmcCommon {
/// Reads a SD/MMC command sent from the host.
fn read_command<C: MmcCommand>(&mut self) -> Result<C, Self::Error>;

/// Writes a SD/MMC response based on the previous command.
fn write_response<R: MmcResponse>(&mut self, response: &R) -> Result<(), Self::Error>;
}
24 changes: 24 additions & 0 deletions embedded-hal/src/mmc/bus_width.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// Represents the variants of the `bus width` configuration for the SD/MMC peripheral.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BusWidth {
/// Represents the selection of a 1-bit bus width.
Bits1 = 0b00,
/// Represents the selection of a 4-bit bus width.
Bits4 = 0b10,
/// Represents the selection of a 8-bit bus width.
Bits8 = 0b11,
}

impl BusWidth {
/// Creates a new [BusWidth].
pub const fn new() -> Self {
Self::Bits1
}
}

impl Default for BusWidth {
fn default() -> Self {
Self::new()
}
}
39 changes: 39 additions & 0 deletions embedded-hal/src/mmc/card_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// Represents the card mode of the peripheral.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CardMode {
/// Represents a device in SD mode.
Sd,
/// Represents a device in SDIO mode.
Sdio,
/// Represents a device in SPI mode.
Spi,
}

impl CardMode {
/// Creates a new [CardMode].
pub const fn new() -> Self {
Self::Sd
}

/// Convenience function to get if the [CardMode] is SD.
pub const fn is_sd(&self) -> bool {
matches!(self, Self::Sd)
}

/// Convenience function to get if the [CardMode] is SDIO.
pub const fn is_sdio(&self) -> bool {
matches!(self, Self::Sdio)
}

/// Convenience function to get if the [CardMode] is SPI.
pub const fn is_spi(&self) -> bool {
matches!(self, Self::Spi)
}
}

impl Default for CardMode {
fn default() -> Self {
Self::new()
}
}
32 changes: 32 additions & 0 deletions embedded-hal/src/mmc/card_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// Represents the card type of the peripheral.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CardType {
/// Represents a SD device.
Sd,
/// Represents a MMC device.
Mmc,
}

impl CardType {
/// Creates a new [CardType].
pub const fn new() -> Self {
Self::Sd
}

/// Convenience function to get if the [CardType] is a SD.
pub const fn is_sd(&self) -> bool {
matches!(self, Self::Sd)
}

/// Convenience function to get if the [CardType] is a MMC.
pub const fn is_mmc(&self) -> bool {
matches!(self, Self::Mmc)
}
}

impl Default for CardType {
fn default() -> Self {
Self::new()
}
}
36 changes: 36 additions & 0 deletions embedded-hal/src/mmc/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! SD/MMC command types.
use super::response::ResponseType;

mod types;

pub use types::*;

/// Represents common functionality for SD/MMC command types.
pub trait MmcCommand {
/// Gets the SD/MMC command type.
fn command_type(&self) -> CommandType;

/// Gets the SD/MMC response type expected for the command.
fn response_type(&self) -> ResponseType;

/// Gets the SD/MMC command argument.
///
/// # Note
///
/// Returns `0` for commands that do not expect an argument.
fn argument(&self) -> u32;

/// Gets the SD/MMC command argument.
///
/// # Note
///
/// No effect for commands that do not expect an argument.
fn set_argument(&mut self, arg: u32);

/// Gets the CRC-7 of the command.
fn crc(&self) -> u8;

/// Sets the CRC-7 of the command.
fn set_crc(&mut self, crc: u8);
}
13 changes: 13 additions & 0 deletions embedded-hal/src/mmc/command/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Represents SD/MMC command types.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CommandType {
/// Addressed commands: point-to-point, no data transfer on DAT.
Ac = 0,
/// Addressed commands: point-to-point, data transfer on DAT.
Adtc = 1,
/// Broadcast commands no response. Only available if all CMD lines connected.
Bc = 2,
/// Broadcast commands with response. Only available if all CMD lines separated.
Bcr = 3,
}
24 changes: 24 additions & 0 deletions embedded-hal/src/mmc/fifo_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! FIFO status types.
/// Represents the FIFO status of the host controller.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FifoStatus {
/// MMC FIFO is empty.
Empty = 0,
/// MMC FIFO is full.
Full = 1,
}

impl FifoStatus {
/// Creates a new [FifoStatus].
pub const fn new() -> Self {
Self::Empty
}
}

impl Default for FifoStatus {
fn default() -> Self {
Self::new()
}
}
34 changes: 34 additions & 0 deletions embedded-hal/src/mmc/reset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! SD/MMC reset types.
/// Represents the resets to enable on the MMC host controller.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Reset {
/// Reset the MMC peripheral.
Mmc = 1,
/// Reset the FIFO peripheral.
Fifo = 2,
/// Reset the DMA peripheral.
Dma = 4,
/// Reset the MMC + FIFO peripherals.
MmcFifo = 3,
/// Reset the MMC + DMA peripherals.
MmcDma = 5,
/// Reset the FIFO + DMA peripherals.
FifoDma = 6,
/// Reset all peripherals.
All = 7,
}

impl Reset {
/// Creates a new [Reset].
pub const fn new() -> Self {
Self::Mmc
}
}

impl Default for Reset {
fn default() -> Self {
Self::new()
}
}
16 changes: 16 additions & 0 deletions embedded-hal/src/mmc/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! SD/MMC response types.
mod mode;
mod types;

pub use mode::*;
pub use types::*;

/// Represents common functionality for SD/MMC response types.
pub trait MmcResponse {
/// Gets the SD/MMC response type.
fn response_type(&self) -> ResponseType;

/// Gets the SD/MMC response mode.
fn response_mode(&self) -> ResponseMode;
}
11 changes: 11 additions & 0 deletions embedded-hal/src/mmc/response/mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// Represents the response mode of the SD/MMC protocol.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ResponseMode {
/// Standard SD mode of operation.
Sd,
/// SDIO mode of operation.
Sdio,
/// SPI mode of operation.
Spi,
}
84 changes: 84 additions & 0 deletions embedded-hal/src/mmc/response/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! SD/MMC response types.
use super::ResponseMode;

/// Represents the response types used in the SD/MMC protocol.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ResponseType {
/// No response type.
None,
/// The standard response sent for most command types.
R1,
/// The same as the `R1` response, but drives a `BUSY` signal on the `DAT` line(s).
R1b,
/// 136-bit response that includes the contents of the card `CID` or `CSD` register.
R2,
/// Returns the contents of the card `OCR` register.
R3,
/// SDIO response to the `IO_SEND_OP_COND` command.
///
/// Returns the card `IO_OCR` register contents, and other operating conditions.
R4,
/// SDIO response to the `IO_RW_DIRECT` commands.
R5,
/// Response containing the published RCA information.
R6,
/// Response containing the card interface condition.
R7,
}

impl ResponseType {
/// Represents the byte length for an 8-bit response.
pub const LEN_8BIT: usize = 1;
/// Represents the byte length for an 16-bit response.
pub const LEN_16BIT: usize = 2;
/// Represents the byte length for an 40-bit response.
pub const LEN_40BIT: usize = 5;
/// Represents the byte length for an 48-bit response.
pub const LEN_48BIT: usize = 6;
/// Represents the byte length for an 136-bit response.
pub const LEN_136BIT: usize = 17;
/// Represents the byte length for no response.
pub const LEN_NONE: usize = 0;

/// Creates a new [ResponseType].
pub const fn new() -> Self {
Self::R1
}

/// Gets the byte length of the [ResponseType] based on the operation mode.
pub const fn len(&self, mode: ResponseMode) -> usize {
match (mode, self) {
(
ResponseMode::Sd,
Self::R1 | Self::R1b | Self::R3 | Self::R4 | Self::R6 | Self::R7,
) => Self::LEN_48BIT,
(ResponseMode::Sd | ResponseMode::Sdio, Self::R2) => Self::LEN_136BIT,
(ResponseMode::Sdio, Self::R1 | Self::R1b | Self::R4 | Self::R5 | Self::R6) => {
Self::LEN_48BIT
}
(ResponseMode::Spi, Self::R1 | Self::R1b) => Self::LEN_8BIT,
(ResponseMode::Spi, Self::R2 | Self::R5) => Self::LEN_16BIT,
(ResponseMode::Spi, Self::R3 | Self::R4 | Self::R7) => Self::LEN_40BIT,
_ => Self::LEN_NONE,
}
}

/// Gets whether the response type includes a `CRC-7` checksum field.
pub const fn has_crc(&self, mode: ResponseMode) -> bool {
matches!(
(mode, self),
(
ResponseMode::Sd,
Self::R1 | Self::R1b | Self::R2 | Self::R4 | Self::R5 | Self::R6 | Self::R7
)
) || matches!((mode, self), (ResponseMode::Sdio, Self::R5 | Self::R6))
}
}

impl Default for ResponseType {
fn default() -> Self {
Self::new()
}
}
48 changes: 48 additions & 0 deletions embedded-hal/src/mmc/tuning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Tuning data for SD/MMC peripherals.
/// Represents the tuning width for the SD/MMC device.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TuningWidth {
/// Represents a 4-bit tuning block.
Bits4 = 0,
/// Represents a 8-bit tuning block.
Bits8 = 1,
}

/// Represents the tuning speed mode for the SD/MMC device.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TuningMode {
/// Represents a standard tuning mode.
Standard = 0,
/// Represents a high-speed HS200 tuning mode.
Hs200 = 1,
/// Represents a high-speed HS400 tuning mode.
Hs400 = 2,
}

/// Represents the byte length of the 4-bit tuning block.
pub const TUNING_BLOCK_4BIT_LEN: usize = 64;
/// Represents the byte length of the 8-bit tuning block.
pub const TUNING_BLOCK_8BIT_LEN: usize = 128;

/// Represents the tuning pattern used for 4-bit SD/MMC cards.
pub const TUNING_BLOCK_PATTERN_4BIT: [u8; TUNING_BLOCK_4BIT_LEN] = [
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
];

/// Represents the tuning pattern used for 8-bit SD/MMC cards.
pub const TUNING_BLOCK_PATTERN_8BIT: [u8; TUNING_BLOCK_8BIT_LEN] = [
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
];