//! Secure Digital / MultiMedia Card (SDMMC) #![macro_use] use core::default::Default; use core::future::poll_fn; use core::marker::PhantomData; use core::slice; use core::task::Poll; use aligned::{A4, Aligned}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::Cmd; use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; use sdio_host::sd::{BusWidth, CardStatus}; use sdio_host::sd_cmd::{R6, R7}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; use crate::sdmmc::sd::Addressable; use crate::time::Hertz; use crate::{interrupt, peripherals}; /// Module for SD and EMMC cards pub mod sd; /// Module for SDIO interface pub mod sdio; /// Interrupt handler. pub struct InterruptHandler { _phantom: PhantomData, } impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { T::state().waker.wake(); let status = T::info().regs.star().read(); T::info().regs.maskr().modify(|w| { if status.dcrcfail() { w.set_dcrcfailie(false) } if status.dtimeout() { w.set_dtimeoutie(false) } if status.dataend() { w.set_dataendie(false) } if status.dbckend() { w.set_dbckendie(false) } #[cfg(sdmmc_v1)] if status.stbiterr() { w.set_stbiterre(false) } #[cfg(sdmmc_v2)] if status.dabort() { w.set_dabortie(false) } }); } } struct U128(pub u128); trait TypedResp: Resp { type Word: From; } impl From for () { fn from(value: U128) -> Self { match value.0 { 0 => (), _ => unreachable!(), } } } impl From for u32 { fn from(value: U128) -> Self { unwrap!(value.0.try_into()) } } impl From for u128 { fn from(value: U128) -> Self { value.0 } } impl TypedResp for Rz { type Word = (); } impl TypedResp for R1 { type Word = u32; } impl TypedResp for R2 { type Word = u128; } impl TypedResp for R3 { type Word = u32; } impl TypedResp for R6 { type Word = u32; } impl TypedResp for R7 { type Word = u32; } /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. const SD_INIT_FREQ: Hertz = Hertz(400_000); /// The signalling scheme used on the SDMMC bus #[non_exhaustive] #[allow(missing_docs)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Signalling { SDR12, SDR25, SDR50, SDR104, DDR50, } impl Default for Signalling { fn default() -> Self { Signalling::SDR12 } } const fn aligned_mut(x: &mut [u32]) -> &mut Aligned { let len = x.len() * 4; unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len)) } } const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { let len = x.len() * 4; unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len) } } #[allow(unused)] const fn slice32_mut(x: &mut Aligned) -> &mut [u32] { let len = (size_of_val(x) + 4 - 1) / 4; unsafe { slice::from_raw_parts_mut(x as *mut Aligned as *mut u32, len) } } const fn aligned_ref(x: &[u32]) -> &Aligned { let len = x.len() * 4; unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as *const u8, len)) } } const fn slice8_ref(x: &[u32]) -> &[u8] { let len = x.len() * 4; unsafe { slice::from_raw_parts(x.as_ptr() as *const u8, len) } } #[allow(unused)] const fn slice32_ref(x: &Aligned) -> &[u32] { let len = (size_of_val(x) + 4 - 1) / 4; unsafe { slice::from_raw_parts(x as *const Aligned as *const u32, len) } } /// Errors #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// Timeout reported by the hardware Timeout, /// Timeout reported by the software driver. SoftwareTimeout, /// Unsupported card version. UnsupportedCardVersion, /// Unsupported card type. UnsupportedCardType, /// Unsupported voltage. UnsupportedVoltage, /// CRC 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. SignalingSwitchFailed, /// Underrun error Underrun, /// ST bit error. #[cfg(sdmmc_v1)] StBitErr, } #[repr(u8)] enum PowerCtrl { Off = 0b00, On = 0b11, } enum DatapathMode { Block(BlockSize), Byte, } 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 /// `sdmmc_ck` in Hertz. /// /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. #[cfg(sdmmc_v1)] fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { // sdmmc_v1 maximum clock is 50 MHz if sdmmc_ck > 50_000_000 { return Err(Error::BadClock); } // bypass divisor if ker_ck.0 <= sdmmc_ck { return Ok((true, 0, ker_ck)); } let clk_div = match ker_ck.0.div_ceil(sdmmc_ck) { 0 | 1 => Ok(0), x @ 2..=258 => Ok((x - 2) as u8), _ => Err(Error::BadClock), }?; // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); Ok((false, clk_div, clk_f)) } fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { match bus_width { BusWidth::One => (0, 1u32), BusWidth::Four => (1, 4u32), BusWidth::Eight => (2, 8u32), _ => panic!("Invalid Bus Width"), } } #[repr(u8)] enum BlockSize { Size1 = 0b0000, Size2 = 0b0001, Size4 = 0b0010, Size8 = 0b0011, Size16 = 0b0100, Size32 = 0b0101, Size64 = 0b0110, Size128 = 0b0111, Size256 = 0b1000, Size512 = 0b1001, Size1024 = 0b1010, Size2048 = 0b1011, Size4096 = 0b1100, Size8192 = 0b1101, Size16384 = 0b1110, } const fn block_size(bytes: usize) -> BlockSize { match bytes { 1 => BlockSize::Size1, 2 => BlockSize::Size2, 4 => BlockSize::Size4, 8 => BlockSize::Size8, 16 => BlockSize::Size16, 32 => BlockSize::Size32, 64 => BlockSize::Size64, 128 => BlockSize::Size128, 256 => BlockSize::Size256, 512 => BlockSize::Size512, 1024 => BlockSize::Size1024, 2048 => BlockSize::Size2048, 4096 => BlockSize::Size4096, 8192 => BlockSize::Size8192, 16384 => BlockSize::Size16384, _ => core::unreachable!(), } } /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to /// `sdmmc_ck` in Hertz. /// /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. #[cfg(sdmmc_v2)] fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { match ker_ck.0.div_ceil(sdmmc_ck) { 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] let clk_div = x.div_ceil(2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); Ok((false, clk_div, clk)) } _ => Err(Error::BadClock), } } #[cfg(sdmmc_v1)] type Transfer<'a> = crate::dma::Transfer<'a>; #[cfg(sdmmc_v2)] struct Transfer<'a> { _dummy: PhantomData<&'a ()>, } struct WrappedTransfer<'a> { _transfer: Transfer<'a>, sdmmc: &'a Sdmmc<'a>, defused: bool, } impl<'a> WrappedTransfer<'a> { pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self { Self { _transfer, sdmmc, defused: false, } } pub fn defuse(&mut self) { self.defused = true; } } impl<'a> Drop for WrappedTransfer<'a> { fn drop(&mut self) { if !self.defused { self.sdmmc.on_drop(); } } } #[cfg(all(sdmmc_v1, dma))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), priority: crate::dma::Priority::VeryHigh, circular: false, half_transfer_ir: false, complete_transfer_ir: true, }; #[cfg(all(sdmmc_v1, not(dma)))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { priority: crate::dma::Priority::VeryHigh, circular: false, half_transfer_ir: false, complete_transfer_ir: true, }; /// SDMMC configuration /// /// Default values: /// data_transfer_timeout: 5_000_000 #[non_exhaustive] pub struct Config { /// The timeout to be set for data transfers, in card bus clock periods pub data_transfer_timeout: u32, } impl Default for Config { fn default() -> Self { Self { data_transfer_timeout: 5_000_000, } } } /// Sdmmc device pub struct Sdmmc<'d> { info: &'static Info, state: &'static State, ker_clk: Hertz, #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, clk: Peri<'d, AnyPin>, cmd: Peri<'d, AnyPin>, d0: Peri<'d, AnyPin>, d1: Option>, d2: Option>, d3: Option>, d4: Option>, d5: Option>, d6: Option>, d7: Option>, config: Config, /// Current clock to card clock: Hertz, /// Current signalling scheme to card signalling: Signalling, } const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); #[cfg(gpio_v1)] const CMD_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh); #[cfg(gpio_v2)] const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up); const DATA_AF: AfType = CMD_AF; #[cfg(sdmmc_v1)] impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 1 data lane. pub fn new_1bit( 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>, config: Config, ) -> Self { critical_section::with(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); }); Self::new_inner( sdmmc, new_dma_nonopt!(dma), clk.into(), cmd.into(), d0.into(), None, None, None, None, None, None, None, config, ) } /// Create a new SDMMC driver, with 4 data lanes. pub fn new_4bit( 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>, config: Config, ) -> Self { critical_section::with(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); set_as_af!(d1, DATA_AF); set_as_af!(d2, DATA_AF); set_as_af!(d3, 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()), None, None, None, None, config, ) } } #[cfg(sdmmc_v1)] impl<'d> Sdmmc<'d> { /// 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(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); set_as_af!(d1, DATA_AF); set_as_af!(d2, DATA_AF); set_as_af!(d3, DATA_AF); set_as_af!(d4, DATA_AF); set_as_af!(d5, DATA_AF); set_as_af!(d6, DATA_AF); set_as_af!(d7, 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, ) } } #[cfg(sdmmc_v2)] impl<'d> Sdmmc<'d> { /// Create a new SDMMC driver, with 1 data lane. pub fn new_1bit( 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>, config: Config, ) -> Self { critical_section::with(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); }); 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. pub fn new_4bit( 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>, config: Config, ) -> Self { critical_section::with(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); set_as_af!(d1, DATA_AF); set_as_af!(d2, DATA_AF); set_as_af!(d3, DATA_AF); }); Self::new_inner( sdmmc, clk.into(), cmd.into(), d0.into(), Some(d1.into()), Some(d2.into()), Some(d3.into()), None, None, None, None, config, ) } } #[cfg(sdmmc_v2)] impl<'d> Sdmmc<'d> { /// 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(|_| { set_as_af!(clk, CLK_AF); set_as_af!(cmd, CMD_AF); set_as_af!(d0, DATA_AF); set_as_af!(d1, DATA_AF); set_as_af!(d2, DATA_AF); set_as_af!(d3, DATA_AF); set_as_af!(d4, DATA_AF); set_as_af!(d5, DATA_AF); set_as_af!(d6, DATA_AF); set_as_af!(d7, 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, ) } } impl<'d> Sdmmc<'d> { fn enable_interrupts(&self) { let regs = self.info.regs; regs.maskr().write(|w| { w.set_dcrcfailie(true); w.set_dtimeoutie(true); w.set_dataendie(true); w.set_dbckendie(true); #[cfg(sdmmc_v1)] w.set_stbiterre(true); #[cfg(sdmmc_v2)] w.set_dabortie(true); }); } fn new_inner( _sdmmc: Peri<'d, T>, #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, clk: Peri<'d, AnyPin>, cmd: Peri<'d, AnyPin>, d0: Peri<'d, AnyPin>, d1: Option>, d2: Option>, d3: Option>, d4: Option>, d5: Option>, d6: Option>, d7: Option>, config: Config, ) -> Self { rcc::enable_and_reset_without_stop::(); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; let info = T::info(); let state = T::state(); let ker_clk = T::frequency(); info.regs.clkcr().write(|w| { w.set_pwrsav(false); w.set_negedge(false); // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. // See chip erratas for more details. #[cfg(sdmmc_v1)] w.set_hwfc_en(false); #[cfg(sdmmc_v2)] w.set_hwfc_en(true); #[cfg(sdmmc_v1)] w.set_clken(true); }); // Power off, writen 00: Clock to the card is stopped; // D[7:0], CMD, and CK are driven high. info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); Self { info, state, ker_clk, #[cfg(sdmmc_v1)] dma, clk, cmd, d0, d1, d2, d3, d4, d5, d6, d7, config, clock: SD_INIT_FREQ, signalling: Default::default(), } } /// Data transfer is in progress #[inline] fn data_active(&self) -> bool { let regs = self.info.regs; let status = regs.star().read(); #[cfg(sdmmc_v1)] return status.rxact() || status.txact(); #[cfg(sdmmc_v2)] return status.dpsmact(); } /// Coammand transfer is in progress #[inline] fn cmd_active(&self) -> bool { let regs = self.info.regs; let status = regs.star().read(); #[cfg(sdmmc_v1)] return status.cmdact(); #[cfg(sdmmc_v2)] return status.cpsmact(); } /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) #[inline] fn wait_idle(&self) { while self.data_active() || self.cmd_active() {} } fn bus_width(&self) -> BusWidth { match (self.d3.is_some(), self.d7.is_some()) { (true, true) => BusWidth::Eight, (true, false) => BusWidth::Four, _ => BusWidth::One, } } /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned #[allow(unused_variables)] fn prepare_datapath_read<'a>( &'a self, buffer: &'a mut Aligned, mode: DatapathMode, ) -> WrappedTransfer<'a> { let regs = self.info.regs; let (byte_mode, block_size) = match mode { DatapathMode::Block(block_size) => (false, block_size as u8), DatapathMode::Byte => (true, 0), }; // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] let transfer = unsafe { self.dma.read_unchecked( regs.fifor().as_ptr() as *mut u32, slice32_mut(buffer), DMA_TRANSFER_OPTIONS, ) }; #[cfg(sdmmc_v2)] let transfer = { regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); regs.idmactrlr().modify(|w| w.set_idmaen(true)); Transfer { _dummy: core::marker::PhantomData, } }; #[cfg(sdmmc_v2)] let byte_mode = byte_mode as u8; regs.dctrl().modify(|w| { w.set_dtmode(byte_mode); w.set_dblocksize(block_size as u8); w.set_dtdir(true); #[cfg(sdmmc_v1)] { w.set_dmaen(true); w.set_dten(true); } }); self.enable_interrupts(); WrappedTransfer::new(transfer, &self) } /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned fn prepare_datapath_write<'a>(&'a self, buffer: &'a Aligned, mode: DatapathMode) -> WrappedTransfer<'a> { let regs = self.info.regs; let (byte_mode, block_size) = match mode { DatapathMode::Block(block_size) => (false, block_size as u8), DatapathMode::Byte => (true, 0), }; // Command AND Data state machines must be idle self.wait_idle(); self.clear_interrupt_flags(); regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); // SAFETY: No other functions use the dma #[cfg(sdmmc_v1)] let transfer = unsafe { self.dma.write_unchecked( slice32_ref(buffer), regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS, ) }; #[cfg(sdmmc_v2)] let transfer = { regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); regs.idmactrlr().modify(|w| w.set_idmaen(true)); Transfer { _dummy: core::marker::PhantomData, } }; #[cfg(sdmmc_v2)] let byte_mode = byte_mode as u8; regs.dctrl().modify(|w| { w.set_dtmode(byte_mode); w.set_dblocksize(block_size as u8); w.set_dtdir(false); #[cfg(sdmmc_v1)] { w.set_dmaen(true); w.set_dten(true); } }); self.enable_interrupts(); WrappedTransfer::new(transfer, &self) } /// Stops the DMA datapath fn stop_datapath(&self) { let regs = self.info.regs; #[cfg(sdmmc_v1)] regs.dctrl().modify(|w| { w.set_dmaen(false); w.set_dten(false); }); #[cfg(sdmmc_v2)] regs.idmactrlr().modify(|w| w.set_idmaen(false)); } fn init_idle(&mut self) -> Result<(), Error> { let regs = self.info.regs; self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?; regs.dtimer() .write(|w| w.set_datatime(self.config.data_transfer_timeout)); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); self.cmd(common_cmd::idle(), false) } /// Sets the CLKDIV field in CLKCR. Updates clock field in self fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> { let regs = self.info.regs; let (widbus, width_u32) = bus_width_vals(width); let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq.0)?; // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 // Section 55.5.8 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32); self.clock = new_clock; // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS self.wait_idle(); regs.clkcr().modify(|w| { w.set_clkdiv(clkdiv); #[cfg(sdmmc_v1)] w.set_bypass(_bypass); w.set_widbus(widbus); }); Ok(()) } fn get_cid(&self) -> Result { self.cmd(common_cmd::all_send_cid(), false) // CMD2 } fn get_csd(&self, address: u16) -> Result { self.cmd(common_cmd::send_csd(address), false) } /// Query the card status (CMD13, returns R1) fn read_status(&self, card: &A) -> Result, Error> where CardStatus: From, { let rca = card.get_address(); Ok(self.cmd(common_cmd::card_status(rca, false), false)?.into()) // CMD13 } /// 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, rca: Option) -> Result<(), Error> { // Determine Relative Card Address (RCA) of given card let rca = rca.unwrap_or(0); let resp = self.cmd(common_cmd::select_card(rca), false); if let Err(Error::Timeout) = resp && rca == 0 { return Ok(()); } resp?; Ok(()) } /// Clear flags in interrupt clear register #[inline] fn clear_interrupt_flags(&self) { let regs = self.info.regs; regs.icr().write(|w| { w.set_ccrcfailc(true); w.set_dcrcfailc(true); w.set_ctimeoutc(true); w.set_dtimeoutc(true); w.set_txunderrc(true); w.set_rxoverrc(true); w.set_cmdrendc(true); w.set_cmdsentc(true); w.set_dataendc(true); w.set_dbckendc(true); w.set_sdioitc(true); #[cfg(sdmmc_v1)] w.set_stbiterrc(true); #[cfg(sdmmc_v2)] { w.set_dholdc(true); w.set_dabortc(true); w.set_busyd0endc(true); w.set_ackfailc(true); w.set_acktimeoutc(true); w.set_vswendc(true); w.set_ckstopc(true); w.set_idmatec(true); w.set_idmabtcc(true); } }); } /// Send command to card #[allow(unused_variables)] fn cmd(&self, cmd: Cmd, data: bool) -> Result { let regs = self.info.regs; self.clear_interrupt_flags(); // CP state machine must be idle while self.cmd_active() {} // Command arg regs.argr().write(|w| w.set_cmdarg(cmd.arg)); // Command index and start CP State Machine regs.cmdr().write(|w| { w.set_waitint(false); w.set_waitresp(get_waitresp_val(cmd.response_len())); w.set_cmdindex(cmd.cmd); w.set_cpsmen(true); #[cfg(sdmmc_v2)] { // Special mode in CP State Machine // CMD12: Stop Transmission let cpsm_stop_transmission = cmd.cmd == 12; w.set_cmdstop(cpsm_stop_transmission); w.set_cmdtrans(data); } }); let mut status; if cmd.response_len() == ResponseLen::Zero { // Wait for CMDSENT or a timeout while { status = regs.star().read(); !(status.ctimeout() || status.cmdsent()) } {} } else { // Wait for CMDREND or CCRCFAIL or a timeout while { status = regs.star().read(); !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) } {} } if status.ctimeout() { return Err(Error::Timeout); } else if status.ccrcfail() { return Err(Error::Crc); } Ok(match R::LENGTH { ResponseLen::Zero => U128(0u128), ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128), ResponseLen::R136 => { let cid0 = self.info.regs.respr(0).read().cardstatus() as u128; let cid1 = self.info.regs.respr(1).read().cardstatus() as u128; let cid2 = self.info.regs.respr(2).read().cardstatus() as u128; let cid3 = self.info.regs.respr(3).read().cardstatus() as u128; U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3)) } } .into()) } fn on_drop(&self) { let regs = self.info.regs; if self.data_active() { self.clear_interrupt_flags(); // Send abort // CP state machine must be idle while self.cmd_active() {} // Command arg regs.argr().write(|w| w.set_cmdarg(0)); // Command index and start CP State Machine regs.cmdr().write(|w| { w.set_waitint(false); w.set_waitresp(get_waitresp_val(ResponseLen::R48)); w.set_cmdindex(12); w.set_cpsmen(true); #[cfg(sdmmc_v2)] { w.set_cmdstop(true); w.set_cmdtrans(false); } }); // Wait for the abort while self.data_active() {} } regs.maskr().write(|_| ()); // disable irqs self.clear_interrupt_flags(); self.stop_datapath(); } /// Wait for a previously started datapath transfer to complete from an interrupt. #[inline] #[allow(unused)] async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> { let res = poll_fn(|cx| { // Compiler might not be sufficiently constrained here // https://github.com/embassy-rs/embassy/issues/4723 self.state.waker.register(cx.waker()); let status = self.info.regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); } if status.dtimeout() { return Poll::Ready(Err(Error::Timeout)); } if status.txunderr() { return Poll::Ready(Err(Error::Underrun)); } #[cfg(sdmmc_v1)] if status.stbiterr() { return Poll::Ready(Err(Error::StBitErr)); } #[cfg(sdmmc_v1)] let done = match block { true => status.dbckend(), false => status.dataend(), }; #[cfg(sdmmc_v2)] let done = status.dataend(); if done { return Poll::Ready(Ok(())); } Poll::Pending }) .await; self.clear_interrupt_flags(); self.stop_datapath(); transfer.defuse(); drop(transfer); res } /// Get the current SDMMC bus clock pub fn clock(&self) -> Hertz { self.clock } } impl<'d> Drop for Sdmmc<'d> { fn drop(&mut self) { // T::Interrupt::disable(); self.on_drop(); self.info.rcc.disable_without_stop(); critical_section::with(|_| { self.clk.set_as_disconnected(); self.cmd.set_as_disconnected(); self.d0.set_as_disconnected(); if let Some(x) = &mut self.d1 { x.set_as_disconnected(); } if let Some(x) = &mut self.d2 { x.set_as_disconnected(); } 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(); } }); } } ////////////////////////////////////////////////////// type Regs = RegBlock; struct Info { regs: Regs, rcc: RccInfo, } struct State { waker: AtomicWaker, } impl State { const fn new() -> Self { Self { waker: AtomicWaker::new(), } } } trait SealedInstance { fn info() -> &'static Info; fn state() -> &'static State; } /// SDMMC instance trait. #[allow(private_bounds)] pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { /// Interrupt for this instance. type Interrupt: interrupt::typelevel::Interrupt; } pin_trait!(CkPin, Instance); pin_trait!(CmdPin, Instance); pin_trait!(D0Pin, Instance); pin_trait!(D1Pin, Instance); pin_trait!(D2Pin, Instance); pin_trait!(D3Pin, Instance); pin_trait!(D4Pin, Instance); pin_trait!(D5Pin, Instance); pin_trait!(D6Pin, Instance); pin_trait!(D7Pin, Instance); #[cfg(sdmmc_v1)] dma_trait!(SdmmcDma, Instance); foreach_peripheral!( (sdmmc, $inst:ident) => { impl SealedInstance for peripherals::$inst { fn info() -> &'static Info { static INFO: Info = Info { regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, rcc: crate::peripherals::$inst::RCC_INFO, }; &INFO } fn state() -> &'static State { static STATE: State = State::new(); &STATE } } impl Instance for peripherals::$inst { type Interrupt = crate::interrupt::typelevel::$inst; } }; );