stm32/sdmmc: Fix SDIOv1 writes
This commit is contained in:
		
							parent
							
								
									e3f8020c3b
								
							
						
					
					
						commit
						a53f525f51
					
				| @ -192,6 +192,7 @@ mod low_level_api { | |||||||
|             options.flow_ctrl == crate::dma::FlowControl::Dma, |             options.flow_ctrl == crate::dma::FlowControl::Dma, | ||||||
|             "Peripheral flow control not supported" |             "Peripheral flow control not supported" | ||||||
|         ); |         ); | ||||||
|  |         assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); | ||||||
| 
 | 
 | ||||||
|         let ch = dma.ch(channel_number as _); |         let ch = dma.ch(channel_number as _); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ use core::task::Waker; | |||||||
| use embassy_cortex_m::interrupt::Priority; | use embassy_cortex_m::interrupt::Priority; | ||||||
| use embassy_sync::waitqueue::AtomicWaker; | use embassy_sync::waitqueue::AtomicWaker; | ||||||
| 
 | 
 | ||||||
| use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; | use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; | ||||||
| use crate::_generated::DMA_CHANNEL_COUNT; | use crate::_generated::DMA_CHANNEL_COUNT; | ||||||
| use crate::interrupt::{Interrupt, InterruptExt}; | use crate::interrupt::{Interrupt, InterruptExt}; | ||||||
| use crate::pac::dma::{regs, vals}; | use crate::pac::dma::{regs, vals}; | ||||||
| @ -40,6 +40,17 @@ impl From<FlowControl> for vals::Pfctrl { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl From<FifoThreshold> for vals::Fth { | ||||||
|  |     fn from(value: FifoThreshold) -> Self { | ||||||
|  |         match value { | ||||||
|  |             FifoThreshold::Quarter => vals::Fth::QUARTER, | ||||||
|  |             FifoThreshold::Half => vals::Fth::HALF, | ||||||
|  |             FifoThreshold::ThreeQuarters => vals::Fth::THREEQUARTERS, | ||||||
|  |             FifoThreshold::Full => vals::Fth::FULL, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct ChannelState { | struct ChannelState { | ||||||
|     waker: AtomicWaker, |     waker: AtomicWaker, | ||||||
| } | } | ||||||
| @ -236,6 +247,16 @@ mod low_level_api { | |||||||
|         ch.par().write_value(peri_addr as u32); |         ch.par().write_value(peri_addr as u32); | ||||||
|         ch.m0ar().write_value(mem_addr as u32); |         ch.m0ar().write_value(mem_addr as u32); | ||||||
|         ch.ndtr().write_value(regs::Ndtr(mem_len as _)); |         ch.ndtr().write_value(regs::Ndtr(mem_len as _)); | ||||||
|  |         ch.fcr().write(|w| { | ||||||
|  |             if let Some(fth) = options.fifo_threshold { | ||||||
|  |                 // FIFO mode
 | ||||||
|  |                 w.set_dmdis(vals::Dmdis::DISABLED); | ||||||
|  |                 w.set_fth(fth.into()); | ||||||
|  |             } else { | ||||||
|  |                 // Direct mode
 | ||||||
|  |                 w.set_dmdis(vals::Dmdis::ENABLED); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|         ch.cr().write(|w| { |         ch.cr().write(|w| { | ||||||
|             w.set_dir(dir); |             w.set_dir(dir); | ||||||
|             w.set_msize(data_size); |             w.set_msize(data_size); | ||||||
|  | |||||||
| @ -176,8 +176,16 @@ mod low_level_api { | |||||||
|         mem_len: usize, |         mem_len: usize, | ||||||
|         incr_mem: bool, |         incr_mem: bool, | ||||||
|         data_size: WordSize, |         data_size: WordSize, | ||||||
|         _options: TransferOptions, |         options: TransferOptions, | ||||||
|     ) { |     ) { | ||||||
|  |         assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); | ||||||
|  |         assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); | ||||||
|  |         assert!( | ||||||
|  |             options.flow_ctrl == crate::dma::FlowControl::Dma, | ||||||
|  |             "Peripheral flow control not supported" | ||||||
|  |         ); | ||||||
|  |         assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); | ||||||
|  | 
 | ||||||
|         // "Preceding reads and writes cannot be moved past subsequent writes."
 |         // "Preceding reads and writes cannot be moved past subsequent writes."
 | ||||||
|         fence(Ordering::SeqCst); |         fence(Ordering::SeqCst); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -186,6 +186,19 @@ pub enum FlowControl { | |||||||
|     Peripheral, |     Peripheral, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub enum FifoThreshold { | ||||||
|  |     /// 1/4 full FIFO
 | ||||||
|  |     Quarter, | ||||||
|  |     /// 1/2 full FIFO
 | ||||||
|  |     Half, | ||||||
|  |     /// 3/4 full FIFO
 | ||||||
|  |     ThreeQuarters, | ||||||
|  |     /// Full FIFO
 | ||||||
|  |     Full, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
| pub struct TransferOptions { | pub struct TransferOptions { | ||||||
| @ -195,6 +208,8 @@ pub struct TransferOptions { | |||||||
|     pub mburst: Burst, |     pub mburst: Burst, | ||||||
|     /// Flow control configuration
 |     /// Flow control configuration
 | ||||||
|     pub flow_ctrl: FlowControl, |     pub flow_ctrl: FlowControl, | ||||||
|  |     /// FIFO threshold for DMA FIFO mode. If none, direct mode is used.
 | ||||||
|  |     pub fifo_threshold: Option<FifoThreshold>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for TransferOptions { | impl Default for TransferOptions { | ||||||
| @ -203,6 +218,7 @@ impl Default for TransferOptions { | |||||||
|             pburst: Burst::Single, |             pburst: Burst::Single, | ||||||
|             mburst: Burst::Single, |             mburst: Burst::Single, | ||||||
|             flow_ctrl: FlowControl::Dma, |             flow_ctrl: FlowControl::Dma, | ||||||
|  |             fifo_threshold: None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| use core::default::Default; | use core::default::Default; | ||||||
| use core::future::poll_fn; | use core::future::poll_fn; | ||||||
|  | use core::ops::{Deref, DerefMut}; | ||||||
| use core::task::Poll; | use core::task::Poll; | ||||||
| 
 | 
 | ||||||
| use embassy_hal_common::drop::OnDrop; | use embassy_hal_common::drop::OnDrop; | ||||||
| @ -40,7 +41,23 @@ impl Default for Signalling { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[repr(align(4))] | #[repr(align(4))] | ||||||
| pub struct DataBlock([u8; 512]); | #[derive(Debug, Clone)] | ||||||
|  | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||||
|  | pub struct DataBlock(pub [u8; 512]); | ||||||
|  | 
 | ||||||
|  | impl Deref for DataBlock { | ||||||
|  |     type Target = [u8; 512]; | ||||||
|  | 
 | ||||||
|  |     fn deref(&self) -> &Self::Target { | ||||||
|  |         &self.0 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DerefMut for DataBlock { | ||||||
|  |     fn deref_mut(&mut self) -> &mut Self::Target { | ||||||
|  |         &mut self.0 | ||||||
|  |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /// Errors
 | /// Errors
 | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
| @ -570,6 +587,12 @@ impl SdmmcInner { | |||||||
|         regs.clkcr().write(|w| { |         regs.clkcr().write(|w| { | ||||||
|             w.set_pwrsav(false); |             w.set_pwrsav(false); | ||||||
|             w.set_negedge(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); |             w.set_hwfc_en(true); | ||||||
| 
 | 
 | ||||||
|             #[cfg(sdmmc_v1)] |             #[cfg(sdmmc_v1)] | ||||||
| @ -807,10 +830,16 @@ impl SdmmcInner { | |||||||
|         let regs = self.0; |         let regs = self.0; | ||||||
|         let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); |         let on_drop = OnDrop::new(|| 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 { |         unsafe { | ||||||
|             self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); |             self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); | ||||||
|             self.data_interrupts(true); |             self.data_interrupts(true); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         #[cfg(sdmmc_v2)] | ||||||
|         self.cmd(Cmd::write_single_block(address), true)?; |         self.cmd(Cmd::write_single_block(address), true)?; | ||||||
| 
 | 
 | ||||||
|         let res = poll_fn(|cx| { |         let res = poll_fn(|cx| { | ||||||
| @ -922,7 +951,9 @@ impl SdmmcInner { | |||||||
|                 let request = dma.request(); |                 let request = dma.request(); | ||||||
|                 dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { |                 dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { | ||||||
|                     pburst: crate::dma::Burst::Incr4, |                     pburst: crate::dma::Burst::Incr4, | ||||||
|  |                     mburst: crate::dma::Burst::Incr4, | ||||||
|                     flow_ctrl: crate::dma::FlowControl::Peripheral, |                     flow_ctrl: crate::dma::FlowControl::Peripheral, | ||||||
|  |                     fifo_threshold: Some(crate::dma::FifoThreshold::Full), | ||||||
|                     ..Default::default() |                     ..Default::default() | ||||||
|                 }); |                 }); | ||||||
|             } else if #[cfg(sdmmc_v2)] { |             } else if #[cfg(sdmmc_v2)] { | ||||||
| @ -970,7 +1001,9 @@ impl SdmmcInner { | |||||||
|                 let request = dma.request(); |                 let request = dma.request(); | ||||||
|                 dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { |                 dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { | ||||||
|                     pburst: crate::dma::Burst::Incr4, |                     pburst: crate::dma::Burst::Incr4, | ||||||
|  |                     mburst: crate::dma::Burst::Incr4, | ||||||
|                     flow_ctrl: crate::dma::FlowControl::Peripheral, |                     flow_ctrl: crate::dma::FlowControl::Peripheral, | ||||||
|  |                     fifo_threshold: Some(crate::dma::FifoThreshold::Full), | ||||||
|                     ..Default::default() |                     ..Default::default() | ||||||
|                 }); |                 }); | ||||||
|             } else if #[cfg(sdmmc_v2)] { |             } else if #[cfg(sdmmc_v2)] { | ||||||
| @ -982,6 +1015,11 @@ impl SdmmcInner { | |||||||
|         regs.dctrl().modify(|w| { |         regs.dctrl().modify(|w| { | ||||||
|             w.set_dblocksize(block_size); |             w.set_dblocksize(block_size); | ||||||
|             w.set_dtdir(false); |             w.set_dtdir(false); | ||||||
|  |             #[cfg(sdmmc_v1)] | ||||||
|  |             { | ||||||
|  |                 w.set_dmaen(true); | ||||||
|  |                 w.set_dten(true); | ||||||
|  |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,11 +4,15 @@ | |||||||
| 
 | 
 | ||||||
| use defmt::*; | use defmt::*; | ||||||
| use embassy_executor::Spawner; | use embassy_executor::Spawner; | ||||||
| use embassy_stm32::sdmmc::Sdmmc; | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | ||||||
| use embassy_stm32::time::mhz; | use embassy_stm32::time::mhz; | ||||||
| use embassy_stm32::{interrupt, Config}; | use embassy_stm32::{interrupt, Config}; | ||||||
| use {defmt_rtt as _, panic_probe as _}; | use {defmt_rtt as _, panic_probe as _}; | ||||||
| 
 | 
 | ||||||
|  | /// This is a safeguard to not overwrite any data on the SD card.
 | ||||||
|  | /// If you don't care about SD card contents, set this to `true` to test writes.
 | ||||||
|  | const ALLOW_WRITES: bool = false; | ||||||
|  | 
 | ||||||
| #[embassy_executor::main] | #[embassy_executor::main] | ||||||
| async fn main(_spawner: Spawner) -> ! { | async fn main(_spawner: Spawner) -> ! { | ||||||
|     let mut config = Config::default(); |     let mut config = Config::default(); | ||||||
| @ -34,11 +38,42 @@ async fn main(_spawner: Spawner) -> ! { | |||||||
|     // Should print 400kHz for initialization
 |     // Should print 400kHz for initialization
 | ||||||
|     info!("Configured clock: {}", sdmmc.clock().0); |     info!("Configured clock: {}", sdmmc.clock().0); | ||||||
| 
 | 
 | ||||||
|     unwrap!(sdmmc.init_card(mhz(25)).await); |     unwrap!(sdmmc.init_card(mhz(24)).await); | ||||||
| 
 | 
 | ||||||
|     let card = unwrap!(sdmmc.card()); |     let card = unwrap!(sdmmc.card()); | ||||||
| 
 | 
 | ||||||
|     info!("Card: {:#?}", Debug2Format(card)); |     info!("Card: {:#?}", Debug2Format(card)); | ||||||
|  |     info!("Clock: {}", sdmmc.clock()); | ||||||
|  | 
 | ||||||
|  |     // Arbitrary block index
 | ||||||
|  |     let block_idx = 16; | ||||||
|  | 
 | ||||||
|  |     // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware.
 | ||||||
|  |     let mut block = DataBlock([0u8; 512]); | ||||||
|  | 
 | ||||||
|  |     sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||||||
|  |     info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||||||
|  | 
 | ||||||
|  |     if !ALLOW_WRITES { | ||||||
|  |         info!("Writing is disabled."); | ||||||
|  |         loop {} | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     info!("Filling block with 0x55"); | ||||||
|  |     block.fill(0x55); | ||||||
|  |     sdmmc.write_block(block_idx, &block).await.unwrap(); | ||||||
|  |     info!("Write done"); | ||||||
|  | 
 | ||||||
|  |     sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||||||
|  |     info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||||||
|  | 
 | ||||||
|  |     info!("Filling block with 0xAA"); | ||||||
|  |     block.fill(0xAA); | ||||||
|  |     sdmmc.write_block(block_idx, &block).await.unwrap(); | ||||||
|  |     info!("Write done"); | ||||||
|  | 
 | ||||||
|  |     sdmmc.read_block(block_idx, &mut block).await.unwrap(); | ||||||
|  |     info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | ||||||
| 
 | 
 | ||||||
|     loop {} |     loop {} | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user