From 2c733306dcfecd74f04ac6ac6ead65eaae39d178 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Wed, 23 Apr 2025 01:57:12 +0000 Subject: [PATCH 1/3] hal: initial SD/MMC implementation Adds the initial implementation of the `embedded-hal` SD/MMC traits. Includes types and traits useful for handling SD/MMC peripherals. --- embedded-hal/src/lib.rs | 1 + embedded-hal/src/mmc.rs | 101 +++++++++++++++++++++++++ embedded-hal/src/mmc/bus_width.rs | 24 ++++++ embedded-hal/src/mmc/command.rs | 36 +++++++++ embedded-hal/src/mmc/command/types.rs | 13 ++++ embedded-hal/src/mmc/fifo_status.rs | 24 ++++++ embedded-hal/src/mmc/reset.rs | 34 +++++++++ embedded-hal/src/mmc/response.rs | 16 ++++ embedded-hal/src/mmc/response/mode.rs | 11 +++ embedded-hal/src/mmc/response/types.rs | 84 ++++++++++++++++++++ embedded-hal/src/mmc/tuning.rs | 36 +++++++++ 11 files changed, 380 insertions(+) create mode 100644 embedded-hal/src/mmc.rs create mode 100644 embedded-hal/src/mmc/bus_width.rs create mode 100644 embedded-hal/src/mmc/command.rs create mode 100644 embedded-hal/src/mmc/command/types.rs create mode 100644 embedded-hal/src/mmc/fifo_status.rs create mode 100644 embedded-hal/src/mmc/reset.rs create mode 100644 embedded-hal/src/mmc/response.rs create mode 100644 embedded-hal/src/mmc/response/mode.rs create mode 100644 embedded-hal/src/mmc/response/types.rs create mode 100644 embedded-hal/src/mmc/tuning.rs diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index f5eb76c32..2c798285d 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 000000000..9cdc52751 --- /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 000000000..e89dd814e --- /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 000000000..cd64b04d5 --- /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 000000000..cf002001f --- /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 000000000..123cf8f69 --- /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 000000000..14b27ffb7 --- /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 000000000..12a339fd0 --- /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 000000000..1e33b8998 --- /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 000000000..9953016df --- /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 000000000..ebdb0f773 --- /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, +]; From b46c26f7ae272ef3cb3adda5bc996a7e45d4f45c Mon Sep 17 00:00:00 2001 From: rmsyn Date: Wed, 30 Apr 2025 23:51:28 +0000 Subject: [PATCH 2/3] mmc: use new-types for the card type Adds the `CardType` enum to represent the device type for a SD/MMC perpipheral. --- embedded-hal/src/mmc.rs | 11 ++++------- embedded-hal/src/mmc/card_type.rs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 embedded-hal/src/mmc/card_type.rs diff --git a/embedded-hal/src/mmc.rs b/embedded-hal/src/mmc.rs index 9cdc52751..00875f084 100644 --- a/embedded-hal/src/mmc.rs +++ b/embedded-hal/src/mmc.rs @@ -1,6 +1,7 @@ //! Types and traits for SD/MMC peripherals. mod bus_width; +mod card_type; mod fifo_status; mod reset; @@ -9,6 +10,7 @@ pub mod response; pub mod tuning; pub use bus_width::BusWidth; +pub use card_type::CardType; pub use fifo_status::FifoStatus; pub use reset::Reset; @@ -21,13 +23,8 @@ 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 [CardType]. + fn card_type(&self) -> CardType; /// Gets whether the device is configured for SPI mode. fn is_spi(&self) -> bool; diff --git a/embedded-hal/src/mmc/card_type.rs b/embedded-hal/src/mmc/card_type.rs new file mode 100644 index 000000000..749d082fa --- /dev/null +++ b/embedded-hal/src/mmc/card_type.rs @@ -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() + } +} From fc49578f0e9acc89f37f741d619217d717e61f99 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 1 May 2025 00:03:06 +0000 Subject: [PATCH 3/3] mmc: use new-types for the card mode Adds the `CardMode` enum to represent the device mode for a SD/MMC perpipheral. --- embedded-hal/src/mmc.rs | 8 ++++--- embedded-hal/src/mmc/card_mode.rs | 39 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 embedded-hal/src/mmc/card_mode.rs diff --git a/embedded-hal/src/mmc.rs b/embedded-hal/src/mmc.rs index 00875f084..40cd4cc6c 100644 --- a/embedded-hal/src/mmc.rs +++ b/embedded-hal/src/mmc.rs @@ -1,6 +1,7 @@ //! Types and traits for SD/MMC peripherals. mod bus_width; +mod card_mode; mod card_type; mod fifo_status; mod reset; @@ -10,6 +11,7 @@ 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; @@ -23,11 +25,11 @@ pub trait MmcOps { /// Associated error type for the SD/MMC trait. type Error; - /// Gets whether the device [CardType]. + /// Gets the device [CardType]. fn card_type(&self) -> CardType; - /// Gets whether the device is configured for SPI mode. - fn is_spi(&self) -> bool; + /// 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>; diff --git a/embedded-hal/src/mmc/card_mode.rs b/embedded-hal/src/mmc/card_mode.rs new file mode 100644 index 000000000..cfe3ac98a --- /dev/null +++ b/embedded-hal/src/mmc/card_mode.rs @@ -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() + } +}