From 0dfa19299224f28ab686062d472c0ccea72ffd5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:54:22 +0200 Subject: stm32/sdmmc: remove "inner" layer. --- embassy-stm32/src/sdmmc/mod.rs | 1462 ++++++++++++++++++---------------------- 1 file changed, 651 insertions(+), 811 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 22b578dcf..23ece3a2a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -41,7 +41,7 @@ impl Default for Signalling { } #[repr(align(4))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataBlock(pub [u8; 512]); @@ -61,7 +61,7 @@ impl DerefMut for DataBlock { /// Errors #[non_exhaustive] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { Timeout, @@ -175,7 +175,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { - // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] let clk_div = ((x + 1) / 2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); @@ -204,9 +204,10 @@ impl Default for Config { } /// Sdmmc device -pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { +pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { _peri: PeripheralRef<'d, T>, irq: PeripheralRef<'d, T::Interrupt>, + #[allow(unused)] dma: PeripheralRef<'d, Dma>, clk: PeripheralRef<'d, AnyPin>, @@ -305,49 +306,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { config, ) } - - fn new_inner( - sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - clk: PeripheralRef<'d, AnyPin>, - cmd: PeripheralRef<'d, AnyPin>, - d0: PeripheralRef<'d, AnyPin>, - d1: Option>, - d2: Option>, - d3: Option>, - config: Config, - ) -> Self { - into_ref!(sdmmc, irq, dma); - - T::enable(); - T::reset(); - - let inner = T::inner(); - unsafe { inner.new_inner() }; - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _peri: sdmmc, - irq, - dma, - - clk, - cmd, - d0, - d1, - d2, - d3, - - config, - clock: SD_INIT_FREQ, - signalling: Default::default(), - card: None, - } - } } #[cfg(sdmmc_v2)] @@ -375,6 +333,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -417,6 +376,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -426,10 +386,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { config, ) } +} +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn new_inner( sdmmc: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, clk: PeripheralRef<'d, AnyPin>, cmd: PeripheralRef<'d, AnyPin>, d0: PeripheralRef<'d, AnyPin>, @@ -438,22 +401,41 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { d3: Option>, config: Config, ) -> Self { - into_ref!(sdmmc, irq); + into_ref!(sdmmc, irq, dma); T::enable(); T::reset(); - let inner = T::inner(); - unsafe { inner.new_inner() }; - irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); + let regs = T::regs(); + unsafe { + 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. + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); + } + Self { _peri: sdmmc, irq, - dma: NoDma.into_ref(), + dma, clk, cmd, @@ -468,338 +450,303 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { card: None, } } -} -impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { + /// Data transfer is in progress #[inline(always)] - pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { - let inner = T::inner(); - let freq = freq.into(); - - let bus_width = match self.d3.is_some() { - true => BusWidth::Four, - false => BusWidth::One, - }; + fn data_active() -> bool { + let regs = T::regs(); - inner - .init_card( - freq, - bus_width, - &mut self.card, - &mut self.signalling, - T::kernel_clk(), - &mut self.clock, - T::state(), - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + 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(always)] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let card_capacity = self.card()?.card_type; - let inner = T::inner(); - let state = T::state(); + fn cmd_active() -> bool { + let regs = T::regs(); - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - inner - .read_block( - block_idx, - buf, - card_capacity, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); + } } - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let inner = T::inner(); - let state = T::state(); - - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - inner - .write_block( - block_idx, - buf, - card, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await + /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) + #[inline(always)] + fn wait_idle() { + while Self::data_active() || Self::cmd_active() {} } - /// Get a reference to the initialized card - /// - /// # Errors + /// # Safety /// - /// Returns Error::NoCard if [`init_card`](#method.init_card) - /// has not previously succeeded - #[inline(always)] - pub fn card(&self) -> Result<&Card, Error> { - self.card.as_ref().ok_or(Error::NoCard) - } + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); - /// Get the current SDMMC bus clock - pub fn clock(&self) -> Hertz { - self.clock - } + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); - #[inline(always)] - fn on_interrupt(_: *mut ()) { - let regs = T::inner(); - let state = T::state(); + // NOTE(unsafe) We have exclusive access to the regisers - regs.data_interrupts(false); - state.wake(); - } -} + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); -impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { - fn drop(&mut self) { - self.irq.disable(); - let inner = T::inner(); - unsafe { inner.on_drop() }; + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_read( + request, + regs.fifor().ptr() as *const u32, + buffer, + 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), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } - critical_section::with(|_| unsafe { - 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(); + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); } }); } -} -pub struct SdmmcInner(pub(crate) RegBlock); - -impl SdmmcInner { /// # Safety /// - /// Access to `regs` registers should be exclusive - unsafe fn new_inner(&self) { - let regs = self.0; + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); + + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); - regs.clkcr().write(|w| { - w.set_pwrsav(false); - w.set_negedge(false); + // NOTE(unsafe) We have exclusive access to the regisers - // 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); + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_write( + request, + buffer, + regs.fifor().ptr() as *mut u32, + 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), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r() + .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); #[cfg(sdmmc_v1)] - w.set_clken(true); + { + w.set_dmaen(true); + w.set_dten(true); + } }); - - // Power off, writen 00: Clock to the card is stopped; - // D[7:0], CMD, and CK are driven high. - regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); } - /// Initializes card (if present) and sets the bus at the - /// specified frequency. - #[allow(clippy::too_many_arguments)] - async fn init_card>( - &self, - freq: Hertz, - bus_width: BusWidth, - old_card: &mut Option, - signalling: &mut Signalling, - ker_ck: Hertz, - clock: &mut Hertz, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let regs = self.0; + /// Stops the DMA datapath + fn stop_datapath() { + let regs = T::regs(); - // NOTE(unsafe) We have exclusive access to the peripheral unsafe { - // 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)); - *clock = init_clock; + #[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)); + } + } - // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + /// Sets the CLKDIV field in CLKCR. Updates clock field in self + fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { + let regs = T::regs(); + + let width_u32 = match width { + BusWidth::One => 1u32, + BusWidth::Four => 4u32, + BusWidth::Eight => 8u32, + _ => panic!("Invalid Bus Width"), + }; + + let ker_ck = T::kernel_clk(); + let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + + // 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!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); + self.clock = new_clock; + // NOTE(unsafe) We have exclusive access to the regblock + unsafe { + // CPSMACT and DPSMACT must be 0 to set CLKDIV + 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(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(); - - let mut card = if r1 == 0x1AA { - // Card echoed back the pattern. Must be at least v2 - Card::default() - } else { - return Err(Error::UnsupportedCardVersion); - }; + Ok(()) + } - let ocr = loop { - // Signal that next command is a app command - self.cmd(Cmd::app_cmd(0), false)?; // CMD55 + /// 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 arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 - | CmdAppOper::HIGH_CAPACITY as u32 - | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; - - // Initialize card - match self.cmd(Cmd::app_op_cmd(arg), 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; - } + 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, }; - if ocr.high_capacity() { - // Card is SDHC or SDXC or SDUC - card.card_type = CardCapacity::SDHC; - } else { - card.card_type = CardCapacity::SDSC; - } - card.ocr = ocr; - - self.cmd(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(); - - self.cmd(Cmd::send_rel_addr(), false)?; - card.rca = regs.respr(0).read().cardstatus() >> 16; - - self.cmd(Cmd::send_csd(card.rca << 16), 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))?; - - self.get_scr(&mut card, waker_reg, data_transfer_timeout, dma).await?; + let mut status = [0u32; 16]; - // 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), - }; - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; - self.cmd(Cmd::cmd6(acmd_arg), false)?; + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + unsafe { + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 - regs.clkcr().modify(|w| { - w.set_widbus(match width { - BusWidth::One => 0, - BusWidth::Four => 1, - BusWidth::Eight => 2, - _ => panic!("Invalid Bus Width"), - }) - }); + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; - // Set Clock - if freq.0 <= 25_000_000 { - // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; - } else { - // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width, ker_ck, clock)?; + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); - // Read status - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; + // 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(); + } - if freq.0 > 25_000_000 { - // Switch to SDR25 - *signalling = self - .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout, dma) - .await?; + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); - if *signalling == Signalling::SDR25 { - // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; + // Function Selection of Function Group 1 + let selection = (u32::from_be(status[4]) >> 24) & 0xF; - if self.read_status(&card)?.state() != CurrentState::Transfer { - return Err(Error::SignalingSwitchFailed); - } + 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), } } - // Read status after signalling change - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; - old_card.replace(card); + Err(e) => Err(e), } + } - Ok(()) + /// Query the card status (CMD13, returns R1) + fn read_status(&self, card: &Card) -> Result { + let regs = T::regs(); + let rca = card.rca; + + self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + + // NOTE(unsafe) Atomic read with no side-effects + let r1 = unsafe { regs.respr(0).read().cardstatus() }; + Ok(r1.into()) } - async fn read_block>( - &self, - block_idx: u32, - buffer: &mut [u32; 128], - capacity: CardCapacity, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - // Always read 1 block of 512 bytes - // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match capacity { - CardCapacity::SDSC => block_idx * 512, - _ => block_idx, - }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + /// 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; + + self.cmd(Cmd::set_block_length(64), false)?; // CMD16 + self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + + let mut status = [0u32; 16]; - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); unsafe { - self.prepare_datapath_read(buffer, 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); } - self.cmd(Cmd::read_single_block(address), true)?; + self.cmd(Cmd::card_status(0), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -812,49 +759,106 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); if res.is_ok() { on_drop.defuse(); - self.stop_datapath(); + Self::stop_datapath(); + + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); + } + self.card.as_mut().unwrap().status = status.into(); } res } - async fn write_block>( - &self, - block_idx: u32, - buffer: &[u32; 128], - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - // 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, - _ => block_idx, - }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + /// 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> { + // Determine Relative Card Address (RCA) of given card + let rca = card.map(|c| c.rca << 16).unwrap_or(0); - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let r = self.cmd(Cmd::sel_desel_card(rca), false); + match (r, rca) { + (Err(Error::Timeout), 0) => Ok(()), + _ => r, + } + } - // sdmmc_v1 uses different cmd/dma order than v2, but only for writes - #[cfg(sdmmc_v1)] - self.cmd(Cmd::write_single_block(address), true)?; + /// Clear flags in interrupt clear register + #[inline(always)] + fn clear_interrupt_flags() { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + 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_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); + } + }); + } + } + /// Enables the interrupts for data transfer + #[inline(always)] + fn data_interrupts(enable: bool) { + let regs = T::regs(); + // NOTE(unsafe) Atomic write unsafe { - self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + regs.maskr().write(|w| { + w.set_dcrcfailie(enable); + w.set_dtimeoutie(enable); + w.set_dataendie(enable); + + #[cfg(sdmmc_v2)] + w.set_dabortie(enable); + }); } + } - #[cfg(sdmmc_v2)] - self.cmd(Cmd::write_single_block(address), true)?; + async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { + // Read the the 64-bit SCR register + self.cmd(Cmd::set_block_length(8), false)?; // CMD16 + self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + + let mut scr = [0u32; 2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut scr[..], 8, 3); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd51(), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -867,348 +871,287 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); + Self::clear_interrupt_flags(); - // TODO: Make this configurable - let mut timeout: u32 = 0x00FF_FFFF; + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); - // Try to read card status (ACMD13) - while timeout > 0 { - match self.read_sd_status(card, waker_reg, data_transfer_timeout, dma).await { - Ok(_) => return Ok(()), - Err(Error::Timeout) => (), // Try again - Err(e) => return Err(e), - } - timeout -= 1; - } - Err(Error::SoftwareTimeout) + unsafe { + let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); + card.scr = SCR(u64::from_be_bytes(*scr_bytes)); } - Err(e) => Err(e), } + res } - /// Data transfer is in progress - #[inline(always)] - fn data_active(&self) -> bool { - let regs = self.0; - - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - 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(always)] - fn cmd_active(&self) -> bool { - let regs = self.0; + /// Send command to card + #[allow(unused_variables)] + fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + let regs = T::regs(); - // NOTE(unsafe) Atomic read with no side-effects + Self::clear_interrupt_flags(); + // NOTE(safety) Atomic operations unsafe { - 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(always)] - fn wait_idle(&self) { - while self.data_active() || self.cmd_active() {} - } - - /// # Safety - /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_read>( - &self, - buffer: *mut [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; + // CP state machine must be idle + while Self::cmd_active() {} - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); + // Command arg + regs.argr().write(|w| w.set_cmdarg(cmd.arg)); - // NOTE(unsafe) We have exclusive access to the regisers + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(cmd.resp as u8); + w.set_cmdindex(cmd.cmd); + w.set_cpsmen(true); - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); + #[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); + } + }); - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_read( - request, - regs.fifor().ptr() as *const u32, - buffer, - 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), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + let mut status; + if cmd.resp == Response::None { + // 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()) + } {} + } - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); + if status.ctimeout() { + return Err(Error::Timeout); + } else if status.ccrcfail() { + return Err(Error::Crc); } - }); + Ok(()) + } } /// # Safety /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_write>( - &self, - buffer: *const [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; - - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); - - // NOTE(unsafe) We have exclusive access to the regisers - - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_write( - request, - buffer, - regs.fifor().ptr() as *mut u32, - 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), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r() - .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + /// Ensure that `regs` has exclusive access to the regblocks + unsafe fn on_drop() { + let regs = T::regs(); + if Self::data_active() { + Self::clear_interrupt_flags(); + // Send abort + // CP state machine must be idle + while Self::cmd_active() {} - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); - } + // Command arg + regs.argr().write(|w| w.set_cmdarg(0)); - /// Stops the DMA datapath - fn stop_datapath(&self) { - let regs = self.0; + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(Response::Short as u8); + w.set_cmdindex(12); + w.set_cpsmen(true); - unsafe { - #[cfg(sdmmc_v1)] - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); + #[cfg(sdmmc_v2)] + { + w.set_cmdstop(true); + w.set_cmdtrans(false); + } }); - #[cfg(sdmmc_v2)] - regs.idmactrlr().modify(|w| w.set_idmaen(false)); + + // Wait for the abort + while Self::data_active() {} } + Self::data_interrupts(false); + Self::clear_interrupt_flags(); + Self::stop_datapath(); } - /// Sets the CLKDIV field in CLKCR. Updates clock field in self - fn clkcr_set_clkdiv(&self, freq: u32, width: BusWidth, ker_ck: Hertz, clock: &mut Hertz) -> Result<(), Error> { - let regs = self.0; + /// 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(); + let ker_ck = T::kernel_clk(); - let width_u32 = match width { - BusWidth::One => 1u32, - BusWidth::Four => 4u32, - BusWidth::Eight => 8u32, - _ => panic!("Invalid Bus Width"), + let bus_width = match self.d3.is_some() { + true => BusWidth::Four, + false => BusWidth::One, }; - let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + // NOTE(unsafe) We have exclusive access to the peripheral + unsafe { + // 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; - // 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!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); - *clock = new_clock; + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); - // NOTE(unsafe) We have exclusive access to the regblock - unsafe { - // CPSMACT and DPSMACT must be 0 to set CLKDIV - self.wait_idle(); regs.clkcr().modify(|w| { + w.set_widbus(0); w.set_clkdiv(clkdiv); #[cfg(sdmmc_v1)] w.set_bypass(_bypass); }); - } - Ok(()) - } + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); + self.cmd(Cmd::idle(), false)?; - /// 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>( - &self, - signalling: Signalling, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result { - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" + // Check if cards supports CMD8 (with pattern) + self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; + let r1 = regs.respr(0).read().cardstatus(); - 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 mut card = if r1 == 0x1AA { + // Card echoed back the pattern. Must be at least v2 + Card::default() + } else { + return Err(Error::UnsupportedCardVersion); }; - let mut status = [0u32; 16]; + let ocr = loop { + // Signal that next command is a app command + self.cmd(Cmd::app_cmd(0), false)?; // CMD55 - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 + | CmdAppOper::HIGH_CAPACITY as u32 + | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; - unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 + // Initialize card + match self.cmd(Cmd::app_op_cmd(arg), 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; + } + }; - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; + if ocr.high_capacity() { + // Card is SDHC or SDXC or SDUC + card.card_type = CardCapacity::SDHC; + } else { + card.card_type = CardCapacity::SDSC; + } + card.ocr = ocr; - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); + self.cmd(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(); + + self.cmd(Cmd::send_rel_addr(), false)?; + card.rca = regs.respr(0).read().cardstatus() >> 16; + + self.cmd(Cmd::send_csd(card.rca << 16), 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))?; + + self.get_scr(&mut card).await?; + + // 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), + }; + self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + self.cmd(Cmd::cmd6(acmd_arg), false)?; + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); + + regs.clkcr().modify(|w| { + w.set_widbus(match width { + BusWidth::One => 0, + BusWidth::Four => 1, + BusWidth::Eight => 2, + _ => panic!("Invalid Bus Width"), + }) + }); + + // 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)?; } - 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(); - } + self.card = Some(card); - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); + // 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?; - // Function Selection of Function Group 1 - let selection = (u32::from_be(status[4]) >> 24) & 0xF; + if self.signalling == Signalling::SDR25 { + // Set final clock frequency + self.clkcr_set_clkdiv(freq.0, width)?; - 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), + if self.read_status(&card)?.state() != CurrentState::Transfer { + return Err(Error::SignalingSwitchFailed); + } } } - Err(e) => Err(e), + // Read status after signalling change + self.read_sd_status().await?; } - } - - /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &Card) -> Result { - let regs = self.0; - let rca = card.rca; - - self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 - // NOTE(unsafe) Atomic read with no side-effects - let r1 = unsafe { regs.respr(0).read().cardstatus() }; - Ok(r1.into()) + Ok(()) } - /// Reads the SD Status (ACMD13) - async fn read_sd_status>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let rca = card.rca; - self.cmd(Cmd::set_block_length(64), false)?; // CMD16 - self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + #[inline(always)] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let card_capacity = self.card()?.card_type; - let mut status = [0u32; 16]; + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + // 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, + _ => block_idx, + }; + self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_read(buffer, 512, 9); + Self::data_interrupts(true); } - self.cmd(Cmd::card_status(0), true)?; + self.cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -1221,112 +1164,46 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); if res.is_ok() { on_drop.defuse(); - self.stop_datapath(); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - card.status = status.into(); + Self::stop_datapath(); } 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> { - // Determine Relative Card Address (RCA) of given card - let rca = card.map(|c| c.rca << 16).unwrap_or(0); - - let r = self.cmd(Cmd::sel_desel_card(rca), false); - match (r, rca) { - (Err(Error::Timeout), 0) => Ok(()), - _ => r, - } - } - - /// Clear flags in interrupt clear register - #[inline(always)] - fn clear_interrupt_flags(&self) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - 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_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); - } - }); - } - } - - /// Enables the interrupts for data transfer - #[inline(always)] - fn data_interrupts(&self, enable: bool) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; - #[cfg(sdmmc_v2)] - w.set_dabortie(enable); - }); - } - } + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - async fn get_scr>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - // Read the the 64-bit SCR register - self.cmd(Cmd::set_block_length(8), false)?; // CMD16 - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + // 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, + _ => block_idx, + }; + self.cmd(Cmd::set_block_length(512), false)?; // CMD16 - let mut scr = [0u32; 2]; + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(move || unsafe { 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)?; unsafe { - self.prepare_datapath_read(&mut scr[..], 8, 3, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); + Self::data_interrupts(true); } - self.cmd(Cmd::cmd51(), true)?; + + #[cfg(sdmmc_v2)] + self.cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -1339,109 +1216,73 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - - unsafe { - let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); - card.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res - } + Self::clear_interrupt_flags(); - /// Send command to card - #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { - let regs = self.0; - - self.clear_interrupt_flags(); - // NOTE(safety) Atomic operations - unsafe { - // CP state machine must be idle - while self.cmd_active() {} - - // Command arg - regs.argr().write(|w| w.set_cmdarg(cmd.arg)); + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(cmd.resp as u8); - w.set_cmdindex(cmd.cmd); - w.set_cpsmen(true); + // TODO: Make this configurable + let mut timeout: u32 = 0x00FF_FFFF; - #[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); + // Try to read card status (ACMD13) + while timeout > 0 { + match self.read_sd_status().await { + Ok(_) => return Ok(()), + Err(Error::Timeout) => (), // Try again + Err(e) => return Err(e), + } + timeout -= 1; } - }); - - let mut status; - if cmd.resp == Response::None { - // 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); + Err(Error::SoftwareTimeout) } - Ok(()) + Err(e) => Err(e), } } - /// # Safety + /// Get a reference to the initialized card /// - /// Ensure that `regs` has exclusive access to the regblocks - unsafe fn on_drop(&self) { - let regs = self.0; - if self.data_active() { - self.clear_interrupt_flags(); - // Send abort - // CP state machine must be idle - while self.cmd_active() {} + /// # Errors + /// + /// Returns Error::NoCard if [`init_card`](#method.init_card) + /// has not previously succeeded + #[inline(always)] + pub fn card(&self) -> Result<&Card, Error> { + self.card.as_ref().ok_or(Error::NoCard) + } - // Command arg - regs.argr().write(|w| w.set_cmdarg(0)); + /// Get the current SDMMC bus clock + pub fn clock(&self) -> Hertz { + self.clock + } - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(Response::Short as u8); - w.set_cmdindex(12); - w.set_cpsmen(true); + #[inline(always)] + fn on_interrupt(_: *mut ()) { + Self::data_interrupts(false); + T::state().wake(); + } +} - #[cfg(sdmmc_v2)] - { - w.set_cmdstop(true); - w.set_cmdtrans(false); - } - }); +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { + fn drop(&mut self) { + self.irq.disable(); + unsafe { Self::on_drop() }; - // Wait for the abort - while self.data_active() {} - } - self.data_interrupts(false); - self.clear_interrupt_flags(); - self.stop_datapath(); + critical_section::with(|_| unsafe { + 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(); + } + }); } } @@ -1540,7 +1381,7 @@ pub(crate) mod sealed { pub trait Instance { type Interrupt: Interrupt; - fn inner() -> SdmmcInner; + fn regs() -> RegBlock; fn state() -> &'static AtomicWaker; fn kernel_clk() -> Hertz; } @@ -1629,9 +1470,8 @@ foreach_peripheral!( impl sealed::Instance for peripherals::$inst { type Interrupt = crate::interrupt::$inst; - fn inner() -> SdmmcInner { - const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); - INNER + fn regs() -> RegBlock { + crate::pac::$inst } fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { -- cgit