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 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -5,6 +5,7 @@
pub mod delay;
pub mod digital;
pub mod i2c;
pub mod mmc;
pub mod pwm;
pub mod spi;

Expand Down
101 changes: 101 additions & 0 deletions embedded-hal/src/mmc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Types and traits for SD/MMC peripherals.

mod bus_width;
mod fifo_status;
mod reset;

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

pub use bus_width::BusWidth;
pub use fifo_status::FifoStatus;
pub use reset::Reset;

use command::MmcCommand;
use response::MmcResponse;
use tuning::TuningMode;

/// Common operations for DesignWare MMC controllers on JH7110 SoCs.
pub trait MmcOps {
/// Associated error type for the SD/MMC trait.
type Error;

/// Gets whether the device is a MMC card.
fn is_mmc(&self) -> bool;

/// Gets whether the device is a SD card.
fn is_sd(&self) -> bool {
!self.is_mmc()
}

/// Gets whether the device is configured for SPI mode.
fn is_spi(&self) -> bool;

/// 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>;

/// 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>;

/// 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>;

/// 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>;

/// Reads the raw response bytes from the MMC controller.
///
/// # Note
///
/// Set `exp_crc` to true if a CRC checksum is expected in the response.
///
/// The generic `N` parameter is for the expected length (in bytes) of the response.
fn response_bytes<const N: usize>(&mut self, exp_crc: bool) -> Result<[u8; N], 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>;

/// Requests the card to send a tuning block.
fn send_tuning(&mut self, bus_width: BusWidth, mode: TuningMode) -> Result<(), Self::Error>;

/// Executes MMC tuning.
fn execute_tuning(&mut self, bus_width: BusWidth, mode: TuningMode) -> 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);
}
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` field of the [Argument](super::Argument).
#[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()
}
}
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()
}
}
36 changes: 36 additions & 0 deletions embedded-hal/src/mmc/tuning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Tuning data for SD/MMC peripherals.

/// Represents the tuning mode for the SD/MMC device.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TuningMode {
/// Represents a 4-bit tuning block.
Block4bit = 0,
/// Represents a 8-bit tuning block.
Block8bit = 1,
}

/// 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,
];
Loading