diff options
| author | Thales Fragoso <[email protected]> | 2021-05-11 01:28:45 -0300 |
|---|---|---|
| committer | Thales Fragoso <[email protected]> | 2021-05-14 23:42:12 -0300 |
| commit | c183c352c747bb418f5ed4553645c82ab2be3dbf (patch) | |
| tree | 595d3538f3327ff932931a73a8d1b9d60aa00d0a | |
| parent | 490152d02892ed7478c3920a2eb7373af66be90f (diff) | |
SDMMC: Implement read and write
| -rw-r--r-- | embassy-stm32/src/sdmmc_v2.rs | 185 |
1 files changed, 154 insertions, 31 deletions
diff --git a/embassy-stm32/src/sdmmc_v2.rs b/embassy-stm32/src/sdmmc_v2.rs index a3094f691..182add6f6 100644 --- a/embassy-stm32/src/sdmmc_v2.rs +++ b/embassy-stm32/src/sdmmc_v2.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::task::{Context, Poll}; | 2 | use core::task::Poll; |
| 3 | 3 | ||
| 4 | use embassy::interrupt::InterruptExt; | 4 | use embassy::interrupt::InterruptExt; |
| 5 | use embassy::util::{AtomicWaker, OnDrop, Unborrow}; | 5 | use embassy::util::{AtomicWaker, OnDrop, Unborrow}; |
| @@ -10,7 +10,7 @@ use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, | |||
| 10 | use crate::fmt::*; | 10 | use crate::fmt::*; |
| 11 | use crate::pac; | 11 | use crate::pac; |
| 12 | use crate::pac::gpio::Gpio; | 12 | use crate::pac::gpio::Gpio; |
| 13 | use crate::pac::interrupt::{Interrupt, InterruptEnum}; | 13 | use crate::pac::interrupt::Interrupt; |
| 14 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 14 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 15 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 16 | 16 | ||
| @@ -32,6 +32,9 @@ impl Default for Signalling { | |||
| 32 | } | 32 | } |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | #[repr(align(4))] | ||
| 36 | pub struct DataBlock([u8; 512]); | ||
| 37 | |||
| 35 | /// Errors | 38 | /// Errors |
| 36 | #[non_exhaustive] | 39 | #[non_exhaustive] |
| 37 | #[derive(Debug, Copy, Clone)] | 40 | #[derive(Debug, Copy, Clone)] |
| @@ -201,16 +204,43 @@ impl<'d, T: Instance, P: Pins<T>> Sdmmc<'d, T, P> { | |||
| 201 | .await | 204 | .await |
| 202 | } | 205 | } |
| 203 | 206 | ||
| 207 | #[inline(always)] | ||
| 208 | pub async fn read_block( | ||
| 209 | &mut self, | ||
| 210 | block_idx: u32, | ||
| 211 | buffer: &mut DataBlock, | ||
| 212 | ) -> Result<(), Error> { | ||
| 213 | let card_capacity = self.card()?.card_type; | ||
| 214 | let inner = T::inner(); | ||
| 215 | let state = T::state(); | ||
| 216 | |||
| 217 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 218 | let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 219 | inner.read_block(block_idx, buf, card_capacity, state).await | ||
| 220 | } | ||
| 221 | |||
| 222 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 223 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 224 | let inner = T::inner(); | ||
| 225 | let state = T::state(); | ||
| 226 | |||
| 227 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 228 | let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 229 | inner.write_block(block_idx, buf, card, state).await | ||
| 230 | } | ||
| 231 | |||
| 204 | /// Get a reference to the initialized card | 232 | /// Get a reference to the initialized card |
| 205 | /// | 233 | /// |
| 206 | /// # Errors | 234 | /// # Errors |
| 207 | /// | 235 | /// |
| 208 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | 236 | /// Returns Error::NoCard if [`init_card`](#method.init_card) |
| 209 | /// has not previously succeeded | 237 | /// has not previously succeeded |
| 238 | #[inline(always)] | ||
| 210 | pub fn card(&self) -> Result<&Card, Error> { | 239 | pub fn card(&self) -> Result<&Card, Error> { |
| 211 | self.card.as_ref().ok_or(Error::NoCard) | 240 | self.card.as_ref().ok_or(Error::NoCard) |
| 212 | } | 241 | } |
| 213 | 242 | ||
| 243 | #[inline(always)] | ||
| 214 | fn on_interrupt(_: *mut ()) { | 244 | fn on_interrupt(_: *mut ()) { |
| 215 | let regs = T::inner(); | 245 | let regs = T::inner(); |
| 216 | let state = T::state(); | 246 | let state = T::state(); |
| @@ -260,7 +290,7 @@ impl SdmmcInner { | |||
| 260 | /// Initializes card (if present) and sets the bus at the | 290 | /// Initializes card (if present) and sets the bus at the |
| 261 | /// specified frequency. | 291 | /// specified frequency. |
| 262 | #[allow(clippy::too_many_arguments)] | 292 | #[allow(clippy::too_many_arguments)] |
| 263 | pub async fn init_card( | 293 | async fn init_card( |
| 264 | &self, | 294 | &self, |
| 265 | freq: Hertz, | 295 | freq: Hertz, |
| 266 | bus_width: BusWidth, | 296 | bus_width: BusWidth, |
| @@ -397,8 +427,120 @@ impl SdmmcInner { | |||
| 397 | Ok(()) | 427 | Ok(()) |
| 398 | } | 428 | } |
| 399 | 429 | ||
| 400 | async fn read_block(&mut self, block_idx: u32, buffer: &mut [u32; 128]) -> Result<(), Error> { | 430 | async fn read_block( |
| 401 | self::todo!() | 431 | &self, |
| 432 | block_idx: u32, | ||
| 433 | buffer: &mut [u32; 128], | ||
| 434 | capacity: CardCapacity, | ||
| 435 | waker_reg: &AtomicWaker, | ||
| 436 | ) -> Result<(), Error> { | ||
| 437 | // Always read 1 block of 512 bytes | ||
| 438 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 439 | let address = match capacity { | ||
| 440 | CardCapacity::SDSC => block_idx * 512, | ||
| 441 | _ => block_idx, | ||
| 442 | }; | ||
| 443 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 444 | |||
| 445 | let regs = self.0; | ||
| 446 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 447 | |||
| 448 | let buf_addr = buffer as *mut [u32; 128] as u32; | ||
| 449 | unsafe { | ||
| 450 | self.prepare_datapath_transfer(buf_addr, 512, 9, Dir::CardToHost); | ||
| 451 | self.data_interrupts(true); | ||
| 452 | } | ||
| 453 | self.cmd(Cmd::read_single_block(address), true)?; | ||
| 454 | |||
| 455 | let res = poll_fn(|cx| { | ||
| 456 | waker_reg.register(cx.waker()); | ||
| 457 | let status = unsafe { regs.star().read() }; | ||
| 458 | |||
| 459 | if status.dcrcfail() { | ||
| 460 | return Poll::Ready(Err(Error::Crc)); | ||
| 461 | } else if status.dtimeout() { | ||
| 462 | return Poll::Ready(Err(Error::Timeout)); | ||
| 463 | } else if status.dataend() { | ||
| 464 | return Poll::Ready(Ok(())); | ||
| 465 | } | ||
| 466 | Poll::Pending | ||
| 467 | }) | ||
| 468 | .await; | ||
| 469 | self.clear_interrupt_flags(); | ||
| 470 | |||
| 471 | if res.is_ok() { | ||
| 472 | on_drop.defuse(); | ||
| 473 | unsafe { | ||
| 474 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 475 | } | ||
| 476 | } | ||
| 477 | res | ||
| 478 | } | ||
| 479 | |||
| 480 | async fn write_block( | ||
| 481 | &self, | ||
| 482 | block_idx: u32, | ||
| 483 | buffer: &[u32; 128], | ||
| 484 | card: &mut Card, | ||
| 485 | waker_reg: &AtomicWaker, | ||
| 486 | ) -> Result<(), Error> { | ||
| 487 | // Always read 1 block of 512 bytes | ||
| 488 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 489 | let address = match card.card_type { | ||
| 490 | CardCapacity::SDSC => block_idx * 512, | ||
| 491 | _ => block_idx, | ||
| 492 | }; | ||
| 493 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 494 | |||
| 495 | let regs = self.0; | ||
| 496 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 497 | |||
| 498 | let buf_addr = buffer as *const [u32; 128] as u32; | ||
| 499 | unsafe { | ||
| 500 | self.prepare_datapath_transfer(buf_addr, 512, 9, Dir::HostToCard); | ||
| 501 | self.data_interrupts(true); | ||
| 502 | } | ||
| 503 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 504 | |||
| 505 | let res = poll_fn(|cx| { | ||
| 506 | waker_reg.register(cx.waker()); | ||
| 507 | let status = unsafe { regs.star().read() }; | ||
| 508 | |||
| 509 | if status.dcrcfail() { | ||
| 510 | return Poll::Ready(Err(Error::Crc)); | ||
| 511 | } else if status.dtimeout() { | ||
| 512 | return Poll::Ready(Err(Error::Timeout)); | ||
| 513 | } else if status.dataend() { | ||
| 514 | return Poll::Ready(Ok(())); | ||
| 515 | } | ||
| 516 | Poll::Pending | ||
| 517 | }) | ||
| 518 | .await; | ||
| 519 | self.clear_interrupt_flags(); | ||
| 520 | |||
| 521 | match res { | ||
| 522 | Ok(_) => { | ||
| 523 | on_drop.defuse(); | ||
| 524 | unsafe { | ||
| 525 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 526 | } | ||
| 527 | // TODO: Make this configurable | ||
| 528 | let mut timeout: u32 = 0xFFFF_FFFF; | ||
| 529 | |||
| 530 | // Try to read card status (ACMD13) | ||
| 531 | while timeout > 0 { | ||
| 532 | match self.read_sd_status(card, waker_reg).await { | ||
| 533 | Ok(_) => return Ok(()), | ||
| 534 | Err(Error::Timeout) => (), // Try again | ||
| 535 | Err(e) => return Err(e), | ||
| 536 | } | ||
| 537 | |||
| 538 | timeout -= 1; | ||
| 539 | } | ||
| 540 | Err(Error::SoftwareTimeout) | ||
| 541 | } | ||
| 542 | Err(e) => Err(e), | ||
| 543 | } | ||
| 402 | } | 544 | } |
| 403 | 545 | ||
| 404 | /// Get the current SDMMC bus clock | 546 | /// Get the current SDMMC bus clock |
| @@ -795,32 +937,13 @@ impl SdmmcInner { | |||
| 795 | } | 937 | } |
| 796 | } | 938 | } |
| 797 | 939 | ||
| 798 | fn store_waker_and_unmask( | ||
| 799 | &self, | ||
| 800 | cx: &Context, | ||
| 801 | interrupt_sdmmc1: bool, | ||
| 802 | waker_reg: &AtomicWaker, | ||
| 803 | ) { | ||
| 804 | use cortex_m::peripheral::NVIC; | ||
| 805 | |||
| 806 | // NOTE(unsafe) We own the interrupt and can unmask it, it won't cause unsoundness | ||
| 807 | unsafe { | ||
| 808 | if interrupt_sdmmc1 { | ||
| 809 | waker_reg.register(cx.waker()); | ||
| 810 | NVIC::unmask(InterruptEnum::SDMMC1); | ||
| 811 | } else { | ||
| 812 | waker_reg.register(cx.waker()); | ||
| 813 | NVIC::unmask(InterruptEnum::SDMMC2); | ||
| 814 | } | ||
| 815 | } | ||
| 816 | } | ||
| 817 | |||
| 818 | /// # Safety | 940 | /// # Safety |
| 819 | /// | 941 | /// |
| 820 | /// Ensure that `regs` has exclusive access to the regblocks | 942 | /// Ensure that `regs` has exclusive access to the regblocks |
| 821 | unsafe fn on_drop(&self) { | 943 | unsafe fn on_drop(&self) { |
| 822 | let regs = self.0; | 944 | let regs = self.0; |
| 823 | if regs.star().read().dpsmact() { | 945 | if regs.star().read().dpsmact() { |
| 946 | self.clear_interrupt_flags(); | ||
| 824 | // Send abort | 947 | // Send abort |
| 825 | // CP state machine must be idle | 948 | // CP state machine must be idle |
| 826 | while regs.star().read().cpsmact() {} | 949 | while regs.star().read().cpsmact() {} |
| @@ -890,9 +1013,9 @@ impl Cmd { | |||
| 890 | } | 1013 | } |
| 891 | 1014 | ||
| 892 | /// CMD12: | 1015 | /// CMD12: |
| 893 | const fn stop_transmission() -> Cmd { | 1016 | //const fn stop_transmission() -> Cmd { |
| 894 | Cmd::new(12, 0, Response::Short) | 1017 | // Cmd::new(12, 0, Response::Short) |
| 895 | } | 1018 | //} |
| 896 | 1019 | ||
| 897 | /// CMD13: Ask card to send status register | 1020 | /// CMD13: Ask card to send status register |
| 898 | /// ACMD13: SD Status | 1021 | /// ACMD13: SD Status |
| @@ -911,9 +1034,9 @@ impl Cmd { | |||
| 911 | } | 1034 | } |
| 912 | 1035 | ||
| 913 | /// CMD18: Multiple Block Read | 1036 | /// CMD18: Multiple Block Read |
| 914 | const fn read_multiple_blocks(addr: u32) -> Cmd { | 1037 | //const fn read_multiple_blocks(addr: u32) -> Cmd { |
| 915 | Cmd::new(18, addr, Response::Short) | 1038 | // Cmd::new(18, addr, Response::Short) |
| 916 | } | 1039 | //} |
| 917 | 1040 | ||
| 918 | /// CMD24: Block Write | 1041 | /// CMD24: Block Write |
| 919 | const fn write_single_block(addr: u32) -> Cmd { | 1042 | const fn write_single_block(addr: u32) -> Cmd { |
