diff options
| author | xoviat <[email protected]> | 2025-12-11 14:03:01 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-11 14:03:01 +0000 |
| commit | f650afc33b2d6b39116f27c6545c5f2d9e3c7d06 (patch) | |
| tree | 7789d3f97012c14d8444df8acbaa52a6c9eb0fa5 | |
| parent | 32a1d0ef7ea52d3cee9959ff52d47fd13fc6b4b9 (diff) | |
| parent | 48100a2e8d15364f6243d3db0a649e5c90c9ffe7 (diff) | |
Merge pull request #5032 from xoviat/sdio
sdmmc: factor out sd and impl. sdio
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 823 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/sd.rs | 693 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/sdio.rs | 177 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 5 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/sdmmc.rs | 3 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/sdmmc.rs | 3 | ||||
| -rw-r--r-- | tests/stm32/src/bin/sdmmc.rs | 11 |
7 files changed, 993 insertions, 722 deletions
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 0d5260016..12086cd3a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -4,18 +4,15 @@ | |||
| 4 | use core::default::Default; | 4 | use core::default::Default; |
| 5 | use core::future::poll_fn; | 5 | use core::future::poll_fn; |
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::ops::{Deref, DerefMut}; | ||
| 8 | use core::slice; | 7 | use core::slice; |
| 9 | use core::task::Poll; | 8 | use core::task::Poll; |
| 10 | 9 | ||
| 11 | use embassy_hal_internal::drop::OnDrop; | ||
| 12 | use embassy_hal_internal::{Peri, PeripheralType}; | 10 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 11 | use embassy_sync::waitqueue::AtomicWaker; |
| 12 | use sdio_host::Cmd; | ||
| 14 | use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; | 13 | use sdio_host::common_cmd::{self, R1, R2, R3, Resp, ResponseLen, Rz}; |
| 15 | use sdio_host::emmc::{EMMC, ExtCSD}; | 14 | use sdio_host::sd::{BusWidth, CardStatus}; |
| 16 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 17 | use sdio_host::sd_cmd::{R6, R7}; | 15 | use sdio_host::sd_cmd::{R6, R7}; |
| 18 | use sdio_host::{Cmd, emmc_cmd, sd_cmd}; | ||
| 19 | 16 | ||
| 20 | #[cfg(sdmmc_v1)] | 17 | #[cfg(sdmmc_v1)] |
| 21 | use crate::dma::ChannelAndRequest; | 18 | use crate::dma::ChannelAndRequest; |
| @@ -25,9 +22,16 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | |||
| 25 | use crate::interrupt::typelevel::Interrupt; | 22 | use crate::interrupt::typelevel::Interrupt; |
| 26 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 23 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 27 | use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; | 24 | use crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral}; |
| 28 | use crate::time::{Hertz, mhz}; | 25 | use crate::sdmmc::sd::Addressable; |
| 26 | use crate::time::Hertz; | ||
| 29 | use crate::{interrupt, peripherals}; | 27 | use crate::{interrupt, peripherals}; |
| 30 | 28 | ||
| 29 | /// Module for SD and EMMC cards | ||
| 30 | pub mod sd; | ||
| 31 | |||
| 32 | /// Module for SDIO interface | ||
| 33 | pub mod sdio; | ||
| 34 | |||
| 31 | /// Interrupt handler. | 35 | /// Interrupt handler. |
| 32 | pub struct InterruptHandler<T: Instance> { | 36 | pub struct InterruptHandler<T: Instance> { |
| 33 | _phantom: PhantomData<T>, | 37 | _phantom: PhantomData<T>, |
| @@ -135,59 +139,14 @@ impl Default for Signalling { | |||
| 135 | } | 139 | } |
| 136 | } | 140 | } |
| 137 | 141 | ||
| 138 | /// Aligned data block for SDMMC transfers. | 142 | const fn slice8_mut(x: &mut [u32]) -> &mut [u8] { |
| 139 | /// | ||
| 140 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 141 | #[repr(align(4))] | ||
| 142 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 143 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 144 | pub struct DataBlock(pub [u8; 512]); | ||
| 145 | |||
| 146 | impl Deref for DataBlock { | ||
| 147 | type Target = [u8; 512]; | ||
| 148 | |||
| 149 | fn deref(&self) -> &Self::Target { | ||
| 150 | &self.0 | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | impl DerefMut for DataBlock { | ||
| 155 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 156 | &mut self.0 | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | ||
| 161 | let len = x.len() * 4; | 143 | let len = x.len() * 4; |
| 162 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } | 144 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } |
| 163 | } | 145 | } |
| 164 | 146 | ||
| 165 | /// Command Block buffer for SDMMC command transfers. | 147 | const fn slice8_ref(x: &[u32]) -> &[u8] { |
| 166 | /// | 148 | let len = x.len() * 4; |
| 167 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | 149 | unsafe { slice::from_raw_parts(x.as_ptr() as _, len) } |
| 168 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 169 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 170 | pub struct CmdBlock(pub [u32; 16]); | ||
| 171 | |||
| 172 | impl CmdBlock { | ||
| 173 | /// Creates a new instance of CmdBlock | ||
| 174 | pub const fn new() -> Self { | ||
| 175 | Self([0u32; 16]) | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | impl Deref for CmdBlock { | ||
| 180 | type Target = [u32; 16]; | ||
| 181 | |||
| 182 | fn deref(&self) -> &Self::Target { | ||
| 183 | &self.0 | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | impl DerefMut for CmdBlock { | ||
| 188 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 189 | &mut self.0 | ||
| 190 | } | ||
| 191 | } | 150 | } |
| 192 | 151 | ||
| 193 | /// Errors | 152 | /// Errors |
| @@ -222,610 +181,6 @@ pub enum Error { | |||
| 222 | StBitErr, | 181 | StBitErr, |
| 223 | } | 182 | } |
| 224 | 183 | ||
| 225 | /// Represents either an SD or EMMC card | ||
| 226 | pub trait Addressable: Sized + Clone { | ||
| 227 | /// Associated type | ||
| 228 | type Ext; | ||
| 229 | |||
| 230 | /// Get this peripheral's address on the SDMMC bus | ||
| 231 | fn get_address(&self) -> u16; | ||
| 232 | |||
| 233 | /// Is this a standard or high capacity peripheral? | ||
| 234 | fn get_capacity(&self) -> CardCapacity; | ||
| 235 | |||
| 236 | /// Size in bytes | ||
| 237 | fn size(&self) -> u64; | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Storage Device | ||
| 241 | pub struct StorageDevice<'a, 'b, T: Addressable> { | ||
| 242 | info: T, | ||
| 243 | /// Inner member | ||
| 244 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 245 | } | ||
| 246 | |||
| 247 | /// Card Storage Device | ||
| 248 | impl<'a, 'b> StorageDevice<'a, 'b, Card> { | ||
| 249 | /// Create a new SD card | ||
| 250 | pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 251 | let mut s = Self { | ||
| 252 | info: Card::default(), | ||
| 253 | sdmmc, | ||
| 254 | }; | ||
| 255 | |||
| 256 | s.acquire(cmd_block, freq).await?; | ||
| 257 | |||
| 258 | Ok(s) | ||
| 259 | } | ||
| 260 | |||
| 261 | /// Initializes the card into a known state (or at least tries to). | ||
| 262 | pub async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 263 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 264 | let regs = self.sdmmc.info.regs; | ||
| 265 | |||
| 266 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 267 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 268 | bus_width => bus_width, | ||
| 269 | }; | ||
| 270 | |||
| 271 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 272 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 273 | self.sdmmc.init_idle()?; | ||
| 274 | |||
| 275 | // Check if cards supports CMD8 (with pattern) | ||
| 276 | self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 277 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 278 | |||
| 279 | if cic.pattern() != 0xAA { | ||
| 280 | return Err(Error::UnsupportedCardVersion); | ||
| 281 | } | ||
| 282 | |||
| 283 | if cic.voltage_accepted() & 1 == 0 { | ||
| 284 | return Err(Error::UnsupportedVoltage); | ||
| 285 | } | ||
| 286 | |||
| 287 | let ocr = loop { | ||
| 288 | // Signal that next command is a app command | ||
| 289 | self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 290 | |||
| 291 | // 3.2-3.3V | ||
| 292 | let voltage_window = 1 << 5; | ||
| 293 | // Initialize card | ||
| 294 | match self | ||
| 295 | .sdmmc | ||
| 296 | .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) | ||
| 297 | { | ||
| 298 | // ACMD41 | ||
| 299 | Ok(_) => (), | ||
| 300 | Err(Error::Crc) => (), | ||
| 301 | Err(err) => return Err(err), | ||
| 302 | } | ||
| 303 | |||
| 304 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 305 | if !ocr.is_busy() { | ||
| 306 | // Power up done | ||
| 307 | break ocr; | ||
| 308 | } | ||
| 309 | }; | ||
| 310 | |||
| 311 | if ocr.high_capacity() { | ||
| 312 | // Card is SDHC or SDXC or SDUC | ||
| 313 | self.info.card_type = CardCapacity::HighCapacity; | ||
| 314 | } else { | ||
| 315 | self.info.card_type = CardCapacity::StandardCapacity; | ||
| 316 | } | ||
| 317 | self.info.ocr = ocr; | ||
| 318 | |||
| 319 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 320 | |||
| 321 | self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 322 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 323 | self.info.rca = rca.address(); | ||
| 324 | |||
| 325 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 326 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 327 | |||
| 328 | self.info.scr = self.get_scr(cmd_block).await?; | ||
| 329 | |||
| 330 | let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { | ||
| 331 | (BusWidth::One, 0) | ||
| 332 | } else { | ||
| 333 | (BusWidth::Four, 2) | ||
| 334 | }; | ||
| 335 | |||
| 336 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 337 | self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 338 | |||
| 339 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 340 | |||
| 341 | // Read status | ||
| 342 | self.info.status = self.read_sd_status(cmd_block).await?; | ||
| 343 | |||
| 344 | if freq > mhz(25) { | ||
| 345 | // Switch to SDR25 | ||
| 346 | self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; | ||
| 347 | |||
| 348 | if self.sdmmc.signalling == Signalling::SDR25 { | ||
| 349 | // Set final clock frequency | ||
| 350 | self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; | ||
| 351 | |||
| 352 | if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { | ||
| 353 | return Err(Error::SignalingSwitchFailed); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | // Read status after signalling change | ||
| 358 | self.read_sd_status(cmd_block).await?; | ||
| 359 | } | ||
| 360 | |||
| 361 | Ok(()) | ||
| 362 | } | ||
| 363 | |||
| 364 | /// Switch mode using CMD6. | ||
| 365 | /// | ||
| 366 | /// Attempt to set a new signalling mode. The selected | ||
| 367 | /// signalling mode is returned. Expects the current clock | ||
| 368 | /// frequency to be > 12.5MHz. | ||
| 369 | /// | ||
| 370 | /// SD only. | ||
| 371 | async fn switch_signalling_mode( | ||
| 372 | &self, | ||
| 373 | cmd_block: &mut CmdBlock, | ||
| 374 | signalling: Signalling, | ||
| 375 | ) -> Result<Signalling, Error> { | ||
| 376 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 377 | // necessary" | ||
| 378 | |||
| 379 | let set_function = 0x8000_0000 | ||
| 380 | | match signalling { | ||
| 381 | // See PLSS v7_10 Table 4-11 | ||
| 382 | Signalling::DDR50 => 0xFF_FF04, | ||
| 383 | Signalling::SDR104 => 0xFF_1F03, | ||
| 384 | Signalling::SDR50 => 0xFF_1F02, | ||
| 385 | Signalling::SDR25 => 0xFF_FF01, | ||
| 386 | Signalling::SDR12 => 0xFF_FF00, | ||
| 387 | }; | ||
| 388 | |||
| 389 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 390 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 391 | |||
| 392 | let transfer = self.sdmmc.prepare_datapath_read(cmd_block.as_mut(), 64, 6); | ||
| 393 | self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 394 | |||
| 395 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 396 | |||
| 397 | // Host is allowed to use the new functions at least 8 | ||
| 398 | // clocks after the end of the switch command | ||
| 399 | // transaction. We know the current clock period is < 80ns, | ||
| 400 | // so a total delay of 640ns is required here | ||
| 401 | for _ in 0..300 { | ||
| 402 | cortex_m::asm::nop(); | ||
| 403 | } | ||
| 404 | |||
| 405 | on_drop.defuse(); | ||
| 406 | |||
| 407 | // Function Selection of Function Group 1 | ||
| 408 | let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; | ||
| 409 | |||
| 410 | match selection { | ||
| 411 | 0 => Ok(Signalling::SDR12), | ||
| 412 | 1 => Ok(Signalling::SDR25), | ||
| 413 | 2 => Ok(Signalling::SDR50), | ||
| 414 | 3 => Ok(Signalling::SDR104), | ||
| 415 | 4 => Ok(Signalling::DDR50), | ||
| 416 | _ => Err(Error::UnsupportedCardType), | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | /// Reads the SCR register. | ||
| 421 | /// | ||
| 422 | /// SD only. | ||
| 423 | async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> { | ||
| 424 | // Read the 64-bit SCR register | ||
| 425 | self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 426 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 427 | |||
| 428 | let scr = &mut cmd_block.0[..2]; | ||
| 429 | |||
| 430 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 431 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 432 | |||
| 433 | let transfer = self.sdmmc.prepare_datapath_read(scr, 8, 3); | ||
| 434 | self.sdmmc.cmd(sd_cmd::send_scr(), true)?; | ||
| 435 | |||
| 436 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 437 | |||
| 438 | on_drop.defuse(); | ||
| 439 | |||
| 440 | Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) | ||
| 441 | } | ||
| 442 | |||
| 443 | /// Reads the SD Status (ACMD13) | ||
| 444 | /// | ||
| 445 | /// SD only. | ||
| 446 | async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> { | ||
| 447 | let rca = self.info.rca; | ||
| 448 | |||
| 449 | self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 450 | self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 451 | |||
| 452 | let status = cmd_block; | ||
| 453 | |||
| 454 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 455 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 456 | |||
| 457 | let transfer = self.sdmmc.prepare_datapath_read(status.as_mut(), 64, 6); | ||
| 458 | self.sdmmc.cmd(sd_cmd::sd_status(), true)?; | ||
| 459 | |||
| 460 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 461 | |||
| 462 | on_drop.defuse(); | ||
| 463 | |||
| 464 | for byte in status.iter_mut() { | ||
| 465 | *byte = u32::from_be(*byte); | ||
| 466 | } | ||
| 467 | |||
| 468 | Ok(status.0.into()) | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | /// Emmc storage device | ||
| 473 | impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { | ||
| 474 | /// Create a new EMMC card | ||
| 475 | pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 476 | let mut s = Self { | ||
| 477 | info: Emmc::default(), | ||
| 478 | sdmmc, | ||
| 479 | }; | ||
| 480 | |||
| 481 | s.acquire(cmd_block, freq).await?; | ||
| 482 | |||
| 483 | Ok(s) | ||
| 484 | } | ||
| 485 | |||
| 486 | async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 487 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 488 | let regs = self.sdmmc.info.regs; | ||
| 489 | |||
| 490 | let bus_width = self.sdmmc.bus_width(); | ||
| 491 | |||
| 492 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 493 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 494 | self.sdmmc.init_idle()?; | ||
| 495 | |||
| 496 | let ocr = loop { | ||
| 497 | let high_voltage = 0b0 << 7; | ||
| 498 | let access_mode = 0b10 << 29; | ||
| 499 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 500 | // Initialize card | ||
| 501 | match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 502 | Ok(_) => (), | ||
| 503 | Err(Error::Crc) => (), | ||
| 504 | Err(err) => return Err(err), | ||
| 505 | } | ||
| 506 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 507 | if !ocr.is_busy() { | ||
| 508 | // Power up done | ||
| 509 | break ocr; | ||
| 510 | } | ||
| 511 | }; | ||
| 512 | |||
| 513 | self.info.capacity = if ocr.access_mode() == 0b10 { | ||
| 514 | // Card is SDHC or SDXC or SDUC | ||
| 515 | CardCapacity::HighCapacity | ||
| 516 | } else { | ||
| 517 | CardCapacity::StandardCapacity | ||
| 518 | }; | ||
| 519 | self.info.ocr = ocr; | ||
| 520 | |||
| 521 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 522 | |||
| 523 | self.info.rca = 1u16.into(); | ||
| 524 | self.sdmmc | ||
| 525 | .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; | ||
| 526 | |||
| 527 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 528 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 529 | |||
| 530 | let (widbus, _) = bus_width_vals(bus_width); | ||
| 531 | |||
| 532 | // Write bus width to ExtCSD byte 183 | ||
| 533 | self.sdmmc.cmd( | ||
| 534 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 535 | false, | ||
| 536 | )?; | ||
| 537 | |||
| 538 | // Wait for ready after R1b response | ||
| 539 | loop { | ||
| 540 | let status = self.sdmmc.read_status(&self.info)?; | ||
| 541 | |||
| 542 | if status.ready_for_data() { | ||
| 543 | break; | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 548 | self.info.ext_csd = self.read_ext_csd().await?; | ||
| 549 | |||
| 550 | Ok(()) | ||
| 551 | } | ||
| 552 | |||
| 553 | /// Gets the EXT_CSD register. | ||
| 554 | /// | ||
| 555 | /// eMMC only. | ||
| 556 | async fn read_ext_csd(&self) -> Result<ExtCSD, Error> { | ||
| 557 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 558 | let mut data_block = DataBlock([0u8; 512]); | ||
| 559 | |||
| 560 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 561 | let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 562 | |||
| 563 | self.sdmmc.cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 | ||
| 564 | |||
| 565 | let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); | ||
| 566 | self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 567 | |||
| 568 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 569 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 570 | |||
| 571 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 572 | |||
| 573 | on_drop.defuse(); | ||
| 574 | |||
| 575 | Ok(unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into()) | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | /// Card or Emmc storage device | ||
| 580 | impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { | ||
| 581 | /// Write a block | ||
| 582 | pub fn card(&self) -> A { | ||
| 583 | self.info.clone() | ||
| 584 | } | ||
| 585 | |||
| 586 | /// Read a data block. | ||
| 587 | #[inline] | ||
| 588 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 589 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 590 | let card_capacity = self.info.get_capacity(); | ||
| 591 | |||
| 592 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 593 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 594 | |||
| 595 | // Always read 1 block of 512 bytes | ||
| 596 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 597 | let address = match card_capacity { | ||
| 598 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 599 | _ => block_idx, | ||
| 600 | }; | ||
| 601 | self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 602 | |||
| 603 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 604 | |||
| 605 | let transfer = self.sdmmc.prepare_datapath_read(buffer, 512, 9); | ||
| 606 | self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; | ||
| 607 | |||
| 608 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 609 | |||
| 610 | on_drop.defuse(); | ||
| 611 | |||
| 612 | Ok(()) | ||
| 613 | } | ||
| 614 | |||
| 615 | /// Read multiple data blocks. | ||
| 616 | #[inline] | ||
| 617 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 618 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 619 | let card_capacity = self.info.get_capacity(); | ||
| 620 | |||
| 621 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 622 | let buffer = unsafe { | ||
| 623 | let ptr = blocks.as_mut_ptr() as *mut u32; | ||
| 624 | let len = blocks.len() * 128; | ||
| 625 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 626 | }; | ||
| 627 | |||
| 628 | // Always read 1 block of 512 bytes | ||
| 629 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 630 | let address = match card_capacity { | ||
| 631 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 632 | _ => block_idx, | ||
| 633 | }; | ||
| 634 | self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 635 | |||
| 636 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 637 | let transfer = self.sdmmc.prepare_datapath_read(buffer, 512 * blocks.len() as u32, 9); | ||
| 638 | self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 639 | |||
| 640 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 641 | |||
| 642 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 643 | self.sdmmc.clear_interrupt_flags(); | ||
| 644 | |||
| 645 | on_drop.defuse(); | ||
| 646 | |||
| 647 | Ok(()) | ||
| 648 | } | ||
| 649 | |||
| 650 | /// Write a data block. | ||
| 651 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> | ||
| 652 | where | ||
| 653 | CardStatus<A::Ext>: From<u32>, | ||
| 654 | { | ||
| 655 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 656 | |||
| 657 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 658 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 659 | |||
| 660 | // Always read 1 block of 512 bytes | ||
| 661 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 662 | let address = match self.info.get_capacity() { | ||
| 663 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 664 | _ => block_idx, | ||
| 665 | }; | ||
| 666 | self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 667 | |||
| 668 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 669 | |||
| 670 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 671 | #[cfg(sdmmc_v1)] | ||
| 672 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 673 | |||
| 674 | let transfer = self.sdmmc.prepare_datapath_write(buffer, 512, 9); | ||
| 675 | |||
| 676 | #[cfg(sdmmc_v2)] | ||
| 677 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 678 | |||
| 679 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 680 | |||
| 681 | on_drop.defuse(); | ||
| 682 | |||
| 683 | // TODO: Make this configurable | ||
| 684 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 685 | |||
| 686 | while timeout > 0 { | ||
| 687 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 688 | if ready_for_data { | ||
| 689 | return Ok(()); | ||
| 690 | } | ||
| 691 | timeout -= 1; | ||
| 692 | } | ||
| 693 | |||
| 694 | Err(Error::SoftwareTimeout) | ||
| 695 | } | ||
| 696 | |||
| 697 | /// Write multiple data blocks. | ||
| 698 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> | ||
| 699 | where | ||
| 700 | CardStatus<A::Ext>: From<u32>, | ||
| 701 | { | ||
| 702 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 703 | |||
| 704 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 705 | let buffer = unsafe { | ||
| 706 | let ptr = blocks.as_ptr() as *const u32; | ||
| 707 | let len = blocks.len() * 128; | ||
| 708 | core::slice::from_raw_parts(ptr, len) | ||
| 709 | }; | ||
| 710 | |||
| 711 | // Always read 1 block of 512 bytes | ||
| 712 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 713 | let address = match self.info.get_capacity() { | ||
| 714 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 715 | _ => block_idx, | ||
| 716 | }; | ||
| 717 | |||
| 718 | self.sdmmc.cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 719 | |||
| 720 | let block_count = blocks.len(); | ||
| 721 | |||
| 722 | let on_drop = OnDrop::new(|| self.sdmmc.on_drop()); | ||
| 723 | |||
| 724 | #[cfg(sdmmc_v1)] | ||
| 725 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 726 | |||
| 727 | // Setup write command | ||
| 728 | let transfer = self.sdmmc.prepare_datapath_write(buffer, 512 * block_count as u32, 9); | ||
| 729 | |||
| 730 | #[cfg(sdmmc_v2)] | ||
| 731 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 732 | |||
| 733 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 734 | |||
| 735 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 736 | self.sdmmc.clear_interrupt_flags(); | ||
| 737 | |||
| 738 | on_drop.defuse(); | ||
| 739 | |||
| 740 | // TODO: Make this configurable | ||
| 741 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 742 | |||
| 743 | while timeout > 0 { | ||
| 744 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 745 | |||
| 746 | if ready_for_data { | ||
| 747 | return Ok(()); | ||
| 748 | } | ||
| 749 | timeout -= 1; | ||
| 750 | } | ||
| 751 | Err(Error::SoftwareTimeout) | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | #[derive(Clone, Copy, Debug, Default)] | ||
| 756 | /// SD Card | ||
| 757 | pub struct Card { | ||
| 758 | /// The type of this card | ||
| 759 | pub card_type: CardCapacity, | ||
| 760 | /// Operation Conditions Register | ||
| 761 | pub ocr: OCR<SD>, | ||
| 762 | /// Relative Card Address | ||
| 763 | pub rca: u16, | ||
| 764 | /// Card ID | ||
| 765 | pub cid: CID<SD>, | ||
| 766 | /// Card Specific Data | ||
| 767 | pub csd: CSD<SD>, | ||
| 768 | /// SD CARD Configuration Register | ||
| 769 | pub scr: SCR, | ||
| 770 | /// SD Status | ||
| 771 | pub status: SDStatus, | ||
| 772 | } | ||
| 773 | |||
| 774 | impl Addressable for Card { | ||
| 775 | type Ext = SD; | ||
| 776 | |||
| 777 | /// Get this peripheral's address on the SDMMC bus | ||
| 778 | fn get_address(&self) -> u16 { | ||
| 779 | self.rca | ||
| 780 | } | ||
| 781 | |||
| 782 | /// Is this a standard or high capacity peripheral? | ||
| 783 | fn get_capacity(&self) -> CardCapacity { | ||
| 784 | self.card_type | ||
| 785 | } | ||
| 786 | |||
| 787 | /// Size in bytes | ||
| 788 | fn size(&self) -> u64 { | ||
| 789 | u64::from(self.csd.block_count()) * 512 | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | #[derive(Clone, Copy, Debug, Default)] | ||
| 794 | /// eMMC storage | ||
| 795 | pub struct Emmc { | ||
| 796 | /// The capacity of this card | ||
| 797 | pub capacity: CardCapacity, | ||
| 798 | /// Operation Conditions Register | ||
| 799 | pub ocr: OCR<EMMC>, | ||
| 800 | /// Relative Card Address | ||
| 801 | pub rca: u16, | ||
| 802 | /// Card ID | ||
| 803 | pub cid: CID<EMMC>, | ||
| 804 | /// Card Specific Data | ||
| 805 | pub csd: CSD<EMMC>, | ||
| 806 | /// Extended Card Specific Data | ||
| 807 | pub ext_csd: ExtCSD, | ||
| 808 | } | ||
| 809 | |||
| 810 | impl Addressable for Emmc { | ||
| 811 | type Ext = EMMC; | ||
| 812 | |||
| 813 | /// Get this peripheral's address on the SDMMC bus | ||
| 814 | fn get_address(&self) -> u16 { | ||
| 815 | self.rca | ||
| 816 | } | ||
| 817 | |||
| 818 | /// Is this a standard or high capacity peripheral? | ||
| 819 | fn get_capacity(&self) -> CardCapacity { | ||
| 820 | self.capacity | ||
| 821 | } | ||
| 822 | |||
| 823 | /// Size in bytes | ||
| 824 | fn size(&self) -> u64 { | ||
| 825 | u64::from(self.ext_csd.sector_count()) * 512 | ||
| 826 | } | ||
| 827 | } | ||
| 828 | |||
| 829 | #[repr(u8)] | 184 | #[repr(u8)] |
| 830 | enum PowerCtrl { | 185 | enum PowerCtrl { |
| 831 | Off = 0b00, | 186 | Off = 0b00, |
| @@ -877,6 +232,46 @@ fn bus_width_vals(bus_width: BusWidth) -> (u8, u32) { | |||
| 877 | } | 232 | } |
| 878 | } | 233 | } |
| 879 | 234 | ||
| 235 | #[repr(u8)] | ||
| 236 | enum BlockSize { | ||
| 237 | Size1 = 0b0000, | ||
| 238 | Size2 = 0b0001, | ||
| 239 | Size4 = 0b0010, | ||
| 240 | Size8 = 0b0011, | ||
| 241 | Size16 = 0b0100, | ||
| 242 | Size32 = 0b0101, | ||
| 243 | Size64 = 0b0110, | ||
| 244 | Size128 = 0b0111, | ||
| 245 | Size256 = 0b1000, | ||
| 246 | Size512 = 0b1001, | ||
| 247 | Size1024 = 0b1010, | ||
| 248 | Size2048 = 0b1011, | ||
| 249 | Size4096 = 0b1100, | ||
| 250 | Size8192 = 0b1101, | ||
| 251 | Size16384 = 0b1110, | ||
| 252 | } | ||
| 253 | |||
| 254 | const fn block_size(bytes: usize) -> BlockSize { | ||
| 255 | match bytes { | ||
| 256 | 1 => BlockSize::Size1, | ||
| 257 | 2 => BlockSize::Size2, | ||
| 258 | 4 => BlockSize::Size4, | ||
| 259 | 8 => BlockSize::Size8, | ||
| 260 | 16 => BlockSize::Size16, | ||
| 261 | 32 => BlockSize::Size32, | ||
| 262 | 64 => BlockSize::Size64, | ||
| 263 | 128 => BlockSize::Size128, | ||
| 264 | 256 => BlockSize::Size256, | ||
| 265 | 512 => BlockSize::Size512, | ||
| 266 | 1024 => BlockSize::Size1024, | ||
| 267 | 2048 => BlockSize::Size2048, | ||
| 268 | 4096 => BlockSize::Size4096, | ||
| 269 | 8192 => BlockSize::Size8192, | ||
| 270 | 16384 => BlockSize::Size16384, | ||
| 271 | _ => core::unreachable!(), | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 880 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 275 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 881 | /// `sdmmc_ck` in Hertz. | 276 | /// `sdmmc_ck` in Hertz. |
| 882 | /// | 277 | /// |
| @@ -904,6 +299,34 @@ struct Transfer<'a> { | |||
| 904 | _dummy: PhantomData<&'a ()>, | 299 | _dummy: PhantomData<&'a ()>, |
| 905 | } | 300 | } |
| 906 | 301 | ||
| 302 | struct WrappedTransfer<'a> { | ||
| 303 | _transfer: Transfer<'a>, | ||
| 304 | sdmmc: &'a Sdmmc<'a>, | ||
| 305 | defused: bool, | ||
| 306 | } | ||
| 307 | |||
| 308 | impl<'a> WrappedTransfer<'a> { | ||
| 309 | pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self { | ||
| 310 | Self { | ||
| 311 | _transfer, | ||
| 312 | sdmmc, | ||
| 313 | defused: false, | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | pub fn defuse(&mut self) { | ||
| 318 | self.defused = true; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | impl<'a> Drop for WrappedTransfer<'a> { | ||
| 323 | fn drop(&mut self) { | ||
| 324 | if !self.defused { | ||
| 325 | self.sdmmc.on_drop(); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 907 | #[cfg(all(sdmmc_v1, dma))] | 330 | #[cfg(all(sdmmc_v1, dma))] |
| 908 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { | 331 | const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { |
| 909 | pburst: crate::dma::Burst::Incr4, | 332 | pburst: crate::dma::Burst::Incr4, |
| @@ -1343,15 +766,19 @@ impl<'d> Sdmmc<'d> { | |||
| 1343 | /// | 766 | /// |
| 1344 | /// `buffer` must be valid for the whole transfer and word aligned | 767 | /// `buffer` must be valid for the whole transfer and word aligned |
| 1345 | #[allow(unused_variables)] | 768 | #[allow(unused_variables)] |
| 1346 | fn prepare_datapath_read<'a>(&'a self, buffer: &'a mut [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { | 769 | fn prepare_datapath_read<'a>( |
| 1347 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 770 | &'a self, |
| 771 | buffer: &'a mut [u32], | ||
| 772 | block_size: BlockSize, | ||
| 773 | byte_mode: bool, | ||
| 774 | ) -> WrappedTransfer<'a> { | ||
| 1348 | let regs = self.info.regs; | 775 | let regs = self.info.regs; |
| 1349 | 776 | ||
| 1350 | // Command AND Data state machines must be idle | 777 | // Command AND Data state machines must be idle |
| 1351 | self.wait_idle(); | 778 | self.wait_idle(); |
| 1352 | self.clear_interrupt_flags(); | 779 | self.clear_interrupt_flags(); |
| 1353 | 780 | ||
| 1354 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 781 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 1355 | 782 | ||
| 1356 | // SAFETY: No other functions use the dma | 783 | // SAFETY: No other functions use the dma |
| 1357 | #[cfg(sdmmc_v1)] | 784 | #[cfg(sdmmc_v1)] |
| @@ -1368,8 +795,12 @@ impl<'d> Sdmmc<'d> { | |||
| 1368 | } | 795 | } |
| 1369 | }; | 796 | }; |
| 1370 | 797 | ||
| 798 | #[cfg(sdmmc_v2)] | ||
| 799 | let byte_mode = byte_mode as u8; | ||
| 800 | |||
| 1371 | regs.dctrl().modify(|w| { | 801 | regs.dctrl().modify(|w| { |
| 1372 | w.set_dblocksize(block_size); | 802 | w.set_dtmode(byte_mode); |
| 803 | w.set_dblocksize(block_size as u8); | ||
| 1373 | w.set_dtdir(true); | 804 | w.set_dtdir(true); |
| 1374 | #[cfg(sdmmc_v1)] | 805 | #[cfg(sdmmc_v1)] |
| 1375 | { | 806 | { |
| @@ -1380,21 +811,25 @@ impl<'d> Sdmmc<'d> { | |||
| 1380 | 811 | ||
| 1381 | self.enable_interrupts(); | 812 | self.enable_interrupts(); |
| 1382 | 813 | ||
| 1383 | transfer | 814 | WrappedTransfer::new(transfer, &self) |
| 1384 | } | 815 | } |
| 1385 | 816 | ||
| 1386 | /// # Safety | 817 | /// # Safety |
| 1387 | /// | 818 | /// |
| 1388 | /// `buffer` must be valid for the whole transfer and word aligned | 819 | /// `buffer` must be valid for the whole transfer and word aligned |
| 1389 | fn prepare_datapath_write<'a>(&'a self, buffer: &'a [u32], length_bytes: u32, block_size: u8) -> Transfer<'a> { | 820 | fn prepare_datapath_write<'a>( |
| 1390 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 821 | &'a self, |
| 822 | buffer: &'a [u32], | ||
| 823 | block_size: BlockSize, | ||
| 824 | byte_mode: bool, | ||
| 825 | ) -> WrappedTransfer<'a> { | ||
| 1391 | let regs = self.info.regs; | 826 | let regs = self.info.regs; |
| 1392 | 827 | ||
| 1393 | // Command AND Data state machines must be idle | 828 | // Command AND Data state machines must be idle |
| 1394 | self.wait_idle(); | 829 | self.wait_idle(); |
| 1395 | self.clear_interrupt_flags(); | 830 | self.clear_interrupt_flags(); |
| 1396 | 831 | ||
| 1397 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 832 | regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32)); |
| 1398 | 833 | ||
| 1399 | // SAFETY: No other functions use the dma | 834 | // SAFETY: No other functions use the dma |
| 1400 | #[cfg(sdmmc_v1)] | 835 | #[cfg(sdmmc_v1)] |
| @@ -1411,8 +846,12 @@ impl<'d> Sdmmc<'d> { | |||
| 1411 | } | 846 | } |
| 1412 | }; | 847 | }; |
| 1413 | 848 | ||
| 849 | #[cfg(sdmmc_v2)] | ||
| 850 | let byte_mode = byte_mode as u8; | ||
| 851 | |||
| 1414 | regs.dctrl().modify(|w| { | 852 | regs.dctrl().modify(|w| { |
| 1415 | w.set_dblocksize(block_size); | 853 | w.set_dtmode(byte_mode); |
| 854 | w.set_dblocksize(block_size as u8); | ||
| 1416 | w.set_dtdir(false); | 855 | w.set_dtdir(false); |
| 1417 | #[cfg(sdmmc_v1)] | 856 | #[cfg(sdmmc_v1)] |
| 1418 | { | 857 | { |
| @@ -1423,7 +862,7 @@ impl<'d> Sdmmc<'d> { | |||
| 1423 | 862 | ||
| 1424 | self.enable_interrupts(); | 863 | self.enable_interrupts(); |
| 1425 | 864 | ||
| 1426 | transfer | 865 | WrappedTransfer::new(transfer, &self) |
| 1427 | } | 866 | } |
| 1428 | 867 | ||
| 1429 | /// Stops the DMA datapath | 868 | /// Stops the DMA datapath |
| @@ -1649,7 +1088,7 @@ impl<'d> Sdmmc<'d> { | |||
| 1649 | /// Wait for a previously started datapath transfer to complete from an interrupt. | 1088 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1650 | #[inline] | 1089 | #[inline] |
| 1651 | #[allow(unused)] | 1090 | #[allow(unused)] |
| 1652 | async fn complete_datapath_transfer(&self, transfer: Transfer<'_>, block: bool) -> Result<(), Error> { | 1091 | async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> { |
| 1653 | let res = poll_fn(|cx| { | 1092 | let res = poll_fn(|cx| { |
| 1654 | // Compiler might not be sufficiently constrained here | 1093 | // Compiler might not be sufficiently constrained here |
| 1655 | // https://github.com/embassy-rs/embassy/issues/4723 | 1094 | // https://github.com/embassy-rs/embassy/issues/4723 |
| @@ -1686,6 +1125,7 @@ impl<'d> Sdmmc<'d> { | |||
| 1686 | self.clear_interrupt_flags(); | 1125 | self.clear_interrupt_flags(); |
| 1687 | self.stop_datapath(); | 1126 | self.stop_datapath(); |
| 1688 | 1127 | ||
| 1128 | transfer.defuse(); | ||
| 1689 | drop(transfer); | 1129 | drop(transfer); |
| 1690 | 1130 | ||
| 1691 | res | 1131 | res |
| @@ -1800,46 +1240,3 @@ foreach_peripheral!( | |||
| 1800 | } | 1240 | } |
| 1801 | }; | 1241 | }; |
| 1802 | ); | 1242 | ); |
| 1803 | |||
| 1804 | impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { | ||
| 1805 | type Error = Error; | ||
| 1806 | type Align = aligned::A4; | ||
| 1807 | |||
| 1808 | async fn read( | ||
| 1809 | &mut self, | ||
| 1810 | block_address: u32, | ||
| 1811 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1812 | ) -> Result<(), Self::Error> { | ||
| 1813 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1814 | if buf.len() == 1 { | ||
| 1815 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; | ||
| 1816 | self.read_block(block_address, block).await?; | ||
| 1817 | } else { | ||
| 1818 | let blocks: &mut [DataBlock] = | ||
| 1819 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 1820 | self.read_blocks(block_address, blocks).await?; | ||
| 1821 | } | ||
| 1822 | Ok(()) | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | async fn write( | ||
| 1826 | &mut self, | ||
| 1827 | block_address: u32, | ||
| 1828 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1829 | ) -> Result<(), Self::Error> { | ||
| 1830 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1831 | if buf.len() == 1 { | ||
| 1832 | let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; | ||
| 1833 | self.write_block(block_address, block).await?; | ||
| 1834 | } else { | ||
| 1835 | let blocks: &[DataBlock] = | ||
| 1836 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 1837 | self.write_blocks(block_address, blocks).await?; | ||
| 1838 | } | ||
| 1839 | Ok(()) | ||
| 1840 | } | ||
| 1841 | |||
| 1842 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 1843 | Ok(self.info.size()) | ||
| 1844 | } | ||
| 1845 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs new file mode 100644 index 000000000..6190226b8 --- /dev/null +++ b/embassy-stm32/src/sdmmc/sd.rs | |||
| @@ -0,0 +1,693 @@ | |||
| 1 | use core::default::Default; | ||
| 2 | use core::ops::{Deref, DerefMut}; | ||
| 3 | |||
| 4 | use sdio_host::emmc::{EMMC, ExtCSD}; | ||
| 5 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 6 | use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; | ||
| 7 | |||
| 8 | use crate::sdmmc::{BlockSize, Error, Sdmmc, Signalling, block_size, bus_width_vals, slice8_mut, slice8_ref}; | ||
| 9 | use crate::time::{Hertz, mhz}; | ||
| 10 | |||
| 11 | /// Aligned data block for SDMMC transfers. | ||
| 12 | /// | ||
| 13 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 14 | #[repr(align(4))] | ||
| 15 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub struct DataBlock(pub [u32; 128]); | ||
| 18 | |||
| 19 | impl DataBlock { | ||
| 20 | /// Create a new DataBlock | ||
| 21 | pub const fn new() -> Self { | ||
| 22 | DataBlock([0u32; 128]) | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | impl Deref for DataBlock { | ||
| 27 | type Target = [u8; 512]; | ||
| 28 | |||
| 29 | fn deref(&self) -> &Self::Target { | ||
| 30 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl DerefMut for DataBlock { | ||
| 35 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 36 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Command Block buffer for SDMMC command transfers. | ||
| 41 | /// | ||
| 42 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | ||
| 43 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 44 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 45 | pub struct CmdBlock(pub [u32; 16]); | ||
| 46 | |||
| 47 | impl CmdBlock { | ||
| 48 | /// Creates a new instance of CmdBlock | ||
| 49 | pub const fn new() -> Self { | ||
| 50 | Self([0u32; 16]) | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | impl Deref for CmdBlock { | ||
| 55 | type Target = [u32; 16]; | ||
| 56 | |||
| 57 | fn deref(&self) -> &Self::Target { | ||
| 58 | &self.0 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl DerefMut for CmdBlock { | ||
| 63 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 64 | &mut self.0 | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Represents either an SD or EMMC card | ||
| 69 | pub trait Addressable: Sized + Clone { | ||
| 70 | /// Associated type | ||
| 71 | type Ext; | ||
| 72 | |||
| 73 | /// Get this peripheral's address on the SDMMC bus | ||
| 74 | fn get_address(&self) -> u16; | ||
| 75 | |||
| 76 | /// Is this a standard or high capacity peripheral? | ||
| 77 | fn get_capacity(&self) -> CardCapacity; | ||
| 78 | |||
| 79 | /// Size in bytes | ||
| 80 | fn size(&self) -> u64; | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Storage Device | ||
| 84 | pub struct StorageDevice<'a, 'b, T: Addressable> { | ||
| 85 | info: T, | ||
| 86 | /// Inner member | ||
| 87 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Card Storage Device | ||
| 91 | impl<'a, 'b> StorageDevice<'a, 'b, Card> { | ||
| 92 | /// Create a new SD card | ||
| 93 | pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 94 | let mut s = Self { | ||
| 95 | info: Card::default(), | ||
| 96 | sdmmc, | ||
| 97 | }; | ||
| 98 | |||
| 99 | s.acquire(cmd_block, freq).await?; | ||
| 100 | |||
| 101 | Ok(s) | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Initializes the card into a known state (or at least tries to). | ||
| 105 | async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 106 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 107 | let regs = self.sdmmc.info.regs; | ||
| 108 | |||
| 109 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 110 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 111 | bus_width => bus_width, | ||
| 112 | }; | ||
| 113 | |||
| 114 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 115 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 116 | self.sdmmc.init_idle()?; | ||
| 117 | |||
| 118 | // Check if cards supports CMD8 (with pattern) | ||
| 119 | self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 120 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 121 | |||
| 122 | if cic.pattern() != 0xAA { | ||
| 123 | return Err(Error::UnsupportedCardVersion); | ||
| 124 | } | ||
| 125 | |||
| 126 | if cic.voltage_accepted() & 1 == 0 { | ||
| 127 | return Err(Error::UnsupportedVoltage); | ||
| 128 | } | ||
| 129 | |||
| 130 | let ocr = loop { | ||
| 131 | // Signal that next command is a app command | ||
| 132 | self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 133 | |||
| 134 | // 3.2-3.3V | ||
| 135 | let voltage_window = 1 << 5; | ||
| 136 | // Initialize card | ||
| 137 | match self | ||
| 138 | .sdmmc | ||
| 139 | .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) | ||
| 140 | { | ||
| 141 | // ACMD41 | ||
| 142 | Ok(_) => (), | ||
| 143 | Err(Error::Crc) => (), | ||
| 144 | Err(err) => return Err(err), | ||
| 145 | } | ||
| 146 | |||
| 147 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 148 | if !ocr.is_busy() { | ||
| 149 | // Power up done | ||
| 150 | break ocr; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | |||
| 154 | if ocr.high_capacity() { | ||
| 155 | // Card is SDHC or SDXC or SDUC | ||
| 156 | self.info.card_type = CardCapacity::HighCapacity; | ||
| 157 | } else { | ||
| 158 | self.info.card_type = CardCapacity::StandardCapacity; | ||
| 159 | } | ||
| 160 | self.info.ocr = ocr; | ||
| 161 | |||
| 162 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 163 | |||
| 164 | self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 165 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 166 | self.info.rca = rca.address(); | ||
| 167 | |||
| 168 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 169 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 170 | |||
| 171 | self.info.scr = self.get_scr(cmd_block).await?; | ||
| 172 | |||
| 173 | let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { | ||
| 174 | (BusWidth::One, 0) | ||
| 175 | } else { | ||
| 176 | (BusWidth::Four, 2) | ||
| 177 | }; | ||
| 178 | |||
| 179 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 180 | self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 181 | |||
| 182 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 183 | |||
| 184 | // Read status | ||
| 185 | self.info.status = self.read_sd_status(cmd_block).await?; | ||
| 186 | |||
| 187 | if freq > mhz(25) { | ||
| 188 | // Switch to SDR25 | ||
| 189 | self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; | ||
| 190 | |||
| 191 | if self.sdmmc.signalling == Signalling::SDR25 { | ||
| 192 | // Set final clock frequency | ||
| 193 | self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; | ||
| 194 | |||
| 195 | if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { | ||
| 196 | return Err(Error::SignalingSwitchFailed); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | // Read status after signalling change | ||
| 201 | self.read_sd_status(cmd_block).await?; | ||
| 202 | } | ||
| 203 | |||
| 204 | Ok(()) | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Switch mode using CMD6. | ||
| 208 | /// | ||
| 209 | /// Attempt to set a new signalling mode. The selected | ||
| 210 | /// signalling mode is returned. Expects the current clock | ||
| 211 | /// frequency to be > 12.5MHz. | ||
| 212 | /// | ||
| 213 | /// SD only. | ||
| 214 | async fn switch_signalling_mode( | ||
| 215 | &self, | ||
| 216 | cmd_block: &mut CmdBlock, | ||
| 217 | signalling: Signalling, | ||
| 218 | ) -> Result<Signalling, Error> { | ||
| 219 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 220 | // necessary" | ||
| 221 | |||
| 222 | let set_function = 0x8000_0000 | ||
| 223 | | match signalling { | ||
| 224 | // See PLSS v7_10 Table 4-11 | ||
| 225 | Signalling::DDR50 => 0xFF_FF04, | ||
| 226 | Signalling::SDR104 => 0xFF_1F03, | ||
| 227 | Signalling::SDR50 => 0xFF_1F02, | ||
| 228 | Signalling::SDR25 => 0xFF_FF01, | ||
| 229 | Signalling::SDR12 => 0xFF_FF00, | ||
| 230 | }; | ||
| 231 | |||
| 232 | let buffer = &mut cmd_block.0[..64 / 4]; | ||
| 233 | |||
| 234 | let transfer = self | ||
| 235 | .sdmmc | ||
| 236 | .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); | ||
| 237 | |||
| 238 | self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 239 | |||
| 240 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 241 | |||
| 242 | // Host is allowed to use the new functions at least 8 | ||
| 243 | // clocks after the end of the switch command | ||
| 244 | // transaction. We know the current clock period is < 80ns, | ||
| 245 | // so a total delay of 640ns is required here | ||
| 246 | for _ in 0..300 { | ||
| 247 | cortex_m::asm::nop(); | ||
| 248 | } | ||
| 249 | |||
| 250 | // Function Selection of Function Group 1 | ||
| 251 | let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; | ||
| 252 | |||
| 253 | match selection { | ||
| 254 | 0 => Ok(Signalling::SDR12), | ||
| 255 | 1 => Ok(Signalling::SDR25), | ||
| 256 | 2 => Ok(Signalling::SDR50), | ||
| 257 | 3 => Ok(Signalling::SDR104), | ||
| 258 | 4 => Ok(Signalling::DDR50), | ||
| 259 | _ => Err(Error::UnsupportedCardType), | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Reads the SCR register. | ||
| 264 | /// | ||
| 265 | /// SD only. | ||
| 266 | async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> { | ||
| 267 | // Read the 64-bit SCR register | ||
| 268 | self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 269 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 270 | |||
| 271 | let scr = &mut cmd_block.0[..2]; | ||
| 272 | |||
| 273 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 274 | |||
| 275 | let transfer = self.sdmmc.prepare_datapath_read(scr, BlockSize::Size8, false); | ||
| 276 | self.sdmmc.cmd(sd_cmd::send_scr(), true)?; | ||
| 277 | |||
| 278 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 279 | |||
| 280 | Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Reads the SD Status (ACMD13) | ||
| 284 | /// | ||
| 285 | /// SD only. | ||
| 286 | async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> { | ||
| 287 | let rca = self.info.rca; | ||
| 288 | |||
| 289 | self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 290 | self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 291 | |||
| 292 | let buffer = &mut cmd_block.as_mut()[..64 / 4]; | ||
| 293 | |||
| 294 | let transfer = self | ||
| 295 | .sdmmc | ||
| 296 | .prepare_datapath_read(buffer, block_size(size_of_val(buffer)), false); | ||
| 297 | self.sdmmc.cmd(sd_cmd::sd_status(), true)?; | ||
| 298 | |||
| 299 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 300 | |||
| 301 | for byte in cmd_block.iter_mut() { | ||
| 302 | *byte = u32::from_be(*byte); | ||
| 303 | } | ||
| 304 | |||
| 305 | Ok(cmd_block.0.into()) | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Emmc storage device | ||
| 310 | impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { | ||
| 311 | /// Create a new EMMC card | ||
| 312 | pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 313 | let mut s = Self { | ||
| 314 | info: Emmc::default(), | ||
| 315 | sdmmc, | ||
| 316 | }; | ||
| 317 | |||
| 318 | s.acquire(cmd_block, freq).await?; | ||
| 319 | |||
| 320 | Ok(s) | ||
| 321 | } | ||
| 322 | |||
| 323 | async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 324 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 325 | let regs = self.sdmmc.info.regs; | ||
| 326 | |||
| 327 | let bus_width = self.sdmmc.bus_width(); | ||
| 328 | |||
| 329 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 330 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 331 | self.sdmmc.init_idle()?; | ||
| 332 | |||
| 333 | let ocr = loop { | ||
| 334 | let high_voltage = 0b0 << 7; | ||
| 335 | let access_mode = 0b10 << 29; | ||
| 336 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 337 | // Initialize card | ||
| 338 | match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 339 | Ok(_) => (), | ||
| 340 | Err(Error::Crc) => (), | ||
| 341 | Err(err) => return Err(err), | ||
| 342 | } | ||
| 343 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 344 | if !ocr.is_busy() { | ||
| 345 | // Power up done | ||
| 346 | break ocr; | ||
| 347 | } | ||
| 348 | }; | ||
| 349 | |||
| 350 | self.info.capacity = if ocr.access_mode() == 0b10 { | ||
| 351 | // Card is SDHC or SDXC or SDUC | ||
| 352 | CardCapacity::HighCapacity | ||
| 353 | } else { | ||
| 354 | CardCapacity::StandardCapacity | ||
| 355 | }; | ||
| 356 | self.info.ocr = ocr; | ||
| 357 | |||
| 358 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 359 | |||
| 360 | self.info.rca = 1u16.into(); | ||
| 361 | self.sdmmc | ||
| 362 | .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; | ||
| 363 | |||
| 364 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 365 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 366 | |||
| 367 | let (widbus, _) = bus_width_vals(bus_width); | ||
| 368 | |||
| 369 | // Write bus width to ExtCSD byte 183 | ||
| 370 | self.sdmmc.cmd( | ||
| 371 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 372 | false, | ||
| 373 | )?; | ||
| 374 | |||
| 375 | // Wait for ready after R1b response | ||
| 376 | loop { | ||
| 377 | let status = self.sdmmc.read_status(&self.info)?; | ||
| 378 | |||
| 379 | if status.ready_for_data() { | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 385 | self.info.ext_csd = self.read_ext_csd().await?; | ||
| 386 | |||
| 387 | Ok(()) | ||
| 388 | } | ||
| 389 | |||
| 390 | /// Gets the EXT_CSD register. | ||
| 391 | /// | ||
| 392 | /// eMMC only. | ||
| 393 | async fn read_ext_csd(&self) -> Result<ExtCSD, Error> { | ||
| 394 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 395 | let mut data_block = DataBlock::new(); | ||
| 396 | |||
| 397 | self.sdmmc | ||
| 398 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false) | ||
| 399 | .unwrap(); // CMD16 | ||
| 400 | |||
| 401 | let transfer = self | ||
| 402 | .sdmmc | ||
| 403 | .prepare_datapath_read(&mut data_block.0, block_size(size_of::<DataBlock>()), false); | ||
| 404 | self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 405 | |||
| 406 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 407 | |||
| 408 | Ok(data_block.0.into()) | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Card or Emmc storage device | ||
| 413 | impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { | ||
| 414 | /// Write a block | ||
| 415 | pub fn card(&self) -> A { | ||
| 416 | self.info.clone() | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Read a data block. | ||
| 420 | #[inline] | ||
| 421 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 422 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 423 | let card_capacity = self.info.get_capacity(); | ||
| 424 | |||
| 425 | // Always read 1 block of 512 bytes | ||
| 426 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 427 | let address = match card_capacity { | ||
| 428 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 429 | _ => block_idx, | ||
| 430 | }; | ||
| 431 | self.sdmmc | ||
| 432 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 433 | |||
| 434 | let transfer = self | ||
| 435 | .sdmmc | ||
| 436 | .prepare_datapath_read(&mut buffer.0, block_size(size_of::<DataBlock>()), false); | ||
| 437 | self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; | ||
| 438 | |||
| 439 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 440 | |||
| 441 | Ok(()) | ||
| 442 | } | ||
| 443 | |||
| 444 | /// Read multiple data blocks. | ||
| 445 | #[inline] | ||
| 446 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 447 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 448 | let card_capacity = self.info.get_capacity(); | ||
| 449 | |||
| 450 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 451 | let buffer = unsafe { | ||
| 452 | core::slice::from_raw_parts_mut( | ||
| 453 | blocks.as_mut_ptr() as *mut u32, | ||
| 454 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 455 | ) | ||
| 456 | }; | ||
| 457 | |||
| 458 | // Always read 1 block of 512 bytes | ||
| 459 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 460 | let address = match card_capacity { | ||
| 461 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 462 | _ => block_idx, | ||
| 463 | }; | ||
| 464 | self.sdmmc | ||
| 465 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 466 | |||
| 467 | let transfer = self | ||
| 468 | .sdmmc | ||
| 469 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 470 | self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 471 | |||
| 472 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 473 | |||
| 474 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 475 | self.sdmmc.clear_interrupt_flags(); | ||
| 476 | |||
| 477 | Ok(()) | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Write a data block. | ||
| 481 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> | ||
| 482 | where | ||
| 483 | CardStatus<A::Ext>: From<u32>, | ||
| 484 | { | ||
| 485 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 486 | |||
| 487 | // Always read 1 block of 512 bytes | ||
| 488 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 489 | let address = match self.info.get_capacity() { | ||
| 490 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 491 | _ => block_idx, | ||
| 492 | }; | ||
| 493 | self.sdmmc | ||
| 494 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 495 | |||
| 496 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 497 | #[cfg(sdmmc_v1)] | ||
| 498 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 499 | |||
| 500 | let transfer = self | ||
| 501 | .sdmmc | ||
| 502 | .prepare_datapath_write(&buffer.0, block_size(size_of::<DataBlock>()), false); | ||
| 503 | |||
| 504 | #[cfg(sdmmc_v2)] | ||
| 505 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 506 | |||
| 507 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 508 | |||
| 509 | // TODO: Make this configurable | ||
| 510 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 511 | |||
| 512 | while timeout > 0 { | ||
| 513 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 514 | if ready_for_data { | ||
| 515 | return Ok(()); | ||
| 516 | } | ||
| 517 | timeout -= 1; | ||
| 518 | } | ||
| 519 | |||
| 520 | Err(Error::SoftwareTimeout) | ||
| 521 | } | ||
| 522 | |||
| 523 | /// Write multiple data blocks. | ||
| 524 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> | ||
| 525 | where | ||
| 526 | CardStatus<A::Ext>: From<u32>, | ||
| 527 | { | ||
| 528 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 529 | |||
| 530 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 531 | let buffer = unsafe { | ||
| 532 | core::slice::from_raw_parts( | ||
| 533 | blocks.as_ptr() as *const u32, | ||
| 534 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 535 | ) | ||
| 536 | }; | ||
| 537 | // Always read 1 block of 512 bytes | ||
| 538 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 539 | let address = match self.info.get_capacity() { | ||
| 540 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 541 | _ => block_idx, | ||
| 542 | }; | ||
| 543 | |||
| 544 | self.sdmmc | ||
| 545 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 546 | |||
| 547 | #[cfg(sdmmc_v1)] | ||
| 548 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 549 | |||
| 550 | // Setup write command | ||
| 551 | let transfer = self | ||
| 552 | .sdmmc | ||
| 553 | .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 554 | |||
| 555 | #[cfg(sdmmc_v2)] | ||
| 556 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 557 | |||
| 558 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 559 | |||
| 560 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 561 | self.sdmmc.clear_interrupt_flags(); | ||
| 562 | |||
| 563 | // TODO: Make this configurable | ||
| 564 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 565 | |||
| 566 | while timeout > 0 { | ||
| 567 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 568 | |||
| 569 | if ready_for_data { | ||
| 570 | return Ok(()); | ||
| 571 | } | ||
| 572 | timeout -= 1; | ||
| 573 | } | ||
| 574 | Err(Error::SoftwareTimeout) | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | #[derive(Clone, Copy, Debug, Default)] | ||
| 579 | /// SD Card | ||
| 580 | pub struct Card { | ||
| 581 | /// The type of this card | ||
| 582 | pub card_type: CardCapacity, | ||
| 583 | /// Operation Conditions Register | ||
| 584 | pub ocr: OCR<SD>, | ||
| 585 | /// Relative Card Address | ||
| 586 | pub rca: u16, | ||
| 587 | /// Card ID | ||
| 588 | pub cid: CID<SD>, | ||
| 589 | /// Card Specific Data | ||
| 590 | pub csd: CSD<SD>, | ||
| 591 | /// SD CARD Configuration Register | ||
| 592 | pub scr: SCR, | ||
| 593 | /// SD Status | ||
| 594 | pub status: SDStatus, | ||
| 595 | } | ||
| 596 | |||
| 597 | impl Addressable for Card { | ||
| 598 | type Ext = SD; | ||
| 599 | |||
| 600 | /// Get this peripheral's address on the SDMMC bus | ||
| 601 | fn get_address(&self) -> u16 { | ||
| 602 | self.rca | ||
| 603 | } | ||
| 604 | |||
| 605 | /// Is this a standard or high capacity peripheral? | ||
| 606 | fn get_capacity(&self) -> CardCapacity { | ||
| 607 | self.card_type | ||
| 608 | } | ||
| 609 | |||
| 610 | /// Size in bytes | ||
| 611 | fn size(&self) -> u64 { | ||
| 612 | u64::from(self.csd.block_count()) * 512 | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | #[derive(Clone, Copy, Debug, Default)] | ||
| 617 | /// eMMC storage | ||
| 618 | pub struct Emmc { | ||
| 619 | /// The capacity of this card | ||
| 620 | pub capacity: CardCapacity, | ||
| 621 | /// Operation Conditions Register | ||
| 622 | pub ocr: OCR<EMMC>, | ||
| 623 | /// Relative Card Address | ||
| 624 | pub rca: u16, | ||
| 625 | /// Card ID | ||
| 626 | pub cid: CID<EMMC>, | ||
| 627 | /// Card Specific Data | ||
| 628 | pub csd: CSD<EMMC>, | ||
| 629 | /// Extended Card Specific Data | ||
| 630 | pub ext_csd: ExtCSD, | ||
| 631 | } | ||
| 632 | |||
| 633 | impl Addressable for Emmc { | ||
| 634 | type Ext = EMMC; | ||
| 635 | |||
| 636 | /// Get this peripheral's address on the SDMMC bus | ||
| 637 | fn get_address(&self) -> u16 { | ||
| 638 | self.rca | ||
| 639 | } | ||
| 640 | |||
| 641 | /// Is this a standard or high capacity peripheral? | ||
| 642 | fn get_capacity(&self) -> CardCapacity { | ||
| 643 | self.capacity | ||
| 644 | } | ||
| 645 | |||
| 646 | /// Size in bytes | ||
| 647 | fn size(&self) -> u64 { | ||
| 648 | u64::from(self.ext_csd.sector_count()) * 512 | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { | ||
| 653 | type Error = Error; | ||
| 654 | type Align = aligned::A4; | ||
| 655 | |||
| 656 | async fn read( | ||
| 657 | &mut self, | ||
| 658 | block_address: u32, | ||
| 659 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 660 | ) -> Result<(), Self::Error> { | ||
| 661 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 662 | if buf.len() == 1 { | ||
| 663 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) }; | ||
| 664 | self.read_block(block_address, block).await?; | ||
| 665 | } else { | ||
| 666 | let blocks: &mut [DataBlock] = | ||
| 667 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 668 | self.read_blocks(block_address, blocks).await?; | ||
| 669 | } | ||
| 670 | Ok(()) | ||
| 671 | } | ||
| 672 | |||
| 673 | async fn write( | ||
| 674 | &mut self, | ||
| 675 | block_address: u32, | ||
| 676 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 677 | ) -> Result<(), Self::Error> { | ||
| 678 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 679 | if buf.len() == 1 { | ||
| 680 | let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) }; | ||
| 681 | self.write_block(block_address, block).await?; | ||
| 682 | } else { | ||
| 683 | let blocks: &[DataBlock] = | ||
| 684 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 685 | self.write_blocks(block_address, blocks).await?; | ||
| 686 | } | ||
| 687 | Ok(()) | ||
| 688 | } | ||
| 689 | |||
| 690 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 691 | Ok(self.info.size()) | ||
| 692 | } | ||
| 693 | } | ||
diff --git a/embassy-stm32/src/sdmmc/sdio.rs b/embassy-stm32/src/sdmmc/sdio.rs new file mode 100644 index 000000000..1412b21fc --- /dev/null +++ b/embassy-stm32/src/sdmmc/sdio.rs | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | use core::ops::{Deref, DerefMut}; | ||
| 2 | |||
| 3 | use sdio_host::common_cmd::{R1, Rz, cmd}; | ||
| 4 | use sdio_host::sd::BusWidth; | ||
| 5 | use sdio_host::sd_cmd; | ||
| 6 | |||
| 7 | use crate::sdmmc::{Error, Sdmmc, block_size, slice8_mut, slice8_ref}; | ||
| 8 | use crate::time::Hertz; | ||
| 9 | |||
| 10 | /// Aligned data block for SDMMC transfers. | ||
| 11 | /// | ||
| 12 | /// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 13 | #[repr(align(4))] | ||
| 14 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 15 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 16 | pub struct DataBlock(pub [u32; 16]); | ||
| 17 | |||
| 18 | impl DataBlock { | ||
| 19 | /// Create a new DataBlock | ||
| 20 | pub const fn new() -> Self { | ||
| 21 | DataBlock([0u32; 16]) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl Deref for DataBlock { | ||
| 26 | type Target = [u8; 64]; | ||
| 27 | |||
| 28 | fn deref(&self) -> &Self::Target { | ||
| 29 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | impl DerefMut for DataBlock { | ||
| 34 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 35 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | /// Storage Device | ||
| 40 | pub struct SerialDataInterface<'a, 'b> { | ||
| 41 | /// Inner member | ||
| 42 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Card Storage Device | ||
| 46 | impl<'a, 'b> SerialDataInterface<'a, 'b> { | ||
| 47 | /// Create a new SD card | ||
| 48 | pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result<Self, Error> { | ||
| 49 | let mut s = Self { sdmmc }; | ||
| 50 | |||
| 51 | s.acquire(freq).await?; | ||
| 52 | |||
| 53 | Ok(s) | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Initializes the card into a known state (or at least tries to). | ||
| 57 | async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> { | ||
| 58 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 59 | |||
| 60 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 61 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 62 | bus_width => bus_width, | ||
| 63 | }; | ||
| 64 | |||
| 65 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 66 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 67 | self.sdmmc.init_idle()?; | ||
| 68 | |||
| 69 | self.sdmmc.cmd(cmd::<Rz>(5, 0), false)?; | ||
| 70 | |||
| 71 | // Get RCA | ||
| 72 | let rca = self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 73 | |||
| 74 | // Select the card with RCA | ||
| 75 | self.sdmmc.select_card(Some(rca.try_into().unwrap()))?; | ||
| 76 | |||
| 77 | Ok(()) | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Set the bus to the 4-bit high-speed frequency | ||
| 81 | pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> { | ||
| 82 | self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?; | ||
| 83 | |||
| 84 | Ok(()) | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Run cmd52 | ||
| 88 | pub async fn cmd52(&mut self, arg: u32) -> Result<u32, Error> { | ||
| 89 | self.sdmmc.cmd(cmd::<R1>(52, arg), false) | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Read in block mode using cmd53 | ||
| 93 | pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 94 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 95 | |||
| 96 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 97 | let buffer = unsafe { | ||
| 98 | core::slice::from_raw_parts_mut( | ||
| 99 | blocks.as_mut_ptr() as *mut u32, | ||
| 100 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 101 | ) | ||
| 102 | }; | ||
| 103 | |||
| 104 | let transfer = self | ||
| 105 | .sdmmc | ||
| 106 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 107 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 108 | |||
| 109 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 110 | self.sdmmc.clear_interrupt_flags(); | ||
| 111 | |||
| 112 | Ok(()) | ||
| 113 | } | ||
| 114 | |||
| 115 | /// Read in multibyte mode using cmd53 | ||
| 116 | pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut [u32]) -> Result<(), Error> { | ||
| 117 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 118 | |||
| 119 | let transfer = self | ||
| 120 | .sdmmc | ||
| 121 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), true); | ||
| 122 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 123 | |||
| 124 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 125 | self.sdmmc.clear_interrupt_flags(); | ||
| 126 | |||
| 127 | Ok(()) | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Write in block mode using cmd53 | ||
| 131 | pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 132 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 133 | |||
| 134 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 135 | let buffer = unsafe { | ||
| 136 | core::slice::from_raw_parts_mut( | ||
| 137 | blocks.as_ptr() as *mut u32, | ||
| 138 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 139 | ) | ||
| 140 | }; | ||
| 141 | |||
| 142 | #[cfg(sdmmc_v1)] | ||
| 143 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 144 | |||
| 145 | let transfer = self | ||
| 146 | .sdmmc | ||
| 147 | .prepare_datapath_read(buffer, block_size(size_of::<DataBlock>()), false); | ||
| 148 | |||
| 149 | #[cfg(sdmmc_v2)] | ||
| 150 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 151 | |||
| 152 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 153 | self.sdmmc.clear_interrupt_flags(); | ||
| 154 | |||
| 155 | Ok(()) | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Write in multibyte mode using cmd53 | ||
| 159 | pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &[u32]) -> Result<(), Error> { | ||
| 160 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 161 | |||
| 162 | #[cfg(sdmmc_v1)] | ||
| 163 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 164 | |||
| 165 | let transfer = self | ||
| 166 | .sdmmc | ||
| 167 | .prepare_datapath_write(buffer, block_size(size_of::<DataBlock>()), true); | ||
| 168 | |||
| 169 | #[cfg(sdmmc_v2)] | ||
| 170 | self.sdmmc.cmd(cmd::<Rz>(53, arg), true)?; | ||
| 171 | |||
| 172 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 173 | self.sdmmc.clear_interrupt_flags(); | ||
| 174 | |||
| 175 | Ok(()) | ||
| 176 | } | ||
| 177 | } | ||
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 897b0e05e..098fd6986 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -72,7 +73,7 @@ async fn main(_spawner: Spawner) { | |||
| 72 | let block_idx = 16; | 73 | let block_idx = 16; |
| 73 | 74 | ||
| 74 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. | 75 | // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. |
| 75 | let mut block = DataBlock([0u8; 512]); | 76 | let mut block = DataBlock::new(); |
| 76 | 77 | ||
| 77 | storage.read_block(block_idx, &mut block).await.unwrap(); | 78 | storage.read_block(block_idx, &mut block).await.unwrap(); |
| 78 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); | 79 | info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); |
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 0dd4dd6f3..e5d261d89 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::{Hertz, mhz}; | 8 | use embassy_stm32::time::{Hertz, mhz}; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 548d0bd09..f2e5bedeb 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::sdmmc::{CmdBlock, Sdmmc, StorageDevice}; | 6 | use embassy_stm32::sdmmc::Sdmmc; |
| 7 | use embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice}; | ||
| 7 | use embassy_stm32::time::mhz; | 8 | use embassy_stm32::time::mhz; |
| 8 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; | 9 | use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index d98b93a15..07422c42e 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs | |||
| @@ -7,7 +7,8 @@ mod common; | |||
| 7 | use common::*; | 7 | use common::*; |
| 8 | use defmt::assert_eq; | 8 | use defmt::assert_eq; |
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_stm32::sdmmc::{CmdBlock, DataBlock, Sdmmc, StorageDevice}; | 10 | use embassy_stm32::sdmmc::Sdmmc; |
| 11 | use embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice}; | ||
| 11 | use embassy_stm32::time::mhz; | 12 | use embassy_stm32::time::mhz; |
| 12 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; | 13 | use embassy_stm32::{bind_interrupts, peripherals, sdmmc}; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -28,16 +29,16 @@ async fn main(_spawner: Spawner) { | |||
| 28 | // Arbitrary block index | 29 | // Arbitrary block index |
| 29 | let block_idx = 16; | 30 | let block_idx = 16; |
| 30 | 31 | ||
| 31 | let mut pattern1 = DataBlock([0u8; 512]); | 32 | let mut pattern1 = DataBlock::new(); |
| 32 | let mut pattern2 = DataBlock([0u8; 512]); | 33 | let mut pattern2 = DataBlock::new(); |
| 33 | for i in 0..512 { | 34 | for i in 0..512 { |
| 34 | pattern1[i] = i as u8; | 35 | pattern1[i] = i as u8; |
| 35 | pattern2[i] = !i as u8; | 36 | pattern2[i] = !i as u8; |
| 36 | } | 37 | } |
| 37 | let patterns = [pattern1.clone(), pattern2.clone()]; | 38 | let patterns = [pattern1.clone(), pattern2.clone()]; |
| 38 | 39 | ||
| 39 | let mut block = DataBlock([0u8; 512]); | 40 | let mut block = DataBlock::new(); |
| 40 | let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])]; | 41 | let mut blocks = [DataBlock::new(), DataBlock::new()]; |
| 41 | 42 | ||
| 42 | // ======== Try 4bit. ============== | 43 | // ======== Try 4bit. ============== |
| 43 | info!("initializing in 4-bit mode..."); | 44 | info!("initializing in 4-bit mode..."); |
