diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index f5eb76c3..2c798285 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -5,6 +5,7 @@ pub mod delay; pub mod digital; pub mod i2c; +pub mod mmc; pub mod pwm; pub mod spi; diff --git a/embedded-hal/src/mmc.rs b/embedded-hal/src/mmc.rs new file mode 100644 index 00000000..9cdc5275 --- /dev/null +++ b/embedded-hal/src/mmc.rs @@ -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(&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(&mut self, cmd: &C) -> Result; + + /// 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(&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); +} diff --git a/embedded-hal/src/mmc/bus_width.rs b/embedded-hal/src/mmc/bus_width.rs new file mode 100644 index 00000000..e89dd814 --- /dev/null +++ b/embedded-hal/src/mmc/bus_width.rs @@ -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() + } +} diff --git a/embedded-hal/src/mmc/command.rs b/embedded-hal/src/mmc/command.rs new file mode 100644 index 00000000..cd64b04d --- /dev/null +++ b/embedded-hal/src/mmc/command.rs @@ -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); +} diff --git a/embedded-hal/src/mmc/command/types.rs b/embedded-hal/src/mmc/command/types.rs new file mode 100644 index 00000000..cf002001 --- /dev/null +++ b/embedded-hal/src/mmc/command/types.rs @@ -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, +} diff --git a/embedded-hal/src/mmc/fifo_status.rs b/embedded-hal/src/mmc/fifo_status.rs new file mode 100644 index 00000000..123cf8f6 --- /dev/null +++ b/embedded-hal/src/mmc/fifo_status.rs @@ -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() + } +} diff --git a/embedded-hal/src/mmc/reset.rs b/embedded-hal/src/mmc/reset.rs new file mode 100644 index 00000000..14b27ffb --- /dev/null +++ b/embedded-hal/src/mmc/reset.rs @@ -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() + } +} diff --git a/embedded-hal/src/mmc/response.rs b/embedded-hal/src/mmc/response.rs new file mode 100644 index 00000000..12a339fd --- /dev/null +++ b/embedded-hal/src/mmc/response.rs @@ -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; +} diff --git a/embedded-hal/src/mmc/response/mode.rs b/embedded-hal/src/mmc/response/mode.rs new file mode 100644 index 00000000..1e33b899 --- /dev/null +++ b/embedded-hal/src/mmc/response/mode.rs @@ -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, +} diff --git a/embedded-hal/src/mmc/response/types.rs b/embedded-hal/src/mmc/response/types.rs new file mode 100644 index 00000000..9953016d --- /dev/null +++ b/embedded-hal/src/mmc/response/types.rs @@ -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() + } +} diff --git a/embedded-hal/src/mmc/tuning.rs b/embedded-hal/src/mmc/tuning.rs new file mode 100644 index 00000000..ebdb0f77 --- /dev/null +++ b/embedded-hal/src/mmc/tuning.rs @@ -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, +];