From 280d21a6b415a1c856d08d338afd829b41e9b0b8 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 24 Mar 2025 10:49:47 -0700 Subject: [PATCH 1/9] update sdio-host to 0.6 --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/sdmmc/mod.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8204a0fea..8468436d8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" futures-util = { version = "0.3.30", default-features = false } rand_core = "0.6.3" -sdio-host = "0.5.0" +sdio-host = "0.6.0" critical-section = "1.1" #stm32-metapac = { version = "16" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 8f3c45f50..3cfae7ee1 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -10,7 +10,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; +use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR, SD}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; @@ -162,13 +162,13 @@ pub struct Card { /// The type of this card pub card_type: CardCapacity, /// Operation Conditions Register - pub ocr: OCR, + pub ocr: OCR, /// Relative Card Address pub rca: u32, /// Card ID - pub cid: CID, + pub cid: CID, /// Card Specific Data - pub csd: CSD, + pub csd: CSD, /// SD CARD Configuration Register pub scr: SCR, /// SD Status @@ -765,7 +765,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &Card) -> Result { + fn read_status(&self, card: &Card) -> Result, Error> { let regs = T::regs(); let rca = card.rca; @@ -1089,7 +1089,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Err(Error::Crc) => (), Err(err) => return Err(err), } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); + let ocr: OCR = regs.respr(0).read().cardstatus().into(); if !ocr.is_busy() { // Power up done break ocr; @@ -1098,9 +1098,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { if ocr.high_capacity() { // Card is SDHC or SDXC or SDUC - card.card_type = CardCapacity::SDHC; + card.card_type = CardCapacity::HighCapacity; } else { - card.card_type = CardCapacity::SDSC; + card.card_type = CardCapacity::StandardCapacity; } card.ocr = ocr; @@ -1193,7 +1193,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes let address = match card_capacity { - CardCapacity::SDSC => block_idx * 512, + CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 @@ -1252,7 +1252,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes let address = match card.card_type { - CardCapacity::SDSC => block_idx * 512, + CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 From d22a7a3a9668d8d163068ffc4f4b4c3478cb5a1c Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 24 Mar 2025 10:50:25 -0700 Subject: [PATCH 2/9] update sdio-host to 0.9 --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8468436d8..33eb39584 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" futures-util = { version = "0.3.30", default-features = false } rand_core = "0.6.3" -sdio-host = "0.6.0" +sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } From 5d01712d840af0b48c4d85a1f9347a157991f88e Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 24 Mar 2025 12:47:08 -0700 Subject: [PATCH 3/9] use upstream command definitions --- embassy-stm32/src/sdmmc/mod.rs | 206 +++++++++------------------------ 1 file changed, 55 insertions(+), 151 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3cfae7ee1..2229347f4 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -10,7 +10,11 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR, SD}; +use sdio_host::{ + common_cmd::{self, Resp, ResponseLen}, + sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}, + sd_cmd, Cmd, +}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; @@ -136,6 +140,8 @@ pub enum Error { UnsupportedCardVersion, /// Unsupported card type. UnsupportedCardType, + /// Unsupported voltage. + UnsupportedVoltage, /// CRC error. Crc, /// No card inserted. @@ -149,13 +155,6 @@ pub enum Error { StBitErr, } -/// A SD command -struct Cmd { - cmd: u8, - arg: u32, - resp: Response, -} - #[derive(Clone, Copy, Debug, Default)] /// SD Card pub struct Card { @@ -164,7 +163,7 @@ pub struct Card { /// Operation Conditions Register pub ocr: OCR, /// Relative Card Address - pub rca: u32, + pub rca: u16, /// Card ID pub cid: CID, /// Card Specific Data @@ -189,22 +188,12 @@ enum PowerCtrl { On = 0b11, } -#[repr(u32)] -#[allow(dead_code)] -#[allow(non_camel_case_types)] -enum CmdAppOper { - VOLTAGE_WINDOW_SD = 0x8010_0000, - HIGH_CAPACITY = 0x4000_0000, - SDMMC_STD_CAPACITY = 0x0000_0000, - SDMMC_CHECK_PATTERN = 0x0000_01AA, - SD_SWITCH_1_8V_CAPACITY = 0x0100_0000, -} - -#[derive(Eq, PartialEq, Copy, Clone)] -enum Response { - None = 0, - Short = 1, - Long = 3, +fn get_waitresp_val(rlen: ResponseLen) -> u8 { + match rlen { + common_cmd::ResponseLen::Zero => 0, + common_cmd::ResponseLen::R48 => 1, + common_cmd::ResponseLen::R136 => 3, + } } /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to @@ -710,7 +699,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 6, ); InterruptHandler::::data_interrupts(true); - Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6 + Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -769,7 +758,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let regs = T::regs(); let rca = card.rca; - Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 let r1 = regs.respr(0).read().cardstatus(); Ok(r1.into()) @@ -785,8 +774,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { None => &mut CmdBlock::new(), }; - Self::cmd(Cmd::set_block_length(64), false)?; // CMD16 - Self::cmd(Cmd::app_cmd(rca << 16), false)?; // APP + Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 + Self::cmd(common_cmd::app_cmd(rca), false)?; // APP let status = cmd_block; @@ -803,7 +792,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 6, ); InterruptHandler::::data_interrupts(true); - Self::cmd(Cmd::card_status(0), true)?; + Self::cmd(sd_cmd::sd_status(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -846,9 +835,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// _Stand-by State_ fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { // Determine Relative Card Address (RCA) of given card - let rca = card.map(|c| c.rca << 16).unwrap_or(0); + let rca = card.map(|c| c.rca).unwrap_or(0); - let r = Self::cmd(Cmd::sel_desel_card(rca), false); + let r = Self::cmd(common_cmd::select_card(rca), false); match (r, rca) { (Err(Error::Timeout), 0) => Ok(()), _ => r, @@ -891,8 +880,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { // Read the 64-bit SCR register - Self::cmd(Cmd::set_block_length(8), false)?; // CMD16 - Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 + Self::cmd(common_cmd::app_cmd(card.rca), false)?; let cmd_block = match self.cmd_block.as_deref_mut() { Some(x) => x, @@ -913,7 +902,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 3, ); InterruptHandler::::data_interrupts(true); - Self::cmd(Cmd::cmd51(), true)?; + Self::cmd(sd_cmd::send_scr(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -952,7 +941,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Send command to card #[allow(unused_variables)] - fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { + fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { let regs = T::regs(); Self::clear_interrupt_flags(); @@ -965,7 +954,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Command index and start CP State Machine regs.cmdr().write(|w| { w.set_waitint(false); - w.set_waitresp(cmd.resp as u8); + w.set_waitresp(get_waitresp_val(cmd.response_len())); w.set_cmdindex(cmd.cmd); w.set_cpsmen(true); @@ -980,7 +969,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }); let mut status; - if cmd.resp == Response::None { + if cmd.response_len() == ResponseLen::Zero { // Wait for CMDSENT or a timeout while { status = regs.star().read(); @@ -1016,7 +1005,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Command index and start CP State Machine regs.cmdr().write(|w| { w.set_waitint(false); - w.set_waitresp(Response::Short as u8); + w.set_waitresp(get_waitresp_val(ResponseLen::R48)); w.set_cmdindex(12); w.set_cpsmen(true); @@ -1061,29 +1050,30 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); - Self::cmd(Cmd::idle(), false)?; + Self::cmd(common_cmd::idle(), false)?; // Check if cards supports CMD8 (with pattern) - Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; - let r1 = regs.respr(0).read().cardstatus(); + Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; + let cic = CIC::from(regs.respr(0).read().cardstatus()); - let mut card = if r1 == 0x1AA { - // Card echoed back the pattern. Must be at least v2 - Card::default() - } else { + if cic.pattern() != 0xAA { return Err(Error::UnsupportedCardVersion); - }; + } + + if cic.voltage_accepted() & 1 == 0 { + return Err(Error::UnsupportedVoltage); + } + + let mut card = Card::default(); let ocr = loop { // Signal that next command is a app command - Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 - - let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 - | CmdAppOper::HIGH_CAPACITY as u32 - | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; + Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 + // 3.2-3.3V + let voltage_window = 1 << 5; // Initialize card - match Self::cmd(Cmd::app_op_cmd(arg), false) { + match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { // ACMD41 Ok(_) => (), Err(Error::Crc) => (), @@ -1104,7 +1094,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } card.ocr = ocr; - Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 + Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 let cid0 = regs.respr(0).read().cardstatus() as u128; let cid1 = regs.respr(1).read().cardstatus() as u128; let cid2 = regs.respr(2).read().cardstatus() as u128; @@ -1112,10 +1102,11 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); card.cid = cid.into(); - Self::cmd(Cmd::send_rel_addr(), false)?; - card.rca = regs.respr(0).read().cardstatus() >> 16; + Self::cmd(sd_cmd::send_relative_address(), false)?; + let rca = RCA::::from(regs.respr(0).read().cardstatus()); + card.rca = rca.address(); - Self::cmd(Cmd::send_csd(card.rca << 16), false)?; + Self::cmd(common_cmd::send_csd(card.rca), false)?; let csd0 = regs.respr(0).read().cardstatus() as u128; let csd1 = regs.respr(1).read().cardstatus() as u128; let csd2 = regs.respr(2).read().cardstatus() as u128; @@ -1133,8 +1124,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), _ => (BusWidth::One, 0), }; - Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; - Self::cmd(Cmd::cmd6(acmd_arg), false)?; + Self::cmd(common_cmd::app_cmd(card.rca), false)?; + Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; // CPSMACT and DPSMACT must be 0 to set WIDBUS Self::wait_idle(); @@ -1196,7 +1187,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; - Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); @@ -1210,7 +1201,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { 9, ); InterruptHandler::::data_interrupts(true); - Self::cmd(Cmd::read_single_block(address), true)?; + Self::cmd(common_cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1255,20 +1246,20 @@ impl<'d, T: Instance> Sdmmc<'d, T> { CardCapacity::StandardCapacity => block_idx * 512, _ => block_idx, }; - Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] - Self::cmd(Cmd::write_single_block(address), true)?; + Self::cmd(common_cmd::write_single_block(address), true)?; let transfer = self.prepare_datapath_write(buffer, 512, 9); InterruptHandler::::data_interrupts(true); #[cfg(sdmmc_v2)] - Self::cmd(Cmd::write_single_block(address), true)?; + Self::cmd(common_cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1363,93 +1354,6 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> { } } -/// SD card Commands -impl Cmd { - const fn new(cmd: u8, arg: u32, resp: Response) -> Cmd { - Cmd { cmd, arg, resp } - } - - /// CMD0: Idle - const fn idle() -> Cmd { - Cmd::new(0, 0, Response::None) - } - - /// CMD2: Send CID - const fn all_send_cid() -> Cmd { - Cmd::new(2, 0, Response::Long) - } - - /// CMD3: Send Relative Address - const fn send_rel_addr() -> Cmd { - Cmd::new(3, 0, Response::Short) - } - - /// CMD6: Switch Function Command - /// ACMD6: Bus Width - const fn cmd6(arg: u32) -> Cmd { - Cmd::new(6, arg, Response::Short) - } - - /// CMD7: Select one card and put it into the _Tranfer State_ - const fn sel_desel_card(rca: u32) -> Cmd { - Cmd::new(7, rca, Response::Short) - } - - /// CMD8: - const fn hs_send_ext_csd(arg: u32) -> Cmd { - Cmd::new(8, arg, Response::Short) - } - - /// CMD9: - const fn send_csd(rca: u32) -> Cmd { - Cmd::new(9, rca, Response::Long) - } - - /// CMD12: - //const fn stop_transmission() -> Cmd { - // Cmd::new(12, 0, Response::Short) - //} - - /// CMD13: Ask card to send status register - /// ACMD13: SD Status - const fn card_status(rca: u32) -> Cmd { - Cmd::new(13, rca, Response::Short) - } - - /// CMD16: - const fn set_block_length(blocklen: u32) -> Cmd { - Cmd::new(16, blocklen, Response::Short) - } - - /// CMD17: Block Read - const fn read_single_block(addr: u32) -> Cmd { - Cmd::new(17, addr, Response::Short) - } - - /// CMD18: Multiple Block Read - //const fn read_multiple_blocks(addr: u32) -> Cmd { - // Cmd::new(18, addr, Response::Short) - //} - - /// CMD24: Block Write - const fn write_single_block(addr: u32) -> Cmd { - Cmd::new(24, addr, Response::Short) - } - - const fn app_op_cmd(arg: u32) -> Cmd { - Cmd::new(41, arg, Response::Short) - } - - const fn cmd51() -> Cmd { - Cmd::new(51, 0, Response::Short) - } - - /// App Command. Indicates that next command will be a app command - const fn app_cmd(rca: u32) -> Cmd { - Cmd::new(55, rca, Response::Short) - } -} - ////////////////////////////////////////////////////// trait SealedInstance { From 14bb4ee9e414137f318f64e82d808d4012e30680 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 31 Mar 2025 11:43:35 -0700 Subject: [PATCH 4/9] use ready_for_data status to determine when write has finished `read_sd_status` works, but it's somewhat of a hack, but also won't work on eMMC devices. The official spec for both SD and eMMC recommends using this method. --- embassy-stm32/src/sdmmc/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 2229347f4..a28fd51e8 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1292,12 +1292,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; - // Try to read card status (ACMD13) + let card = self.card.as_ref().unwrap(); while timeout > 0 { - match self.read_sd_status().await { - Ok(_) => return Ok(()), - Err(Error::Timeout) => (), // Try again - Err(e) => return Err(e), + let status = self.read_status(card)?; + + if status.ready_for_data() { + return Ok(()); } timeout -= 1; } From 5325f1d911753e76a6b56d7747b816d24a7eb172 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Tue, 25 Mar 2025 06:29:16 -0700 Subject: [PATCH 5/9] scaffold eMMC support --- embassy-stm32/src/sdmmc/mod.rs | 710 ++++++++++++++++++--------------- 1 file changed, 395 insertions(+), 315 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a28fd51e8..6dbb524b7 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -10,11 +10,10 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use sdio_host::{ - common_cmd::{self, Resp, ResponseLen}, - sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}, - sd_cmd, Cmd, -}; +use sdio_host::common_cmd::{self, Resp, ResponseLen}; +use sdio_host::emmc::{ExtCSD, EMMC}; +use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; +use sdio_host::{sd_cmd, Cmd}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; @@ -174,12 +173,21 @@ pub struct Card { pub status: SDStatus, } -impl Card { - /// Size in bytes - pub fn size(&self) -> u64 { - // SDHC / SDXC / SDUC - u64::from(self.csd.block_count()) * 512 - } +#[derive(Clone, Copy, Debug, Default)] +/// eMMC storage +pub struct Emmc { + /// The capacity of this card + pub capacity: CardCapacity, + /// Operation Conditions Register + pub ocr: OCR, + /// Relative Card Address + pub rca: u16, + /// Card ID + pub cid: CID, + /// Card Specific Data + pub csd: CSD, + /// Extended Card Specific Data + pub ext_csd: ExtCSD, } #[repr(u8)] @@ -290,6 +298,61 @@ impl Default for Config { } } +/// Peripheral that can be operated over SDMMC +#[derive(Clone, Copy, Debug)] +pub enum SdmmcPeripheral { + /// SD Card + SdCard(Card), + /// eMMC memory + Emmc(Emmc), +} + +impl SdmmcPeripheral { + /// Get this peripheral's address on the SDMMC bus + fn get_address(&self) -> u16 { + match self { + Self::SdCard(c) => c.rca, + Self::Emmc(e) => e.rca, + } + } + /// Is this a standard or high capacity peripheral? + fn get_capacity(&self) -> CardCapacity { + match self { + Self::SdCard(c) => c.card_type, + Self::Emmc(e) => e.capacity, + } + } + /// Size in bytes + fn size(&self) -> u64 { + match self { + // SDHC / SDXC / SDUC + Self::SdCard(c) => u64::from(c.csd.block_count()) * 512, + // capacity > 2GB + Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512, + } + } + + /// Get a mutable reference to the SD Card. + /// + /// Panics if there is another peripheral instead. + fn get_sd_card(&mut self) -> &mut Card { + match *self { + Self::SdCard(ref mut c) => c, + _ => unreachable!("SD only"), + } + } + + /// Get a mutable reference to the eMMC. + /// + /// Panics if there is another peripheral instead. + fn get_emmc(&mut self) -> &mut Emmc { + match *self { + Self::Emmc(ref mut e) => e, + _ => unreachable!("eMMC only"), + } + } +} + /// Sdmmc device pub struct Sdmmc<'d, T: Instance> { _peri: Peri<'d, T>, @@ -309,7 +372,7 @@ pub struct Sdmmc<'d, T: Instance> { /// Current signalling scheme to card signalling: Signalling, /// Card - card: Option, + card: Option, /// An optional buffer to be used for commands /// This should be used if there are special memory location requirements for dma @@ -662,101 +725,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Ok(()) } - /// Switch mode using CMD6. - /// - /// Attempt to set a new signalling mode. The selected - /// signalling mode is returned. Expects the current clock - /// frequency to be > 12.5MHz. - async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result { - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" - - let set_function = 0x8000_0000 - | match signalling { - // See PLSS v7_10 Table 4-11 - Signalling::DDR50 => 0xFF_FF04, - Signalling::SDR104 => 0xFF_1F03, - Signalling::SDR50 => 0xFF_1F02, - Signalling::SDR25 => 0xFF_FF01, - Signalling::SDR12 => 0xFF_FF00, - }; - - let status = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), - }; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); - let on_drop = OnDrop::new(|| Self::on_drop()); - - let transfer = Self::prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - status.as_mut(), - 64, - 6, - ); - InterruptHandler::::data_interrupts(true); - Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); - - // Host is allowed to use the new functions at least 8 - // clocks after the end of the switch command - // transaction. We know the current clock period is < 80ns, - // so a total delay of 640ns is required here - for _ in 0..300 { - cortex_m::asm::nop(); - } - - match res { - Ok(_) => { - on_drop.defuse(); - Self::stop_datapath(); - drop(transfer); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(status[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - Err(e) => Err(e), - } - } - /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &Card) -> Result, Error> { + fn read_status(&self, card: &SdmmcPeripheral) -> Result, Error> + where + CardStatus: From, + { let regs = T::regs(); - let rca = card.rca; + let rca = card.get_address(); Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 @@ -764,78 +739,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Ok(r1.into()) } - /// Reads the SD Status (ACMD13) - async fn read_sd_status(&mut self) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let rca = card.rca; - - let cmd_block = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), - }; - - Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 - Self::cmd(common_cmd::app_cmd(rca), false)?; // APP - - let status = cmd_block; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); - let on_drop = OnDrop::new(|| Self::on_drop()); - - let transfer = Self::prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - status.as_mut(), - 64, - 6, - ); - InterruptHandler::::data_interrupts(true); - Self::cmd(sd_cmd::sd_status(), true)?; - - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - Self::stop_datapath(); - drop(transfer); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - self.card.as_mut().unwrap().status = status.0.into(); - } - res - } - /// Select one card and place it into the _Tranfer State_ /// /// If `None` is specifed for `card`, all cards are put back into /// _Stand-by State_ - fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { + fn select_card(&self, rca: Option) -> Result<(), Error> { // Determine Relative Card Address (RCA) of given card - let rca = card.map(|c| c.rca).unwrap_or(0); + let rca = rca.unwrap_or(0); let r = Self::cmd(common_cmd::select_card(rca), false); match (r, rca) { @@ -878,67 +788,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }); } - async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { - // Read the 64-bit SCR register - Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 - Self::cmd(common_cmd::app_cmd(card.rca), false)?; - - let cmd_block = match self.cmd_block.as_deref_mut() { - Some(x) => x, - None => &mut CmdBlock::new(), - }; - let scr = &mut cmd_block.0[..2]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); - let on_drop = OnDrop::new(|| Self::on_drop()); - - let transfer = Self::prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - scr, - 8, - 3, - ); - InterruptHandler::::data_interrupts(true); - Self::cmd(sd_cmd::send_scr(), true)?; - - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - Self::stop_datapath(); - drop(transfer); - - unsafe { - let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); - card.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res - } - /// Send command to card #[allow(unused_variables)] fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { @@ -1024,6 +873,170 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::stop_datapath(); } + /// Read a data block. + #[inline] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let card_capacity = self.card()?.get_capacity(); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let regs = T::regs(); + let on_drop = OnDrop::new(|| Self::on_drop()); + + let transfer = Self::prepare_datapath_read( + &self.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + buffer, + 512, + 9, + ); + InterruptHandler::::data_interrupts(true); + Self::cmd(common_cmd::read_single_block(address), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = regs.star().read(); + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } + if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + drop(transfer); + } + res + } + + /// Write a data block. + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card.get_capacity() { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let regs = T::regs(); + let on_drop = OnDrop::new(|| Self::on_drop()); + + // sdmmc_v1 uses different cmd/dma order than v2, but only for writes + #[cfg(sdmmc_v1)] + Self::cmd(common_cmd::write_single_block(address), true)?; + + let transfer = self.prepare_datapath_write(buffer, 512, 9); + InterruptHandler::::data_interrupts(true); + + #[cfg(sdmmc_v2)] + Self::cmd(common_cmd::write_single_block(address), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = regs.star().read(); + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } + if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); + drop(transfer); + + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; + + let card = self.card.as_ref().unwrap(); + while timeout > 0 { + let ready_for_data = match card { + SdmmcPeripheral::Emmc(_) => self.read_status::(card)?.ready_for_data(), + SdmmcPeripheral::SdCard(_) => self.read_status::(card)?.ready_for_data(), + }; + + if ready_for_data { + return Ok(()); + } + timeout -= 1; + } + Err(Error::SoftwareTimeout) + } + Err(e) => Err(e), + } + } + + /// Get a reference to the initialized card + /// + /// # Errors + /// + /// Returns Error::NoCard if [`init_card`](#method.init_card) + /// has not previously succeeded + #[inline] + pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { + self.card.as_ref().ok_or(Error::NoCard) + } + + /// Get the current SDMMC bus clock + pub fn clock(&self) -> Hertz { + self.clock + } + + /// Set a specific cmd buffer rather than using the default stack allocated one. + /// This is required if stack RAM cannot be used with DMA and usually manifests + /// itself as an indefinite wait on a dma transfer because the dma peripheral + /// cannot access the memory. + pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { + self.cmd_block = Some(cmd_block) + } +} + +/// SD only +impl<'d, T: Instance> Sdmmc<'d, T> { /// Initializes card (if present) and sets the bus at the specified frequency. pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { let regs = T::regs(); @@ -1114,7 +1127,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); card.csd = csd.into(); - self.select_card(Some(&card))?; + self.select_card(Some(card.rca))?; self.get_scr(&mut card).await?; @@ -1148,7 +1161,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { self.clkcr_set_clkdiv(25_000_000, width)?; } - self.card = Some(card); + self.card = Some(SdmmcPeripheral::SdCard(card)); // Read status self.read_sd_status().await?; @@ -1161,7 +1174,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { // Set final clock frequency self.clkcr_set_clkdiv(freq.0, width)?; - if self.read_status(&card)?.state() != CurrentState::Transfer { + if self.read_status::(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { return Err(Error::SignalingSwitchFailed); } } @@ -1173,22 +1186,34 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Ok(()) } - /// Read a data block. - #[inline] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let card_capacity = self.card()?.card_type; + /// Switch mode using CMD6. + /// + /// Attempt to set a new signalling mode. The selected + /// signalling mode is returned. Expects the current clock + /// frequency to be > 12.5MHz. + /// + /// SD only. + async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result { + let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); + // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not + // necessary" - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + let set_function = 0x8000_0000 + | match signalling { + // See PLSS v7_10 Table 4-11 + Signalling::DDR50 => 0xFF_FF04, + Signalling::SDR104 => 0xFF_1F03, + Signalling::SDR50 => 0xFF_1F02, + Signalling::SDR25 => 0xFF_FF01, + Signalling::SDR12 => 0xFF_FF00, + }; - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, + let status = match self.cmd_block.as_deref_mut() { + Some(x) => x, + None => &mut CmdBlock::new(), }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + // Arm `OnDrop` after the buffer, so it will be dropped first let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); @@ -1196,12 +1221,93 @@ impl<'d, T: Instance> Sdmmc<'d, T> { &self.config, #[cfg(sdmmc_v1)] &mut self.dma, - buffer, - 512, - 9, + status.as_mut(), + 64, + 6, ); InterruptHandler::::data_interrupts(true); - Self::cmd(common_cmd::read_single_block(address), true)?; + Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = regs.star().read(); + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } + if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + // Host is allowed to use the new functions at least 8 + // clocks after the end of the switch command + // transaction. We know the current clock period is < 80ns, + // so a total delay of 640ns is required here + for _ in 0..300 { + cortex_m::asm::nop(); + } + + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); + drop(transfer); + + // Function Selection of Function Group 1 + let selection = (u32::from_be(status[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), + } + } + Err(e) => Err(e), + } + } + + /// Reads the SCR register. + /// + /// SD only. + async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { + // Read the 64-bit SCR register + Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 + Self::cmd(common_cmd::app_cmd(card.rca), false)?; + + let cmd_block = match self.cmd_block.as_deref_mut() { + Some(x) => x, + None => &mut CmdBlock::new(), + }; + let scr = &mut cmd_block.0[..2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| Self::on_drop()); + + let transfer = Self::prepare_datapath_read( + &self.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + scr, + 8, + 3, + ); + InterruptHandler::::data_interrupts(true); + Self::cmd(sd_cmd::send_scr(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1229,37 +1335,46 @@ impl<'d, T: Instance> Sdmmc<'d, T> { on_drop.defuse(); Self::stop_datapath(); drop(transfer); + + unsafe { + let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); + card.scr = SCR(u64::from_be_bytes(*scr_bytes)); + } } res } - /// Write a data block. - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?; + /// Reads the SD Status (ACMD13) + /// + /// SD only. + async fn read_sd_status(&mut self) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); + let rca = card.rca; - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card.card_type { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, + let cmd_block = match self.cmd_block.as_deref_mut() { + Some(x) => x, + None => &mut CmdBlock::new(), }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 + Self::cmd(common_cmd::app_cmd(rca), false)?; // APP + + let status = cmd_block; + + // Arm `OnDrop` after the buffer, so it will be dropped first let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); - // sdmmc_v1 uses different cmd/dma order than v2, but only for writes - #[cfg(sdmmc_v1)] - Self::cmd(common_cmd::write_single_block(address), true)?; - - let transfer = self.prepare_datapath_write(buffer, 512, 9); + let transfer = Self::prepare_datapath_read( + &self.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + status.as_mut(), + 64, + 6, + ); InterruptHandler::::data_interrupts(true); - - #[cfg(sdmmc_v2)] - Self::cmd(common_cmd::write_single_block(address), true)?; + Self::cmd(sd_cmd::sd_status(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1283,52 +1398,17 @@ impl<'d, T: Instance> Sdmmc<'d, T> { .await; Self::clear_interrupt_flags(); - match res { - Ok(_) => { - on_drop.defuse(); - Self::stop_datapath(); - drop(transfer); + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + drop(transfer); - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; - - let card = self.card.as_ref().unwrap(); - while timeout > 0 { - let status = self.read_status(card)?; - - if status.ready_for_data() { - return Ok(()); - } - timeout -= 1; - } - Err(Error::SoftwareTimeout) + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); } - Err(e) => Err(e), + card.status = status.0.into(); } - } - - /// Get a reference to the initialized card - /// - /// # Errors - /// - /// Returns Error::NoCard if [`init_card`](#method.init_card) - /// has not previously succeeded - #[inline] - pub fn card(&self) -> Result<&Card, Error> { - self.card.as_ref().ok_or(Error::NoCard) - } - - /// Get the current SDMMC bus clock - pub fn clock(&self) -> Hertz { - self.clock - } - - /// Set a specific cmd buffer rather than using the default stack allocated one. - /// This is required if stack RAM cannot be used with DMA and usually manifests - /// itself as an indefinite wait on a dma transfer because the dma peripheral - /// cannot access the memory. - pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { - self.cmd_block = Some(cmd_block) + res } } From 57731a78963e1afa3dfecc70321ae407e2095a16 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Tue, 25 Mar 2025 08:25:08 -0700 Subject: [PATCH 6/9] support eMMC --- embassy-stm32/src/sdmmc/mod.rs | 185 ++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6dbb524b7..f92c0260b 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -13,7 +13,7 @@ use embassy_sync::waitqueue::AtomicWaker; use sdio_host::common_cmd::{self, Resp, ResponseLen}; use sdio_host::emmc::{ExtCSD, EMMC}; use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; -use sdio_host::{sd_cmd, Cmd}; +use sdio_host::{emmc_cmd, sd_cmd, Cmd}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; @@ -1412,6 +1412,189 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } } +/// eMMC only. +impl<'d, T: Instance> Sdmmc<'d, T> { + /// Initializes eMMC and sets the bus at the specified frequency. + pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { + let regs = T::regs(); + let ker_ck = T::frequency(); + + let bus_width = match self.d3.is_some() { + true => BusWidth::Four, + false => BusWidth::One, + }; + + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); + self.clock = init_clock; + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); + + regs.clkcr().modify(|w| { + w.set_widbus(0); + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); + + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); + Self::cmd(common_cmd::idle(), false)?; + + let mut card = Emmc::default(); + + let ocr = loop { + let high_voltage = 0b0 << 7; + let access_mode = 0b10 << 29; + let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; + // Initialize card + match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; + + card.capacity = if ocr.access_mode() == 0b10 { + // Card is SDHC or SDXC or SDUC + CardCapacity::HighCapacity + } else { + CardCapacity::StandardCapacity + }; + card.ocr = ocr; + + Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 + let cid0 = regs.respr(0).read().cardstatus() as u128; + let cid1 = regs.respr(1).read().cardstatus() as u128; + let cid2 = regs.respr(2).read().cardstatus() as u128; + let cid3 = regs.respr(3).read().cardstatus() as u128; + let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); + card.cid = cid.into(); + + card.rca = 1u16.into(); + Self::cmd(emmc_cmd::assign_relative_address(card.rca), false)?; + + Self::cmd(common_cmd::send_csd(card.rca), false)?; + let csd0 = regs.respr(0).read().cardstatus() as u128; + let csd1 = regs.respr(1).read().cardstatus() as u128; + let csd2 = regs.respr(2).read().cardstatus() as u128; + let csd3 = regs.respr(3).read().cardstatus() as u128; + let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); + card.csd = csd.into(); + + self.select_card(Some(card.rca))?; + + // Set bus width + let (width, widbus) = match bus_width { + BusWidth::Eight => (BusWidth::Eight, 2), + BusWidth::Four => (BusWidth::Four, 1), + _ => (BusWidth::One, 0), + }; + // Write bus width to ExtCSD byte 183 + Self::cmd( + emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), + false, + )?; + + self.card = Some(SdmmcPeripheral::Emmc(card)); + + // Wait for ready after R1b response + loop { + let status = self.read_status::(self.card.as_ref().unwrap())?; + + if status.ready_for_data() { + break; + } + } + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); + + regs.clkcr().modify(|w| w.set_widbus(widbus)); + + // Set Clock + if freq.0 <= 25_000_000 { + // Final clock frequency + self.clkcr_set_clkdiv(freq.0, width)?; + } else { + // Switch to max clock for SDR12 + self.clkcr_set_clkdiv(25_000_000, width)?; + } + + // Read status + self.read_ext_csd().await?; + + Ok(()) + } + + /// Gets the EXT_CSD register. + /// + /// eMMC only. + async fn read_ext_csd(&mut self) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc(); + + // Note: cmd_block can't be used because ExtCSD is too long to fit. + let mut data_block = DataBlock([0u8; 512]); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| Self::on_drop()); + + let transfer = Self::prepare_datapath_read( + &self.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + buffer, + 512, + 9, + ); + InterruptHandler::::data_interrupts(true); + Self::cmd(emmc_cmd::send_ext_csd(), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = regs.star().read(); + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } + if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } + #[cfg(sdmmc_v1)] + if status.stbiterr() { + return Poll::Ready(Err(Error::StBitErr)); + } + if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + drop(transfer); + + card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); + } + res + } +} + impl<'d, T: Instance> Drop for Sdmmc<'d, T> { fn drop(&mut self) { T::Interrupt::disable(); From b92eb948b5de521205e3a8bb81548662c7fc5c1d Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Wed, 26 Mar 2025 23:23:22 -0700 Subject: [PATCH 7/9] fix d6->d7 typo from build.rs --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 798133162..dd69a472a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1049,7 +1049,7 @@ fn main() { (("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)), (("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)), (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), - (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), + (("sdmmc", "D7"), quote!(crate::sdmmc::D7Pin)), (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), (("quadspi", "BK1_IO0"), quote!(crate::qspi::BK1D0Pin)), (("quadspi", "BK1_IO1"), quote!(crate::qspi::BK1D1Pin)), From 0a231505d8225f3f36f39b0be1ded4304fb7ccca Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Wed, 26 Mar 2025 23:26:11 -0700 Subject: [PATCH 8/9] support 8 lane data bus --- embassy-stm32/src/sdmmc/mod.rs | 162 ++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index f92c0260b..a0c3573a9 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -145,6 +145,8 @@ pub enum Error { Crc, /// No card inserted. NoCard, + /// 8-lane buses are not supported for SD cards. + BusWidth, /// Bad clock supplied to the SDMMC peripheral. BadClock, /// Signaling switch failed. @@ -365,6 +367,10 @@ pub struct Sdmmc<'d, T: Instance> { d1: Option>, d2: Option>, d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, config: Config, /// Current clock to card @@ -413,6 +419,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { None, None, None, + None, + None, + None, + None, config, ) } @@ -448,6 +458,60 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Some(d1.into()), Some(d2.into()), Some(d3.into()), + None, + None, + None, + None, + config, + ) + } +} + +#[cfg(sdmmc_v1)] +impl<'d, T: Instance> Sdmmc<'d, T> { + /// Create a new SDMMC driver, with 8 data lanes. + pub fn new_8bit( + sdmmc: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + dma: Peri<'d, impl SdmmcDma>, + clk: Peri<'d, impl CkPin>, + cmd: Peri<'d, impl CmdPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + config: Config, + ) -> Self { + critical_section::with(|_| { + clk.set_as_af(clk.af_num(), CLK_AF); + cmd.set_as_af(cmd.af_num(), CMD_AF); + d0.set_as_af(d0.af_num(), DATA_AF); + d1.set_as_af(d1.af_num(), DATA_AF); + d2.set_as_af(d2.af_num(), DATA_AF); + d3.set_as_af(d3.af_num(), DATA_AF); + d4.set_as_af(d4.af_num(), DATA_AF); + d5.set_as_af(d5.af_num(), DATA_AF); + d6.set_as_af(d6.af_num(), DATA_AF); + d7.set_as_af(d7.af_num(), DATA_AF); + }); + + Self::new_inner( + sdmmc, + new_dma_nonopt!(dma), + clk.into(), + cmd.into(), + d0.into(), + Some(d1.into()), + Some(d2.into()), + Some(d3.into()), + Some(d4.into()), + Some(d5.into()), + Some(d6.into()), + Some(d7.into()), config, ) } @@ -470,7 +534,20 @@ impl<'d, T: Instance> Sdmmc<'d, T> { d0.set_as_af(d0.af_num(), DATA_AF); }); - Self::new_inner(sdmmc, clk.into(), cmd.into(), d0.into(), None, None, None, config) + Self::new_inner( + sdmmc, + clk.into(), + cmd.into(), + d0.into(), + None, + None, + None, + None, + None, + None, + None, + config, + ) } /// Create a new SDMMC driver, with 4 data lanes. @@ -502,6 +579,58 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Some(d1.into()), Some(d2.into()), Some(d3.into()), + None, + None, + None, + None, + config, + ) + } +} + +#[cfg(sdmmc_v2)] +impl<'d, T: Instance> Sdmmc<'d, T> { + /// Create a new SDMMC driver, with 8 data lanes. + pub fn new_8bit( + sdmmc: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + clk: Peri<'d, impl CkPin>, + cmd: Peri<'d, impl CmdPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + config: Config, + ) -> Self { + critical_section::with(|_| { + clk.set_as_af(clk.af_num(), CLK_AF); + cmd.set_as_af(cmd.af_num(), CMD_AF); + d0.set_as_af(d0.af_num(), DATA_AF); + d1.set_as_af(d1.af_num(), DATA_AF); + d2.set_as_af(d2.af_num(), DATA_AF); + d3.set_as_af(d3.af_num(), DATA_AF); + d4.set_as_af(d4.af_num(), DATA_AF); + d5.set_as_af(d5.af_num(), DATA_AF); + d6.set_as_af(d6.af_num(), DATA_AF); + d7.set_as_af(d7.af_num(), DATA_AF); + }); + + Self::new_inner( + sdmmc, + clk.into(), + cmd.into(), + d0.into(), + Some(d1.into()), + Some(d2.into()), + Some(d3.into()), + Some(d4.into()), + Some(d5.into()), + Some(d6.into()), + Some(d7.into()), config, ) } @@ -517,6 +646,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { d1: Option>, d2: Option>, d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, config: Config, ) -> Self { rcc::enable_and_reset::(); @@ -555,6 +688,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { d1, d2, d3, + d4, + d5, + d6, + d7, config, clock: SD_INIT_FREQ, @@ -1039,6 +1176,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { impl<'d, T: Instance> Sdmmc<'d, T> { /// Initializes card (if present) and sets the bus at the specified frequency. pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { + if self.d7.is_some() { + return Err(Error::BusWidth); + } + let regs = T::regs(); let ker_ck = T::frequency(); @@ -1419,9 +1560,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let regs = T::regs(); let ker_ck = T::frequency(); - let bus_width = match self.d3.is_some() { - true => BusWidth::Four, - false => BusWidth::One, + let bus_width = match (self.d3.is_some(), self.d7.is_some()) { + (true, true) => BusWidth::Eight, + (true, false) => BusWidth::Four, + _ => BusWidth::One, }; // While the SD/SDIO card or eMMC is in identification mode, @@ -1613,6 +1755,18 @@ impl<'d, T: Instance> Drop for Sdmmc<'d, T> { if let Some(x) = &mut self.d3 { x.set_as_disconnected(); } + if let Some(x) = &mut self.d4 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d5 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d6 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d7 { + x.set_as_disconnected(); + } }); } } From dc31bfd8295c3626794e0b1b38804af2acc59053 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Fri, 28 Mar 2025 15:54:47 -0700 Subject: [PATCH 9/9] refactor to reduce code duplication --- embassy-stm32/src/sdmmc/mod.rs | 581 ++++++++++++------------------ examples/stm32f4/src/bin/sdmmc.rs | 2 +- examples/stm32f7/src/bin/sdmmc.rs | 2 +- examples/stm32h7/src/bin/sdmmc.rs | 2 +- tests/stm32/src/bin/sdmmc.rs | 4 +- 5 files changed, 233 insertions(+), 358 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a0c3573a9..63868e5ae 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1010,35 +1010,10 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::stop_datapath(); } - /// Read a data block. + /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let card_capacity = self.card()?.get_capacity(); - - // NOTE(unsafe) DataBlock uses align 4 - let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match card_capacity { - CardCapacity::StandardCapacity => block_idx * 512, - _ => block_idx, - }; - Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 - + async fn complete_datapath_transfer() -> Result<(), Error> { let regs = T::regs(); - let on_drop = OnDrop::new(|| Self::on_drop()); - - let transfer = Self::prepare_datapath_read( - &self.config, - #[cfg(sdmmc_v1)] - &mut self.dma, - buffer, - 512, - 9, - ); - InterruptHandler::::data_interrupts(true); - Self::cmd(common_cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1060,8 +1035,43 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Poll::Pending }) .await; + Self::clear_interrupt_flags(); + res + } + + /// Read a data block. + #[inline] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let card_capacity = self.card()?.get_capacity(); + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + + // Always read 1 block of 512 bytes + // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes + let address = match card_capacity { + CardCapacity::StandardCapacity => block_idx * 512, + _ => block_idx, + }; + Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 + + let on_drop = OnDrop::new(|| Self::on_drop()); + + let transfer = Self::prepare_datapath_read( + &self.config, + #[cfg(sdmmc_v1)] + &mut self.dma, + buffer, + 512, + 9, + ); + InterruptHandler::::data_interrupts(true); + Self::cmd(common_cmd::read_single_block(address), true)?; + + let res = Self::complete_datapath_transfer().await; + if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); @@ -1085,7 +1095,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }; Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes @@ -1098,27 +1107,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { #[cfg(sdmmc_v2)] Self::cmd(common_cmd::write_single_block(address), true)?; - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); + let res = Self::complete_datapath_transfer().await; match res { Ok(_) => { @@ -1151,8 +1140,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// # Errors /// - /// Returns Error::NoCard if [`init_card`](#method.init_card) - /// has not previously succeeded + /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or + /// [`init_emmc`](#method.init_emmc) has not previously succeeded #[inline] pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { self.card.as_ref().ok_or(Error::NoCard) @@ -1170,22 +1159,20 @@ impl<'d, T: Instance> Sdmmc<'d, T> { pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { self.cmd_block = Some(cmd_block) } -} - -/// SD only -impl<'d, T: Instance> Sdmmc<'d, T> { - /// Initializes card (if present) and sets the bus at the specified frequency. - pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { - if self.d7.is_some() { - return Err(Error::BusWidth); - } + async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { let regs = T::regs(); let ker_ck = T::frequency(); - let bus_width = match self.d3.is_some() { - true => BusWidth::Four, - false => BusWidth::One, + let bus_width = match (self.d3.is_some(), self.d7.is_some()) { + (true, true) => { + if matches!(card, SdmmcPeripheral::SdCard(_)) { + return Err(Error::BusWidth); + } + BusWidth::Eight + } + (true, false) => BusWidth::Four, + _ => BusWidth::One, }; // While the SD/SDIO card or eMMC is in identification mode, @@ -1206,47 +1193,75 @@ impl<'d, T: Instance> Sdmmc<'d, T> { regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); Self::cmd(common_cmd::idle(), false)?; - // Check if cards supports CMD8 (with pattern) - Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; - let cic = CIC::from(regs.respr(0).read().cardstatus()); + match card { + SdmmcPeripheral::SdCard(ref mut card) => { + // Check if cards supports CMD8 (with pattern) + Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; + let cic = CIC::from(regs.respr(0).read().cardstatus()); - if cic.pattern() != 0xAA { - return Err(Error::UnsupportedCardVersion); - } + if cic.pattern() != 0xAA { + return Err(Error::UnsupportedCardVersion); + } - if cic.voltage_accepted() & 1 == 0 { - return Err(Error::UnsupportedVoltage); - } + if cic.voltage_accepted() & 1 == 0 { + return Err(Error::UnsupportedVoltage); + } - let mut card = Card::default(); + let ocr = loop { + // Signal that next command is a app command + Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 - let ocr = loop { - // Signal that next command is a app command - Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 + // 3.2-3.3V + let voltage_window = 1 << 5; + // Initialize card + match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { + // ACMD41 + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; - // 3.2-3.3V - let voltage_window = 1 << 5; - // Initialize card - match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { - // ACMD41 - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), + if ocr.high_capacity() { + // Card is SDHC or SDXC or SDUC + card.card_type = CardCapacity::HighCapacity; + } else { + card.card_type = CardCapacity::StandardCapacity; + } + card.ocr = ocr; } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; + SdmmcPeripheral::Emmc(ref mut emmc) => { + let ocr = loop { + let high_voltage = 0b0 << 7; + let access_mode = 0b10 << 29; + let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; + // Initialize card + match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), + } + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; + } + }; - if ocr.high_capacity() { - // Card is SDHC or SDXC or SDUC - card.card_type = CardCapacity::HighCapacity; - } else { - card.card_type = CardCapacity::StandardCapacity; + emmc.capacity = if ocr.access_mode() == 0b10 { + // Card is SDHC or SDXC or SDUC + CardCapacity::HighCapacity + } else { + CardCapacity::StandardCapacity + }; + emmc.ocr = ocr; + } } - card.ocr = ocr; Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 let cid0 = regs.respr(0).read().cardstatus() as u128; @@ -1254,79 +1269,139 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let cid2 = regs.respr(2).read().cardstatus() as u128; let cid3 = regs.respr(3).read().cardstatus() as u128; let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); - card.cid = cid.into(); - Self::cmd(sd_cmd::send_relative_address(), false)?; - let rca = RCA::::from(regs.respr(0).read().cardstatus()); - card.rca = rca.address(); + match card { + SdmmcPeripheral::SdCard(ref mut card) => { + card.cid = cid.into(); - Self::cmd(common_cmd::send_csd(card.rca), false)?; + Self::cmd(sd_cmd::send_relative_address(), false)?; + let rca = RCA::::from(regs.respr(0).read().cardstatus()); + card.rca = rca.address(); + } + SdmmcPeripheral::Emmc(ref mut emmc) => { + emmc.cid = cid.into(); + + emmc.rca = 1u16.into(); + Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; + } + } + + Self::cmd(common_cmd::send_csd(card.get_address()), false)?; let csd0 = regs.respr(0).read().cardstatus() as u128; let csd1 = regs.respr(1).read().cardstatus() as u128; let csd2 = regs.respr(2).read().cardstatus() as u128; let csd3 = regs.respr(3).read().cardstatus() as u128; let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); - card.csd = csd.into(); - self.select_card(Some(card.rca))?; + self.select_card(Some(card.get_address()))?; - self.get_scr(&mut card).await?; + let bus_width = match card { + SdmmcPeripheral::SdCard(ref mut card) => { + card.csd = csd.into(); + + self.get_scr(card).await?; + + if !card.scr.bus_width_four() { + BusWidth::One + } else { + BusWidth::Four + } + } + SdmmcPeripheral::Emmc(ref mut emmc) => { + emmc.csd = csd.into(); + + bus_width + } + }; // Set bus width - let (width, acmd_arg) = match bus_width { - BusWidth::Eight => unimplemented!(), - BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), - _ => (BusWidth::One, 0), + let widbus = match bus_width { + BusWidth::Eight => 2, + BusWidth::Four => 1, + BusWidth::One => 0, + _ => unreachable!(), }; - Self::cmd(common_cmd::app_cmd(card.rca), false)?; - Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; - // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); + match card { + SdmmcPeripheral::SdCard(ref mut card) => { + let acmd_arg = match bus_width { + BusWidth::Four if card.scr.bus_width_four() => 2, + _ => 0, + }; + Self::cmd(common_cmd::app_cmd(card.rca), false)?; + Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; + } + SdmmcPeripheral::Emmc(_) => { + // Write bus width to ExtCSD byte 183 + Self::cmd( + emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), + false, + )?; - regs.clkcr().modify(|w| { - w.set_widbus(match width { - BusWidth::One => 0, - BusWidth::Four => 1, - BusWidth::Eight => 2, - _ => panic!("Invalid Bus Width"), - }) - }); + // Wait for ready after R1b response + loop { + let status = self.read_status::(&card)?; - // Set Clock - if freq.0 <= 25_000_000 { - // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width)?; - } else { - // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width)?; - } - - self.card = Some(SdmmcPeripheral::SdCard(card)); - - // Read status - self.read_sd_status().await?; - - if freq.0 > 25_000_000 { - // Switch to SDR25 - self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; - - if self.signalling == Signalling::SDR25 { - // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, width)?; - - if self.read_status::(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { - return Err(Error::SignalingSwitchFailed); + if status.ready_for_data() { + break; + } } } } - // Read status after signalling change - self.read_sd_status().await?; + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); + + regs.clkcr().modify(|w| w.set_widbus(widbus)); + + // Set Clock + if freq.0 <= 25_000_000 { + // Final clock frequency + self.clkcr_set_clkdiv(freq.0, bus_width)?; + } else { + // Switch to max clock for SDR12 + self.clkcr_set_clkdiv(25_000_000, bus_width)?; + } + + self.card = Some(card); + + match card { + SdmmcPeripheral::SdCard(_) => { + // Read status + self.read_sd_status().await?; + + if freq.0 > 25_000_000 { + // Switch to SDR25 + self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; + + if self.signalling == Signalling::SDR25 { + // Set final clock frequency + self.clkcr_set_clkdiv(freq.0, bus_width)?; + + if self.read_status::(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { + return Err(Error::SignalingSwitchFailed); + } + } + } + + // Read status after signalling change + self.read_sd_status().await?; + } + SdmmcPeripheral::Emmc(_) => { + self.read_ext_csd().await?; + } + } Ok(()) } + /// Initializes card (if present) and sets the bus at the specified frequency. + /// + /// SD only. + pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { + self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await + } + /// Switch mode using CMD6. /// /// Attempt to set a new signalling mode. The selected @@ -1355,7 +1430,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { }; // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = Self::prepare_datapath_read( @@ -1369,27 +1443,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { InterruptHandler::::data_interrupts(true); Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); + let res = Self::complete_datapath_transfer().await; // Host is allowed to use the new functions at least 8 // clocks after the end of the switch command @@ -1436,7 +1490,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let scr = &mut cmd_block.0[..2]; // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = Self::prepare_datapath_read( @@ -1450,27 +1503,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { InterruptHandler::::data_interrupts(true); Self::cmd(sd_cmd::send_scr(), true)?; - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); + let res = Self::complete_datapath_transfer().await; if res.is_ok() { on_drop.defuse(); @@ -1503,7 +1536,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { let status = cmd_block; // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = Self::prepare_datapath_read( @@ -1517,27 +1549,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { InterruptHandler::::data_interrupts(true); Self::cmd(sd_cmd::sd_status(), true)?; - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); + let res = Self::complete_datapath_transfer().await; if res.is_ok() { on_drop.defuse(); @@ -1551,128 +1563,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { } res } -} -/// eMMC only. -impl<'d, T: Instance> Sdmmc<'d, T> { /// Initializes eMMC and sets the bus at the specified frequency. + /// + /// eMMC only. pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { - let regs = T::regs(); - let ker_ck = T::frequency(); - - let bus_width = match (self.d3.is_some(), self.d7.is_some()) { - (true, true) => BusWidth::Eight, - (true, false) => BusWidth::Four, - _ => BusWidth::One, - }; - - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be no more than 400 kHz. - let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); - self.clock = init_clock; - - // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); - - regs.clkcr().modify(|w| { - w.set_widbus(0); - w.set_clkdiv(clkdiv); - #[cfg(sdmmc_v1)] - w.set_bypass(_bypass); - }); - - regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); - Self::cmd(common_cmd::idle(), false)?; - - let mut card = Emmc::default(); - - let ocr = loop { - let high_voltage = 0b0 << 7; - let access_mode = 0b10 << 29; - let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; - // Initialize card - match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - card.capacity = if ocr.access_mode() == 0b10 { - // Card is SDHC or SDXC or SDUC - CardCapacity::HighCapacity - } else { - CardCapacity::StandardCapacity - }; - card.ocr = ocr; - - Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 - let cid0 = regs.respr(0).read().cardstatus() as u128; - let cid1 = regs.respr(1).read().cardstatus() as u128; - let cid2 = regs.respr(2).read().cardstatus() as u128; - let cid3 = regs.respr(3).read().cardstatus() as u128; - let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); - card.cid = cid.into(); - - card.rca = 1u16.into(); - Self::cmd(emmc_cmd::assign_relative_address(card.rca), false)?; - - Self::cmd(common_cmd::send_csd(card.rca), false)?; - let csd0 = regs.respr(0).read().cardstatus() as u128; - let csd1 = regs.respr(1).read().cardstatus() as u128; - let csd2 = regs.respr(2).read().cardstatus() as u128; - let csd3 = regs.respr(3).read().cardstatus() as u128; - let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); - card.csd = csd.into(); - - self.select_card(Some(card.rca))?; - - // Set bus width - let (width, widbus) = match bus_width { - BusWidth::Eight => (BusWidth::Eight, 2), - BusWidth::Four => (BusWidth::Four, 1), - _ => (BusWidth::One, 0), - }; - // Write bus width to ExtCSD byte 183 - Self::cmd( - emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), - false, - )?; - - self.card = Some(SdmmcPeripheral::Emmc(card)); - - // Wait for ready after R1b response - loop { - let status = self.read_status::(self.card.as_ref().unwrap())?; - - if status.ready_for_data() { - break; - } - } - - // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); - - regs.clkcr().modify(|w| w.set_widbus(widbus)); - - // Set Clock - if freq.0 <= 25_000_000 { - // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width)?; - } else { - // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width)?; - } - - // Read status - self.read_ext_csd().await?; - - Ok(()) + self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await } /// Gets the EXT_CSD register. @@ -1690,7 +1586,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = T::regs(); let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = Self::prepare_datapath_read( @@ -1704,27 +1599,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { InterruptHandler::::data_interrupts(true); Self::cmd(emmc_cmd::send_ext_csd(), true)?; - let res = poll_fn(|cx| { - T::state().register(cx.waker()); - let status = regs.star().read(); - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } - if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } - #[cfg(sdmmc_v1)] - if status.stbiterr() { - return Poll::Ready(Err(Error::StBitErr)); - } - if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - Self::clear_interrupt_flags(); + let res = Self::complete_datapath_transfer().await; if res.is_ok() { on_drop.defuse(); diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 66e4e527c..e97b63925 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { let mut err = None; loop { - match sdmmc.init_card(mhz(24)).await { + match sdmmc.init_sd_card(mhz(24)).await { Ok(_) => break, Err(e) => { if err != Some(e) { diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 6d36ef518..787bef25e 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(25)).await); + unwrap!(sdmmc.init_sd_card(mhz(25)).await); let card = unwrap!(sdmmc.card()); diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index abe2d4ba7..96840d8ff 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) -> ! { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(25)).await); + unwrap!(sdmmc.init_sd_card(mhz(25)).await); let card = unwrap!(sdmmc.card()); diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 07f17b569..c1ed45588 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { let mut err = None; loop { - match s.init_card(mhz(24)).await { + match s.init_sd_card(mhz(24)).await { Ok(_) => break, Err(e) => { if err != Some(e) { @@ -100,7 +100,7 @@ async fn main(_spawner: Spawner) { let mut err = None; loop { - match s.init_card(mhz(24)).await { + match s.init_sd_card(mhz(24)).await { Ok(_) => break, Err(e) => { if err != Some(e) {