diff options
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 1183 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 13 | ||||
| -rw-r--r-- | tests/stm32/Cargo.toml | 43 | ||||
| -rw-r--r-- | tests/stm32/gen_test.py | 44 | ||||
| -rw-r--r-- | tests/stm32/src/bin/sdmmc.rs | 148 |
5 files changed, 757 insertions, 674 deletions
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ac00b5176..23ece3a2a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -41,7 +41,7 @@ impl Default for Signalling { | |||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | #[repr(align(4))] | 43 | #[repr(align(4))] |
| 44 | #[derive(Debug, Clone)] | 44 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 46 | pub struct DataBlock(pub [u8; 512]); | 46 | pub struct DataBlock(pub [u8; 512]); |
| 47 | 47 | ||
| @@ -61,7 +61,7 @@ impl DerefMut for DataBlock { | |||
| 61 | 61 | ||
| 62 | /// Errors | 62 | /// Errors |
| 63 | #[non_exhaustive] | 63 | #[non_exhaustive] |
| 64 | #[derive(Debug, Copy, Clone)] | 64 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| 65 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 65 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 66 | pub enum Error { | 66 | pub enum Error { |
| 67 | Timeout, | 67 | Timeout, |
| @@ -135,57 +135,53 @@ enum Response { | |||
| 135 | Long = 3, | 135 | Long = 3, |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | cfg_if::cfg_if! { | 138 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 139 | if #[cfg(sdmmc_v1)] { | 139 | /// `sdmmc_ck` in Hertz. |
| 140 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 140 | /// |
| 141 | /// `sdmmc_ck` in Hertz. | 141 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), |
| 142 | /// | 142 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. |
| 143 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), | 143 | #[cfg(sdmmc_v1)] |
| 144 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. | 144 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { |
| 145 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { | 145 | // sdmmc_v1 maximum clock is 50 MHz |
| 146 | // sdmmc_v1 maximum clock is 50 MHz | 146 | if sdmmc_ck > 50_000_000 { |
| 147 | if sdmmc_ck > 50_000_000 { | 147 | return Err(Error::BadClock); |
| 148 | return Err(Error::BadClock); | 148 | } |
| 149 | } | ||
| 150 | 149 | ||
| 151 | // bypass divisor | 150 | // bypass divisor |
| 152 | if ker_ck.0 <= sdmmc_ck { | 151 | if ker_ck.0 <= sdmmc_ck { |
| 153 | return Ok((true, 0, ker_ck)); | 152 | return Ok((true, 0, ker_ck)); |
| 154 | } | 153 | } |
| 155 | 154 | ||
| 156 | // `ker_ck / sdmmc_ck` rounded up | 155 | // `ker_ck / sdmmc_ck` rounded up |
| 157 | let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | 156 | let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { |
| 158 | 0 | 1 => Ok(0), | 157 | 0 | 1 => Ok(0), |
| 159 | x @ 2..=258 => { | 158 | x @ 2..=258 => Ok((x - 2) as u8), |
| 160 | Ok((x - 2) as u8) | 159 | _ => Err(Error::BadClock), |
| 161 | } | 160 | }?; |
| 162 | _ => Err(Error::BadClock), | ||
| 163 | }?; | ||
| 164 | 161 | ||
| 165 | // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] | 162 | // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] |
| 166 | let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); | 163 | let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); |
| 167 | Ok((false, clk_div, clk_f)) | 164 | Ok((false, clk_div, clk_f)) |
| 168 | } | 165 | } |
| 169 | } else if #[cfg(sdmmc_v2)] { | 166 | |
| 170 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 167 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 171 | /// `sdmmc_ck` in Hertz. | 168 | /// `sdmmc_ck` in Hertz. |
| 172 | /// | 169 | /// |
| 173 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), | 170 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), |
| 174 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. | 171 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. |
| 175 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { | 172 | #[cfg(sdmmc_v2)] |
| 176 | // `ker_ck / sdmmc_ck` rounded up | 173 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { |
| 177 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | 174 | // `ker_ck / sdmmc_ck` rounded up |
| 178 | 0 | 1 => Ok((false, 0, ker_ck)), | 175 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { |
| 179 | x @ 2..=2046 => { | 176 | 0 | 1 => Ok((false, 0, ker_ck)), |
| 180 | // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] | 177 | x @ 2..=2046 => { |
| 181 | let clk_div = ((x + 1) / 2) as u16; | 178 | // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] |
| 182 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | 179 | let clk_div = ((x + 1) / 2) as u16; |
| 183 | 180 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | |
| 184 | Ok((false, clk_div, clk)) | 181 | |
| 185 | } | 182 | Ok((false, clk_div, clk)) |
| 186 | _ => Err(Error::BadClock), | ||
| 187 | } | ||
| 188 | } | 183 | } |
| 184 | _ => Err(Error::BadClock), | ||
| 189 | } | 185 | } |
| 190 | } | 186 | } |
| 191 | 187 | ||
| @@ -208,9 +204,10 @@ impl Default for Config { | |||
| 208 | } | 204 | } |
| 209 | 205 | ||
| 210 | /// Sdmmc device | 206 | /// Sdmmc device |
| 211 | pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { | 207 | pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> { |
| 212 | _peri: PeripheralRef<'d, T>, | 208 | _peri: PeripheralRef<'d, T>, |
| 213 | irq: PeripheralRef<'d, T::Interrupt>, | 209 | irq: PeripheralRef<'d, T::Interrupt>, |
| 210 | #[allow(unused)] | ||
| 214 | dma: PeripheralRef<'d, Dma>, | 211 | dma: PeripheralRef<'d, Dma>, |
| 215 | 212 | ||
| 216 | clk: PeripheralRef<'d, AnyPin>, | 213 | clk: PeripheralRef<'d, AnyPin>, |
| @@ -309,49 +306,6 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 309 | config, | 306 | config, |
| 310 | ) | 307 | ) |
| 311 | } | 308 | } |
| 312 | |||
| 313 | fn new_inner( | ||
| 314 | sdmmc: impl Peripheral<P = T> + 'd, | ||
| 315 | irq: impl Peripheral<P = T::Interrupt> + 'd, | ||
| 316 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 317 | clk: PeripheralRef<'d, AnyPin>, | ||
| 318 | cmd: PeripheralRef<'d, AnyPin>, | ||
| 319 | d0: PeripheralRef<'d, AnyPin>, | ||
| 320 | d1: Option<PeripheralRef<'d, AnyPin>>, | ||
| 321 | d2: Option<PeripheralRef<'d, AnyPin>>, | ||
| 322 | d3: Option<PeripheralRef<'d, AnyPin>>, | ||
| 323 | config: Config, | ||
| 324 | ) -> Self { | ||
| 325 | into_ref!(sdmmc, irq, dma); | ||
| 326 | |||
| 327 | T::enable(); | ||
| 328 | T::reset(); | ||
| 329 | |||
| 330 | let inner = T::inner(); | ||
| 331 | unsafe { inner.new_inner() }; | ||
| 332 | |||
| 333 | irq.set_handler(Self::on_interrupt); | ||
| 334 | irq.unpend(); | ||
| 335 | irq.enable(); | ||
| 336 | |||
| 337 | Self { | ||
| 338 | _peri: sdmmc, | ||
| 339 | irq, | ||
| 340 | dma, | ||
| 341 | |||
| 342 | clk, | ||
| 343 | cmd, | ||
| 344 | d0, | ||
| 345 | d1, | ||
| 346 | d2, | ||
| 347 | d3, | ||
| 348 | |||
| 349 | config, | ||
| 350 | clock: SD_INIT_FREQ, | ||
| 351 | signalling: Default::default(), | ||
| 352 | card: None, | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | 309 | } |
| 356 | 310 | ||
| 357 | #[cfg(sdmmc_v2)] | 311 | #[cfg(sdmmc_v2)] |
| @@ -379,6 +333,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 379 | Self::new_inner( | 333 | Self::new_inner( |
| 380 | sdmmc, | 334 | sdmmc, |
| 381 | irq, | 335 | irq, |
| 336 | NoDma.into_ref(), | ||
| 382 | clk.map_into(), | 337 | clk.map_into(), |
| 383 | cmd.map_into(), | 338 | cmd.map_into(), |
| 384 | d0.map_into(), | 339 | d0.map_into(), |
| @@ -421,6 +376,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 421 | Self::new_inner( | 376 | Self::new_inner( |
| 422 | sdmmc, | 377 | sdmmc, |
| 423 | irq, | 378 | irq, |
| 379 | NoDma.into_ref(), | ||
| 424 | clk.map_into(), | 380 | clk.map_into(), |
| 425 | cmd.map_into(), | 381 | cmd.map_into(), |
| 426 | d0.map_into(), | 382 | d0.map_into(), |
| @@ -430,10 +386,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 430 | config, | 386 | config, |
| 431 | ) | 387 | ) |
| 432 | } | 388 | } |
| 389 | } | ||
| 433 | 390 | ||
| 391 | impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | ||
| 434 | fn new_inner( | 392 | fn new_inner( |
| 435 | sdmmc: impl Peripheral<P = T> + 'd, | 393 | sdmmc: impl Peripheral<P = T> + 'd, |
| 436 | irq: impl Peripheral<P = T::Interrupt> + 'd, | 394 | irq: impl Peripheral<P = T::Interrupt> + 'd, |
| 395 | dma: impl Peripheral<P = Dma> + 'd, | ||
| 437 | clk: PeripheralRef<'d, AnyPin>, | 396 | clk: PeripheralRef<'d, AnyPin>, |
| 438 | cmd: PeripheralRef<'d, AnyPin>, | 397 | cmd: PeripheralRef<'d, AnyPin>, |
| 439 | d0: PeripheralRef<'d, AnyPin>, | 398 | d0: PeripheralRef<'d, AnyPin>, |
| @@ -442,22 +401,41 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 442 | d3: Option<PeripheralRef<'d, AnyPin>>, | 401 | d3: Option<PeripheralRef<'d, AnyPin>>, |
| 443 | config: Config, | 402 | config: Config, |
| 444 | ) -> Self { | 403 | ) -> Self { |
| 445 | into_ref!(sdmmc, irq); | 404 | into_ref!(sdmmc, irq, dma); |
| 446 | 405 | ||
| 447 | T::enable(); | 406 | T::enable(); |
| 448 | T::reset(); | 407 | T::reset(); |
| 449 | 408 | ||
| 450 | let inner = T::inner(); | ||
| 451 | unsafe { inner.new_inner() }; | ||
| 452 | |||
| 453 | irq.set_handler(Self::on_interrupt); | 409 | irq.set_handler(Self::on_interrupt); |
| 454 | irq.unpend(); | 410 | irq.unpend(); |
| 455 | irq.enable(); | 411 | irq.enable(); |
| 456 | 412 | ||
| 413 | let regs = T::regs(); | ||
| 414 | unsafe { | ||
| 415 | regs.clkcr().write(|w| { | ||
| 416 | w.set_pwrsav(false); | ||
| 417 | w.set_negedge(false); | ||
| 418 | |||
| 419 | // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. | ||
| 420 | // See chip erratas for more details. | ||
| 421 | #[cfg(sdmmc_v1)] | ||
| 422 | w.set_hwfc_en(false); | ||
| 423 | #[cfg(sdmmc_v2)] | ||
| 424 | w.set_hwfc_en(true); | ||
| 425 | |||
| 426 | #[cfg(sdmmc_v1)] | ||
| 427 | w.set_clken(true); | ||
| 428 | }); | ||
| 429 | |||
| 430 | // Power off, writen 00: Clock to the card is stopped; | ||
| 431 | // D[7:0], CMD, and CK are driven high. | ||
| 432 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | ||
| 433 | } | ||
| 434 | |||
| 457 | Self { | 435 | Self { |
| 458 | _peri: sdmmc, | 436 | _peri: sdmmc, |
| 459 | irq, | 437 | irq, |
| 460 | dma: NoDma.into_ref(), | 438 | dma, |
| 461 | 439 | ||
| 462 | clk, | 440 | clk, |
| 463 | cmd, | 441 | cmd, |
| @@ -472,509 +450,80 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 472 | card: None, | 450 | card: None, |
| 473 | } | 451 | } |
| 474 | } | 452 | } |
| 475 | } | ||
| 476 | |||
| 477 | impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | ||
| 478 | #[inline(always)] | ||
| 479 | pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 480 | let inner = T::inner(); | ||
| 481 | let freq = freq.into(); | ||
| 482 | |||
| 483 | let bus_width = match self.d3.is_some() { | ||
| 484 | true => BusWidth::Four, | ||
| 485 | false => BusWidth::One, | ||
| 486 | }; | ||
| 487 | |||
| 488 | inner | ||
| 489 | .init_card( | ||
| 490 | freq, | ||
| 491 | bus_width, | ||
| 492 | &mut self.card, | ||
| 493 | &mut self.signalling, | ||
| 494 | T::kernel_clk(), | ||
| 495 | &mut self.clock, | ||
| 496 | T::state(), | ||
| 497 | self.config.data_transfer_timeout, | ||
| 498 | &mut *self.dma, | ||
| 499 | ) | ||
| 500 | .await | ||
| 501 | } | ||
| 502 | |||
| 503 | #[inline(always)] | ||
| 504 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 505 | let card_capacity = self.card()?.card_type; | ||
| 506 | let inner = T::inner(); | ||
| 507 | let state = T::state(); | ||
| 508 | |||
| 509 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 510 | let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 511 | inner | ||
| 512 | .read_block( | ||
| 513 | block_idx, | ||
| 514 | buf, | ||
| 515 | card_capacity, | ||
| 516 | state, | ||
| 517 | self.config.data_transfer_timeout, | ||
| 518 | &mut *self.dma, | ||
| 519 | ) | ||
| 520 | .await | ||
| 521 | } | ||
| 522 | |||
| 523 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 524 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 525 | let inner = T::inner(); | ||
| 526 | let state = T::state(); | ||
| 527 | |||
| 528 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 529 | let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 530 | inner | ||
| 531 | .write_block( | ||
| 532 | block_idx, | ||
| 533 | buf, | ||
| 534 | card, | ||
| 535 | state, | ||
| 536 | self.config.data_transfer_timeout, | ||
| 537 | &mut *self.dma, | ||
| 538 | ) | ||
| 539 | .await | ||
| 540 | } | ||
| 541 | |||
| 542 | /// Get a reference to the initialized card | ||
| 543 | /// | ||
| 544 | /// # Errors | ||
| 545 | /// | ||
| 546 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | ||
| 547 | /// has not previously succeeded | ||
| 548 | #[inline(always)] | ||
| 549 | pub fn card(&self) -> Result<&Card, Error> { | ||
| 550 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 551 | } | ||
| 552 | |||
| 553 | /// Get the current SDMMC bus clock | ||
| 554 | pub fn clock(&self) -> Hertz { | ||
| 555 | self.clock | ||
| 556 | } | ||
| 557 | |||
| 558 | #[inline(always)] | ||
| 559 | fn on_interrupt(_: *mut ()) { | ||
| 560 | let regs = T::inner(); | ||
| 561 | let state = T::state(); | ||
| 562 | |||
| 563 | regs.data_interrupts(false); | ||
| 564 | state.wake(); | ||
| 565 | } | ||
| 566 | } | ||
| 567 | |||
| 568 | impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { | ||
| 569 | fn drop(&mut self) { | ||
| 570 | self.irq.disable(); | ||
| 571 | let inner = T::inner(); | ||
| 572 | unsafe { inner.on_drop() }; | ||
| 573 | |||
| 574 | critical_section::with(|_| unsafe { | ||
| 575 | self.clk.set_as_disconnected(); | ||
| 576 | self.cmd.set_as_disconnected(); | ||
| 577 | self.d0.set_as_disconnected(); | ||
| 578 | if let Some(x) = &mut self.d1 { | ||
| 579 | x.set_as_disconnected(); | ||
| 580 | } | ||
| 581 | if let Some(x) = &mut self.d2 { | ||
| 582 | x.set_as_disconnected(); | ||
| 583 | } | ||
| 584 | if let Some(x) = &mut self.d3 { | ||
| 585 | x.set_as_disconnected(); | ||
| 586 | } | ||
| 587 | }); | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | pub struct SdmmcInner(pub(crate) RegBlock); | ||
| 592 | |||
| 593 | impl SdmmcInner { | ||
| 594 | /// # Safety | ||
| 595 | /// | ||
| 596 | /// Access to `regs` registers should be exclusive | ||
| 597 | unsafe fn new_inner(&self) { | ||
| 598 | let regs = self.0; | ||
| 599 | |||
| 600 | regs.clkcr().write(|w| { | ||
| 601 | w.set_pwrsav(false); | ||
| 602 | w.set_negedge(false); | ||
| 603 | |||
| 604 | // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. | ||
| 605 | // See chip erratas for more details. | ||
| 606 | #[cfg(sdmmc_v1)] | ||
| 607 | w.set_hwfc_en(false); | ||
| 608 | #[cfg(sdmmc_v2)] | ||
| 609 | w.set_hwfc_en(true); | ||
| 610 | |||
| 611 | #[cfg(sdmmc_v1)] | ||
| 612 | w.set_clken(true); | ||
| 613 | }); | ||
| 614 | |||
| 615 | // Power off, writen 00: Clock to the card is stopped; | ||
| 616 | // D[7:0], CMD, and CK are driven high. | ||
| 617 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | ||
| 618 | } | ||
| 619 | |||
| 620 | /// Initializes card (if present) and sets the bus at the | ||
| 621 | /// specified frequency. | ||
| 622 | #[allow(clippy::too_many_arguments)] | ||
| 623 | async fn init_card<T: Instance, Dma: SdmmcDma<T>>( | ||
| 624 | &self, | ||
| 625 | freq: Hertz, | ||
| 626 | bus_width: BusWidth, | ||
| 627 | old_card: &mut Option<Card>, | ||
| 628 | signalling: &mut Signalling, | ||
| 629 | ker_ck: Hertz, | ||
| 630 | clock: &mut Hertz, | ||
| 631 | waker_reg: &AtomicWaker, | ||
| 632 | data_transfer_timeout: u32, | ||
| 633 | dma: &mut Dma, | ||
| 634 | ) -> Result<(), Error> { | ||
| 635 | let regs = self.0; | ||
| 636 | |||
| 637 | // NOTE(unsafe) We have exclusive access to the peripheral | ||
| 638 | unsafe { | ||
| 639 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 640 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 641 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 642 | *clock = init_clock; | ||
| 643 | |||
| 644 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 645 | self.wait_idle(); | ||
| 646 | |||
| 647 | regs.clkcr().modify(|w| { | ||
| 648 | w.set_widbus(0); | ||
| 649 | w.set_clkdiv(clkdiv); | ||
| 650 | #[cfg(sdmmc_v1)] | ||
| 651 | w.set_bypass(_bypass); | ||
| 652 | }); | ||
| 653 | |||
| 654 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 655 | self.cmd(Cmd::idle(), false)?; | ||
| 656 | |||
| 657 | // Check if cards supports CMD8 (with pattern) | ||
| 658 | self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; | ||
| 659 | let r1 = regs.respr(0).read().cardstatus(); | ||
| 660 | |||
| 661 | let mut card = if r1 == 0x1AA { | ||
| 662 | // Card echoed back the pattern. Must be at least v2 | ||
| 663 | Card::default() | ||
| 664 | } else { | ||
| 665 | return Err(Error::UnsupportedCardVersion); | ||
| 666 | }; | ||
| 667 | |||
| 668 | let ocr = loop { | ||
| 669 | // Signal that next command is a app command | ||
| 670 | self.cmd(Cmd::app_cmd(0), false)?; // CMD55 | ||
| 671 | |||
| 672 | let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | ||
| 673 | | CmdAppOper::HIGH_CAPACITY as u32 | ||
| 674 | | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; | ||
| 675 | |||
| 676 | // Initialize card | ||
| 677 | match self.cmd(Cmd::app_op_cmd(arg), false) { | ||
| 678 | // ACMD41 | ||
| 679 | Ok(_) => (), | ||
| 680 | Err(Error::Crc) => (), | ||
| 681 | Err(err) => return Err(err), | ||
| 682 | } | ||
| 683 | let ocr: OCR = regs.respr(0).read().cardstatus().into(); | ||
| 684 | if !ocr.is_busy() { | ||
| 685 | // Power up done | ||
| 686 | break ocr; | ||
| 687 | } | ||
| 688 | }; | ||
| 689 | |||
| 690 | if ocr.high_capacity() { | ||
| 691 | // Card is SDHC or SDXC or SDUC | ||
| 692 | card.card_type = CardCapacity::SDHC; | ||
| 693 | } else { | ||
| 694 | card.card_type = CardCapacity::SDSC; | ||
| 695 | } | ||
| 696 | card.ocr = ocr; | ||
| 697 | |||
| 698 | self.cmd(Cmd::all_send_cid(), false)?; // CMD2 | ||
| 699 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 700 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 701 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 702 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 703 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 704 | card.cid = cid.into(); | ||
| 705 | |||
| 706 | self.cmd(Cmd::send_rel_addr(), false)?; | ||
| 707 | card.rca = regs.respr(0).read().cardstatus() >> 16; | ||
| 708 | |||
| 709 | self.cmd(Cmd::send_csd(card.rca << 16), false)?; | ||
| 710 | let csd0 = regs.respr(0).read().cardstatus() as u128; | ||
| 711 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 712 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 713 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 714 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 715 | card.csd = csd.into(); | ||
| 716 | |||
| 717 | self.select_card(Some(&card))?; | ||
| 718 | |||
| 719 | self.get_scr(&mut card, waker_reg, data_transfer_timeout, dma).await?; | ||
| 720 | |||
| 721 | // Set bus width | ||
| 722 | let (width, acmd_arg) = match bus_width { | ||
| 723 | BusWidth::Eight => unimplemented!(), | ||
| 724 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | ||
| 725 | _ => (BusWidth::One, 0), | ||
| 726 | }; | ||
| 727 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 728 | self.cmd(Cmd::cmd6(acmd_arg), false)?; | ||
| 729 | |||
| 730 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 731 | self.wait_idle(); | ||
| 732 | |||
| 733 | regs.clkcr().modify(|w| { | ||
| 734 | w.set_widbus(match width { | ||
| 735 | BusWidth::One => 0, | ||
| 736 | BusWidth::Four => 1, | ||
| 737 | BusWidth::Eight => 2, | ||
| 738 | _ => panic!("Invalid Bus Width"), | ||
| 739 | }) | ||
| 740 | }); | ||
| 741 | |||
| 742 | // Set Clock | ||
| 743 | if freq.0 <= 25_000_000 { | ||
| 744 | // Final clock frequency | ||
| 745 | self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; | ||
| 746 | } else { | ||
| 747 | // Switch to max clock for SDR12 | ||
| 748 | self.clkcr_set_clkdiv(25_000_000, width, ker_ck, clock)?; | ||
| 749 | } | ||
| 750 | |||
| 751 | // Read status | ||
| 752 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) | ||
| 753 | .await?; | ||
| 754 | |||
| 755 | if freq.0 > 25_000_000 { | ||
| 756 | // Switch to SDR25 | ||
| 757 | *signalling = self | ||
| 758 | .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout, dma) | ||
| 759 | .await?; | ||
| 760 | |||
| 761 | if *signalling == Signalling::SDR25 { | ||
| 762 | // Set final clock frequency | ||
| 763 | self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; | ||
| 764 | |||
| 765 | if self.read_status(&card)?.state() != CurrentState::Transfer { | ||
| 766 | return Err(Error::SignalingSwitchFailed); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | } | ||
| 770 | // Read status after signalling change | ||
| 771 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) | ||
| 772 | .await?; | ||
| 773 | old_card.replace(card); | ||
| 774 | } | ||
| 775 | |||
| 776 | Ok(()) | ||
| 777 | } | ||
| 778 | |||
| 779 | async fn read_block<T: Instance, Dma: SdmmcDma<T>>( | ||
| 780 | &self, | ||
| 781 | block_idx: u32, | ||
| 782 | buffer: &mut [u32; 128], | ||
| 783 | capacity: CardCapacity, | ||
| 784 | waker_reg: &AtomicWaker, | ||
| 785 | data_transfer_timeout: u32, | ||
| 786 | dma: &mut Dma, | ||
| 787 | ) -> Result<(), Error> { | ||
| 788 | // Always read 1 block of 512 bytes | ||
| 789 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 790 | let address = match capacity { | ||
| 791 | CardCapacity::SDSC => block_idx * 512, | ||
| 792 | _ => block_idx, | ||
| 793 | }; | ||
| 794 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 795 | |||
| 796 | let regs = self.0; | ||
| 797 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 798 | |||
| 799 | unsafe { | ||
| 800 | self.prepare_datapath_read(buffer as *mut [u32; 128], 512, 9, data_transfer_timeout, dma); | ||
| 801 | self.data_interrupts(true); | ||
| 802 | } | ||
| 803 | self.cmd(Cmd::read_single_block(address), true)?; | ||
| 804 | |||
| 805 | let res = poll_fn(|cx| { | ||
| 806 | waker_reg.register(cx.waker()); | ||
| 807 | let status = unsafe { regs.star().read() }; | ||
| 808 | |||
| 809 | if status.dcrcfail() { | ||
| 810 | return Poll::Ready(Err(Error::Crc)); | ||
| 811 | } else if status.dtimeout() { | ||
| 812 | return Poll::Ready(Err(Error::Timeout)); | ||
| 813 | } else if status.dataend() { | ||
| 814 | return Poll::Ready(Ok(())); | ||
| 815 | } | ||
| 816 | Poll::Pending | ||
| 817 | }) | ||
| 818 | .await; | ||
| 819 | self.clear_interrupt_flags(); | ||
| 820 | |||
| 821 | if res.is_ok() { | ||
| 822 | on_drop.defuse(); | ||
| 823 | self.stop_datapath(); | ||
| 824 | } | ||
| 825 | res | ||
| 826 | } | ||
| 827 | |||
| 828 | async fn write_block<T: Instance, Dma: SdmmcDma<T>>( | ||
| 829 | &self, | ||
| 830 | block_idx: u32, | ||
| 831 | buffer: &[u32; 128], | ||
| 832 | card: &mut Card, | ||
| 833 | waker_reg: &AtomicWaker, | ||
| 834 | data_transfer_timeout: u32, | ||
| 835 | dma: &mut Dma, | ||
| 836 | ) -> Result<(), Error> { | ||
| 837 | // Always read 1 block of 512 bytes | ||
| 838 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 839 | let address = match card.card_type { | ||
| 840 | CardCapacity::SDSC => block_idx * 512, | ||
| 841 | _ => block_idx, | ||
| 842 | }; | ||
| 843 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 844 | |||
| 845 | let regs = self.0; | ||
| 846 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 847 | |||
| 848 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 849 | #[cfg(sdmmc_v1)] | ||
| 850 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 851 | |||
| 852 | unsafe { | ||
| 853 | self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); | ||
| 854 | self.data_interrupts(true); | ||
| 855 | } | ||
| 856 | |||
| 857 | #[cfg(sdmmc_v2)] | ||
| 858 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 859 | |||
| 860 | let res = poll_fn(|cx| { | ||
| 861 | waker_reg.register(cx.waker()); | ||
| 862 | let status = unsafe { regs.star().read() }; | ||
| 863 | |||
| 864 | if status.dcrcfail() { | ||
| 865 | return Poll::Ready(Err(Error::Crc)); | ||
| 866 | } else if status.dtimeout() { | ||
| 867 | return Poll::Ready(Err(Error::Timeout)); | ||
| 868 | } else if status.dataend() { | ||
| 869 | return Poll::Ready(Ok(())); | ||
| 870 | } | ||
| 871 | Poll::Pending | ||
| 872 | }) | ||
| 873 | .await; | ||
| 874 | self.clear_interrupt_flags(); | ||
| 875 | |||
| 876 | match res { | ||
| 877 | Ok(_) => { | ||
| 878 | on_drop.defuse(); | ||
| 879 | self.stop_datapath(); | ||
| 880 | |||
| 881 | // TODO: Make this configurable | ||
| 882 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 883 | |||
| 884 | // Try to read card status (ACMD13) | ||
| 885 | while timeout > 0 { | ||
| 886 | match self.read_sd_status(card, waker_reg, data_transfer_timeout, dma).await { | ||
| 887 | Ok(_) => return Ok(()), | ||
| 888 | Err(Error::Timeout) => (), // Try again | ||
| 889 | Err(e) => return Err(e), | ||
| 890 | } | ||
| 891 | timeout -= 1; | ||
| 892 | } | ||
| 893 | Err(Error::SoftwareTimeout) | ||
| 894 | } | ||
| 895 | Err(e) => Err(e), | ||
| 896 | } | ||
| 897 | } | ||
| 898 | 453 | ||
| 899 | /// Data transfer is in progress | 454 | /// Data transfer is in progress |
| 900 | #[inline(always)] | 455 | #[inline(always)] |
| 901 | fn data_active(&self) -> bool { | 456 | fn data_active() -> bool { |
| 902 | let regs = self.0; | 457 | let regs = T::regs(); |
| 903 | 458 | ||
| 904 | // NOTE(unsafe) Atomic read with no side-effects | 459 | // NOTE(unsafe) Atomic read with no side-effects |
| 905 | unsafe { | 460 | unsafe { |
| 906 | let status = regs.star().read(); | 461 | let status = regs.star().read(); |
| 907 | cfg_if::cfg_if! { | 462 | #[cfg(sdmmc_v1)] |
| 908 | if #[cfg(sdmmc_v1)] { | 463 | return status.rxact() || status.txact(); |
| 909 | status.rxact() || status.txact() | 464 | #[cfg(sdmmc_v2)] |
| 910 | } else if #[cfg(sdmmc_v2)] { | 465 | return status.dpsmact(); |
| 911 | status.dpsmact() | ||
| 912 | } | ||
| 913 | } | ||
| 914 | } | 466 | } |
| 915 | } | 467 | } |
| 916 | 468 | ||
| 917 | /// Coammand transfer is in progress | 469 | /// Coammand transfer is in progress |
| 918 | #[inline(always)] | 470 | #[inline(always)] |
| 919 | fn cmd_active(&self) -> bool { | 471 | fn cmd_active() -> bool { |
| 920 | let regs = self.0; | 472 | let regs = T::regs(); |
| 921 | 473 | ||
| 922 | // NOTE(unsafe) Atomic read with no side-effects | 474 | // NOTE(unsafe) Atomic read with no side-effects |
| 923 | unsafe { | 475 | unsafe { |
| 924 | let status = regs.star().read(); | 476 | let status = regs.star().read(); |
| 925 | cfg_if::cfg_if! { | 477 | #[cfg(sdmmc_v1)] |
| 926 | if #[cfg(sdmmc_v1)] { | 478 | return status.cmdact(); |
| 927 | status.cmdact() | 479 | #[cfg(sdmmc_v2)] |
| 928 | } else if #[cfg(sdmmc_v2)] { | 480 | return status.cpsmact(); |
| 929 | status.cpsmact() | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | 481 | } |
| 933 | } | 482 | } |
| 934 | 483 | ||
| 935 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) | 484 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) |
| 936 | #[inline(always)] | 485 | #[inline(always)] |
| 937 | fn wait_idle(&self) { | 486 | fn wait_idle() { |
| 938 | while self.data_active() || self.cmd_active() {} | 487 | while Self::data_active() || Self::cmd_active() {} |
| 939 | } | 488 | } |
| 940 | 489 | ||
| 941 | /// # Safety | 490 | /// # Safety |
| 942 | /// | 491 | /// |
| 943 | /// `buffer` must be valid for the whole transfer and word aligned | 492 | /// `buffer` must be valid for the whole transfer and word aligned |
| 944 | unsafe fn prepare_datapath_read<T: Instance, Dma: SdmmcDma<T>>( | 493 | unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { |
| 945 | &self, | ||
| 946 | buffer: *mut [u32], | ||
| 947 | length_bytes: u32, | ||
| 948 | block_size: u8, | ||
| 949 | data_transfer_timeout: u32, | ||
| 950 | #[allow(unused_variables)] dma: &mut Dma, | ||
| 951 | ) { | ||
| 952 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 494 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); |
| 953 | let regs = self.0; | 495 | let regs = T::regs(); |
| 954 | 496 | ||
| 955 | // Command AND Data state machines must be idle | 497 | // Command AND Data state machines must be idle |
| 956 | self.wait_idle(); | 498 | Self::wait_idle(); |
| 957 | self.clear_interrupt_flags(); | 499 | Self::clear_interrupt_flags(); |
| 958 | 500 | ||
| 959 | // NOTE(unsafe) We have exclusive access to the regisers | 501 | // NOTE(unsafe) We have exclusive access to the regisers |
| 960 | 502 | ||
| 961 | regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); | 503 | regs.dtimer() |
| 504 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 962 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 505 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); |
| 963 | 506 | ||
| 964 | cfg_if::cfg_if! { | 507 | #[cfg(sdmmc_v1)] |
| 965 | if #[cfg(sdmmc_v1)] { | 508 | { |
| 966 | let request = dma.request(); | 509 | let request = self.dma.request(); |
| 967 | dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { | 510 | self.dma.start_read( |
| 511 | request, | ||
| 512 | regs.fifor().ptr() as *const u32, | ||
| 513 | buffer, | ||
| 514 | crate::dma::TransferOptions { | ||
| 968 | pburst: crate::dma::Burst::Incr4, | 515 | pburst: crate::dma::Burst::Incr4, |
| 969 | mburst: crate::dma::Burst::Incr4, | 516 | mburst: crate::dma::Burst::Incr4, |
| 970 | flow_ctrl: crate::dma::FlowControl::Peripheral, | 517 | flow_ctrl: crate::dma::FlowControl::Peripheral, |
| 971 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), | 518 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), |
| 972 | ..Default::default() | 519 | ..Default::default() |
| 973 | }); | 520 | }, |
| 974 | } else if #[cfg(sdmmc_v2)] { | 521 | ); |
| 975 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); | 522 | } |
| 976 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | 523 | #[cfg(sdmmc_v2)] |
| 977 | } | 524 | { |
| 525 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); | ||
| 526 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | ||
| 978 | } | 527 | } |
| 979 | 528 | ||
| 980 | regs.dctrl().modify(|w| { | 529 | regs.dctrl().modify(|w| { |
| @@ -991,40 +540,41 @@ impl SdmmcInner { | |||
| 991 | /// # Safety | 540 | /// # Safety |
| 992 | /// | 541 | /// |
| 993 | /// `buffer` must be valid for the whole transfer and word aligned | 542 | /// `buffer` must be valid for the whole transfer and word aligned |
| 994 | unsafe fn prepare_datapath_write<T: Instance, Dma: SdmmcDma<T>>( | 543 | unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { |
| 995 | &self, | ||
| 996 | buffer: *const [u32], | ||
| 997 | length_bytes: u32, | ||
| 998 | block_size: u8, | ||
| 999 | data_transfer_timeout: u32, | ||
| 1000 | #[allow(unused_variables)] dma: &mut Dma, | ||
| 1001 | ) { | ||
| 1002 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | 544 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); |
| 1003 | let regs = self.0; | 545 | let regs = T::regs(); |
| 1004 | 546 | ||
| 1005 | // Command AND Data state machines must be idle | 547 | // Command AND Data state machines must be idle |
| 1006 | self.wait_idle(); | 548 | Self::wait_idle(); |
| 1007 | self.clear_interrupt_flags(); | 549 | Self::clear_interrupt_flags(); |
| 1008 | 550 | ||
| 1009 | // NOTE(unsafe) We have exclusive access to the regisers | 551 | // NOTE(unsafe) We have exclusive access to the regisers |
| 1010 | 552 | ||
| 1011 | regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); | 553 | regs.dtimer() |
| 554 | .write(|w| w.set_datatime(self.config.data_transfer_timeout)); | ||
| 1012 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 555 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); |
| 1013 | 556 | ||
| 1014 | cfg_if::cfg_if! { | 557 | #[cfg(sdmmc_v1)] |
| 1015 | if #[cfg(sdmmc_v1)] { | 558 | { |
| 1016 | let request = dma.request(); | 559 | let request = self.dma.request(); |
| 1017 | dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { | 560 | self.dma.start_write( |
| 561 | request, | ||
| 562 | buffer, | ||
| 563 | regs.fifor().ptr() as *mut u32, | ||
| 564 | crate::dma::TransferOptions { | ||
| 1018 | pburst: crate::dma::Burst::Incr4, | 565 | pburst: crate::dma::Burst::Incr4, |
| 1019 | mburst: crate::dma::Burst::Incr4, | 566 | mburst: crate::dma::Burst::Incr4, |
| 1020 | flow_ctrl: crate::dma::FlowControl::Peripheral, | 567 | flow_ctrl: crate::dma::FlowControl::Peripheral, |
| 1021 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), | 568 | fifo_threshold: Some(crate::dma::FifoThreshold::Full), |
| 1022 | ..Default::default() | 569 | ..Default::default() |
| 1023 | }); | 570 | }, |
| 1024 | } else if #[cfg(sdmmc_v2)] { | 571 | ); |
| 1025 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *const u32 as u32)); | 572 | } |
| 1026 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | 573 | #[cfg(sdmmc_v2)] |
| 1027 | } | 574 | { |
| 575 | regs.idmabase0r() | ||
| 576 | .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); | ||
| 577 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | ||
| 1028 | } | 578 | } |
| 1029 | 579 | ||
| 1030 | regs.dctrl().modify(|w| { | 580 | regs.dctrl().modify(|w| { |
| @@ -1039,26 +589,23 @@ impl SdmmcInner { | |||
| 1039 | } | 589 | } |
| 1040 | 590 | ||
| 1041 | /// Stops the DMA datapath | 591 | /// Stops the DMA datapath |
| 1042 | fn stop_datapath(&self) { | 592 | fn stop_datapath() { |
| 1043 | let regs = self.0; | 593 | let regs = T::regs(); |
| 1044 | 594 | ||
| 1045 | unsafe { | 595 | unsafe { |
| 1046 | cfg_if::cfg_if! { | 596 | #[cfg(sdmmc_v1)] |
| 1047 | if #[cfg(sdmmc_v1)] { | 597 | regs.dctrl().modify(|w| { |
| 1048 | regs.dctrl().modify(|w| { | 598 | w.set_dmaen(false); |
| 1049 | w.set_dmaen(false); | 599 | w.set_dten(false); |
| 1050 | w.set_dten(false); | 600 | }); |
| 1051 | }); | 601 | #[cfg(sdmmc_v2)] |
| 1052 | } else if #[cfg(sdmmc_v2)] { | 602 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); |
| 1053 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 1054 | } | ||
| 1055 | } | ||
| 1056 | } | 603 | } |
| 1057 | } | 604 | } |
| 1058 | 605 | ||
| 1059 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self | 606 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self |
| 1060 | fn clkcr_set_clkdiv(&self, freq: u32, width: BusWidth, ker_ck: Hertz, clock: &mut Hertz) -> Result<(), Error> { | 607 | fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { |
| 1061 | let regs = self.0; | 608 | let regs = T::regs(); |
| 1062 | 609 | ||
| 1063 | let width_u32 = match width { | 610 | let width_u32 = match width { |
| 1064 | BusWidth::One => 1u32, | 611 | BusWidth::One => 1u32, |
| @@ -1067,18 +614,19 @@ impl SdmmcInner { | |||
| 1067 | _ => panic!("Invalid Bus Width"), | 614 | _ => panic!("Invalid Bus Width"), |
| 1068 | }; | 615 | }; |
| 1069 | 616 | ||
| 617 | let ker_ck = T::kernel_clk(); | ||
| 1070 | let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; | 618 | let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; |
| 1071 | 619 | ||
| 1072 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | 620 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 |
| 1073 | // Section 55.5.8 | 621 | // Section 55.5.8 |
| 1074 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; | 622 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; |
| 1075 | assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); | 623 | assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); |
| 1076 | *clock = new_clock; | 624 | self.clock = new_clock; |
| 1077 | 625 | ||
| 1078 | // NOTE(unsafe) We have exclusive access to the regblock | 626 | // NOTE(unsafe) We have exclusive access to the regblock |
| 1079 | unsafe { | 627 | unsafe { |
| 1080 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | 628 | // CPSMACT and DPSMACT must be 0 to set CLKDIV |
| 1081 | self.wait_idle(); | 629 | Self::wait_idle(); |
| 1082 | regs.clkcr().modify(|w| { | 630 | regs.clkcr().modify(|w| { |
| 1083 | w.set_clkdiv(clkdiv); | 631 | w.set_clkdiv(clkdiv); |
| 1084 | #[cfg(sdmmc_v1)] | 632 | #[cfg(sdmmc_v1)] |
| @@ -1094,13 +642,7 @@ impl SdmmcInner { | |||
| 1094 | /// Attempt to set a new signalling mode. The selected | 642 | /// Attempt to set a new signalling mode. The selected |
| 1095 | /// signalling mode is returned. Expects the current clock | 643 | /// signalling mode is returned. Expects the current clock |
| 1096 | /// frequency to be > 12.5MHz. | 644 | /// frequency to be > 12.5MHz. |
| 1097 | async fn switch_signalling_mode<T: Instance, Dma: SdmmcDma<T>>( | 645 | async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> { |
| 1098 | &self, | ||
| 1099 | signalling: Signalling, | ||
| 1100 | waker_reg: &AtomicWaker, | ||
| 1101 | data_transfer_timeout: u32, | ||
| 1102 | dma: &mut Dma, | ||
| 1103 | ) -> Result<Signalling, Error> { | ||
| 1104 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | 646 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not |
| 1105 | // necessary" | 647 | // necessary" |
| 1106 | 648 | ||
| @@ -1117,17 +659,17 @@ impl SdmmcInner { | |||
| 1117 | let mut status = [0u32; 16]; | 659 | let mut status = [0u32; 16]; |
| 1118 | 660 | ||
| 1119 | // Arm `OnDrop` after the buffer, so it will be dropped first | 661 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1120 | let regs = self.0; | 662 | let regs = T::regs(); |
| 1121 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | 663 | let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); |
| 1122 | 664 | ||
| 1123 | unsafe { | 665 | unsafe { |
| 1124 | self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); | 666 | self.prepare_datapath_read(&mut status, 64, 6); |
| 1125 | self.data_interrupts(true); | 667 | Self::data_interrupts(true); |
| 1126 | } | 668 | } |
| 1127 | self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 | 669 | self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 |
| 1128 | 670 | ||
| 1129 | let res = poll_fn(|cx| { | 671 | let res = poll_fn(|cx| { |
| 1130 | waker_reg.register(cx.waker()); | 672 | T::state().register(cx.waker()); |
| 1131 | let status = unsafe { regs.star().read() }; | 673 | let status = unsafe { regs.star().read() }; |
| 1132 | 674 | ||
| 1133 | if status.dcrcfail() { | 675 | if status.dcrcfail() { |
| @@ -1140,7 +682,7 @@ impl SdmmcInner { | |||
| 1140 | Poll::Pending | 682 | Poll::Pending |
| 1141 | }) | 683 | }) |
| 1142 | .await; | 684 | .await; |
| 1143 | self.clear_interrupt_flags(); | 685 | Self::clear_interrupt_flags(); |
| 1144 | 686 | ||
| 1145 | // Host is allowed to use the new functions at least 8 | 687 | // Host is allowed to use the new functions at least 8 |
| 1146 | // clocks after the end of the switch command | 688 | // clocks after the end of the switch command |
| @@ -1153,7 +695,7 @@ impl SdmmcInner { | |||
| 1153 | match res { | 695 | match res { |
| 1154 | Ok(_) => { | 696 | Ok(_) => { |
| 1155 | on_drop.defuse(); | 697 | on_drop.defuse(); |
| 1156 | self.stop_datapath(); | 698 | Self::stop_datapath(); |
| 1157 | 699 | ||
| 1158 | // Function Selection of Function Group 1 | 700 | // Function Selection of Function Group 1 |
| 1159 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | 701 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; |
| @@ -1173,7 +715,7 @@ impl SdmmcInner { | |||
| 1173 | 715 | ||
| 1174 | /// Query the card status (CMD13, returns R1) | 716 | /// Query the card status (CMD13, returns R1) |
| 1175 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { | 717 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { |
| 1176 | let regs = self.0; | 718 | let regs = T::regs(); |
| 1177 | let rca = card.rca; | 719 | let rca = card.rca; |
| 1178 | 720 | ||
| 1179 | self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 | 721 | self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 |
| @@ -1184,31 +726,27 @@ impl SdmmcInner { | |||
| 1184 | } | 726 | } |
| 1185 | 727 | ||
| 1186 | /// Reads the SD Status (ACMD13) | 728 | /// Reads the SD Status (ACMD13) |
| 1187 | async fn read_sd_status<T: Instance, Dma: SdmmcDma<T>>( | 729 | async fn read_sd_status(&mut self) -> Result<(), Error> { |
| 1188 | &self, | 730 | let card = self.card.as_mut().ok_or(Error::NoCard)?; |
| 1189 | card: &mut Card, | ||
| 1190 | waker_reg: &AtomicWaker, | ||
| 1191 | data_transfer_timeout: u32, | ||
| 1192 | dma: &mut Dma, | ||
| 1193 | ) -> Result<(), Error> { | ||
| 1194 | let rca = card.rca; | 731 | let rca = card.rca; |
| 732 | |||
| 1195 | self.cmd(Cmd::set_block_length(64), false)?; // CMD16 | 733 | self.cmd(Cmd::set_block_length(64), false)?; // CMD16 |
| 1196 | self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP | 734 | self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP |
| 1197 | 735 | ||
| 1198 | let mut status = [0u32; 16]; | 736 | let mut status = [0u32; 16]; |
| 1199 | 737 | ||
| 1200 | // Arm `OnDrop` after the buffer, so it will be dropped first | 738 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1201 | let regs = self.0; | 739 | let regs = T::regs(); |
| 1202 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | 740 | let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); |
| 1203 | 741 | ||
| 1204 | unsafe { | 742 | unsafe { |
| 1205 | self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); | 743 | self.prepare_datapath_read(&mut status, 64, 6); |
| 1206 | self.data_interrupts(true); | 744 | Self::data_interrupts(true); |
| 1207 | } | 745 | } |
| 1208 | self.cmd(Cmd::card_status(0), true)?; | 746 | self.cmd(Cmd::card_status(0), true)?; |
| 1209 | 747 | ||
| 1210 | let res = poll_fn(|cx| { | 748 | let res = poll_fn(|cx| { |
| 1211 | waker_reg.register(cx.waker()); | 749 | T::state().register(cx.waker()); |
| 1212 | let status = unsafe { regs.star().read() }; | 750 | let status = unsafe { regs.star().read() }; |
| 1213 | 751 | ||
| 1214 | if status.dcrcfail() { | 752 | if status.dcrcfail() { |
| @@ -1221,16 +759,16 @@ impl SdmmcInner { | |||
| 1221 | Poll::Pending | 759 | Poll::Pending |
| 1222 | }) | 760 | }) |
| 1223 | .await; | 761 | .await; |
| 1224 | self.clear_interrupt_flags(); | 762 | Self::clear_interrupt_flags(); |
| 1225 | 763 | ||
| 1226 | if res.is_ok() { | 764 | if res.is_ok() { |
| 1227 | on_drop.defuse(); | 765 | on_drop.defuse(); |
| 1228 | self.stop_datapath(); | 766 | Self::stop_datapath(); |
| 1229 | 767 | ||
| 1230 | for byte in status.iter_mut() { | 768 | for byte in status.iter_mut() { |
| 1231 | *byte = u32::from_be(*byte); | 769 | *byte = u32::from_be(*byte); |
| 1232 | } | 770 | } |
| 1233 | card.status = status.into(); | 771 | self.card.as_mut().unwrap().status = status.into(); |
| 1234 | } | 772 | } |
| 1235 | res | 773 | res |
| 1236 | } | 774 | } |
| @@ -1252,8 +790,8 @@ impl SdmmcInner { | |||
| 1252 | 790 | ||
| 1253 | /// Clear flags in interrupt clear register | 791 | /// Clear flags in interrupt clear register |
| 1254 | #[inline(always)] | 792 | #[inline(always)] |
| 1255 | fn clear_interrupt_flags(&self) { | 793 | fn clear_interrupt_flags() { |
| 1256 | let regs = self.0; | 794 | let regs = T::regs(); |
| 1257 | // NOTE(unsafe) Atomic write | 795 | // NOTE(unsafe) Atomic write |
| 1258 | unsafe { | 796 | unsafe { |
| 1259 | regs.icr().write(|w| { | 797 | regs.icr().write(|w| { |
| @@ -1287,8 +825,8 @@ impl SdmmcInner { | |||
| 1287 | 825 | ||
| 1288 | /// Enables the interrupts for data transfer | 826 | /// Enables the interrupts for data transfer |
| 1289 | #[inline(always)] | 827 | #[inline(always)] |
| 1290 | fn data_interrupts(&self, enable: bool) { | 828 | fn data_interrupts(enable: bool) { |
| 1291 | let regs = self.0; | 829 | let regs = T::regs(); |
| 1292 | // NOTE(unsafe) Atomic write | 830 | // NOTE(unsafe) Atomic write |
| 1293 | unsafe { | 831 | unsafe { |
| 1294 | regs.maskr().write(|w| { | 832 | regs.maskr().write(|w| { |
| @@ -1302,13 +840,7 @@ impl SdmmcInner { | |||
| 1302 | } | 840 | } |
| 1303 | } | 841 | } |
| 1304 | 842 | ||
| 1305 | async fn get_scr<T: Instance, Dma: SdmmcDma<T>>( | 843 | async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { |
| 1306 | &self, | ||
| 1307 | card: &mut Card, | ||
| 1308 | waker_reg: &AtomicWaker, | ||
| 1309 | data_transfer_timeout: u32, | ||
| 1310 | dma: &mut Dma, | ||
| 1311 | ) -> Result<(), Error> { | ||
| 1312 | // Read the the 64-bit SCR register | 844 | // Read the the 64-bit SCR register |
| 1313 | self.cmd(Cmd::set_block_length(8), false)?; // CMD16 | 845 | self.cmd(Cmd::set_block_length(8), false)?; // CMD16 |
| 1314 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | 846 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; |
| @@ -1316,17 +848,17 @@ impl SdmmcInner { | |||
| 1316 | let mut scr = [0u32; 2]; | 848 | let mut scr = [0u32; 2]; |
| 1317 | 849 | ||
| 1318 | // Arm `OnDrop` after the buffer, so it will be dropped first | 850 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1319 | let regs = self.0; | 851 | let regs = T::regs(); |
| 1320 | let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); | 852 | let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); |
| 1321 | 853 | ||
| 1322 | unsafe { | 854 | unsafe { |
| 1323 | self.prepare_datapath_read(&mut scr as *mut [u32], 8, 3, data_transfer_timeout, dma); | 855 | self.prepare_datapath_read(&mut scr[..], 8, 3); |
| 1324 | self.data_interrupts(true); | 856 | Self::data_interrupts(true); |
| 1325 | } | 857 | } |
| 1326 | self.cmd(Cmd::cmd51(), true)?; | 858 | self.cmd(Cmd::cmd51(), true)?; |
| 1327 | 859 | ||
| 1328 | let res = poll_fn(|cx| { | 860 | let res = poll_fn(|cx| { |
| 1329 | waker_reg.register(cx.waker()); | 861 | T::state().register(cx.waker()); |
| 1330 | let status = unsafe { regs.star().read() }; | 862 | let status = unsafe { regs.star().read() }; |
| 1331 | 863 | ||
| 1332 | if status.dcrcfail() { | 864 | if status.dcrcfail() { |
| @@ -1339,11 +871,11 @@ impl SdmmcInner { | |||
| 1339 | Poll::Pending | 871 | Poll::Pending |
| 1340 | }) | 872 | }) |
| 1341 | .await; | 873 | .await; |
| 1342 | self.clear_interrupt_flags(); | 874 | Self::clear_interrupt_flags(); |
| 1343 | 875 | ||
| 1344 | if res.is_ok() { | 876 | if res.is_ok() { |
| 1345 | on_drop.defuse(); | 877 | on_drop.defuse(); |
| 1346 | self.stop_datapath(); | 878 | Self::stop_datapath(); |
| 1347 | 879 | ||
| 1348 | unsafe { | 880 | unsafe { |
| 1349 | let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); | 881 | let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); |
| @@ -1356,13 +888,13 @@ impl SdmmcInner { | |||
| 1356 | /// Send command to card | 888 | /// Send command to card |
| 1357 | #[allow(unused_variables)] | 889 | #[allow(unused_variables)] |
| 1358 | fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { | 890 | fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { |
| 1359 | let regs = self.0; | 891 | let regs = T::regs(); |
| 1360 | 892 | ||
| 1361 | self.clear_interrupt_flags(); | 893 | Self::clear_interrupt_flags(); |
| 1362 | // NOTE(safety) Atomic operations | 894 | // NOTE(safety) Atomic operations |
| 1363 | unsafe { | 895 | unsafe { |
| 1364 | // CP state machine must be idle | 896 | // CP state machine must be idle |
| 1365 | while self.cmd_active() {} | 897 | while Self::cmd_active() {} |
| 1366 | 898 | ||
| 1367 | // Command arg | 899 | // Command arg |
| 1368 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); | 900 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); |
| @@ -1411,13 +943,13 @@ impl SdmmcInner { | |||
| 1411 | /// # Safety | 943 | /// # Safety |
| 1412 | /// | 944 | /// |
| 1413 | /// Ensure that `regs` has exclusive access to the regblocks | 945 | /// Ensure that `regs` has exclusive access to the regblocks |
| 1414 | unsafe fn on_drop(&self) { | 946 | unsafe fn on_drop() { |
| 1415 | let regs = self.0; | 947 | let regs = T::regs(); |
| 1416 | if self.data_active() { | 948 | if Self::data_active() { |
| 1417 | self.clear_interrupt_flags(); | 949 | Self::clear_interrupt_flags(); |
| 1418 | // Send abort | 950 | // Send abort |
| 1419 | // CP state machine must be idle | 951 | // CP state machine must be idle |
| 1420 | while self.cmd_active() {} | 952 | while Self::cmd_active() {} |
| 1421 | 953 | ||
| 1422 | // Command arg | 954 | // Command arg |
| 1423 | regs.argr().write(|w| w.set_cmdarg(0)); | 955 | regs.argr().write(|w| w.set_cmdarg(0)); |
| @@ -1437,11 +969,320 @@ impl SdmmcInner { | |||
| 1437 | }); | 969 | }); |
| 1438 | 970 | ||
| 1439 | // Wait for the abort | 971 | // Wait for the abort |
| 1440 | while self.data_active() {} | 972 | while Self::data_active() {} |
| 1441 | } | 973 | } |
| 1442 | self.data_interrupts(false); | 974 | Self::data_interrupts(false); |
| 1443 | self.clear_interrupt_flags(); | 975 | Self::clear_interrupt_flags(); |
| 1444 | self.stop_datapath(); | 976 | Self::stop_datapath(); |
| 977 | } | ||
| 978 | |||
| 979 | /// Initializes card (if present) and sets the bus at the | ||
| 980 | /// specified frequency. | ||
| 981 | pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 982 | let regs = T::regs(); | ||
| 983 | let ker_ck = T::kernel_clk(); | ||
| 984 | |||
| 985 | let bus_width = match self.d3.is_some() { | ||
| 986 | true => BusWidth::Four, | ||
| 987 | false => BusWidth::One, | ||
| 988 | }; | ||
| 989 | |||
| 990 | // NOTE(unsafe) We have exclusive access to the peripheral | ||
| 991 | unsafe { | ||
| 992 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 993 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 994 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 995 | self.clock = init_clock; | ||
| 996 | |||
| 997 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 998 | Self::wait_idle(); | ||
| 999 | |||
| 1000 | regs.clkcr().modify(|w| { | ||
| 1001 | w.set_widbus(0); | ||
| 1002 | w.set_clkdiv(clkdiv); | ||
| 1003 | #[cfg(sdmmc_v1)] | ||
| 1004 | w.set_bypass(_bypass); | ||
| 1005 | }); | ||
| 1006 | |||
| 1007 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1008 | self.cmd(Cmd::idle(), false)?; | ||
| 1009 | |||
| 1010 | // Check if cards supports CMD8 (with pattern) | ||
| 1011 | self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; | ||
| 1012 | let r1 = regs.respr(0).read().cardstatus(); | ||
| 1013 | |||
| 1014 | let mut card = if r1 == 0x1AA { | ||
| 1015 | // Card echoed back the pattern. Must be at least v2 | ||
| 1016 | Card::default() | ||
| 1017 | } else { | ||
| 1018 | return Err(Error::UnsupportedCardVersion); | ||
| 1019 | }; | ||
| 1020 | |||
| 1021 | let ocr = loop { | ||
| 1022 | // Signal that next command is a app command | ||
| 1023 | self.cmd(Cmd::app_cmd(0), false)?; // CMD55 | ||
| 1024 | |||
| 1025 | let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | ||
| 1026 | | CmdAppOper::HIGH_CAPACITY as u32 | ||
| 1027 | | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; | ||
| 1028 | |||
| 1029 | // Initialize card | ||
| 1030 | match self.cmd(Cmd::app_op_cmd(arg), false) { | ||
| 1031 | // ACMD41 | ||
| 1032 | Ok(_) => (), | ||
| 1033 | Err(Error::Crc) => (), | ||
| 1034 | Err(err) => return Err(err), | ||
| 1035 | } | ||
| 1036 | let ocr: OCR = regs.respr(0).read().cardstatus().into(); | ||
| 1037 | if !ocr.is_busy() { | ||
| 1038 | // Power up done | ||
| 1039 | break ocr; | ||
| 1040 | } | ||
| 1041 | }; | ||
| 1042 | |||
| 1043 | if ocr.high_capacity() { | ||
| 1044 | // Card is SDHC or SDXC or SDUC | ||
| 1045 | card.card_type = CardCapacity::SDHC; | ||
| 1046 | } else { | ||
| 1047 | card.card_type = CardCapacity::SDSC; | ||
| 1048 | } | ||
| 1049 | card.ocr = ocr; | ||
| 1050 | |||
| 1051 | self.cmd(Cmd::all_send_cid(), false)?; // CMD2 | ||
| 1052 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1053 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1054 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1055 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1056 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1057 | card.cid = cid.into(); | ||
| 1058 | |||
| 1059 | self.cmd(Cmd::send_rel_addr(), false)?; | ||
| 1060 | card.rca = regs.respr(0).read().cardstatus() >> 16; | ||
| 1061 | |||
| 1062 | self.cmd(Cmd::send_csd(card.rca << 16), false)?; | ||
| 1063 | let csd0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1064 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1065 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1066 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1067 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1068 | card.csd = csd.into(); | ||
| 1069 | |||
| 1070 | self.select_card(Some(&card))?; | ||
| 1071 | |||
| 1072 | self.get_scr(&mut card).await?; | ||
| 1073 | |||
| 1074 | // Set bus width | ||
| 1075 | let (width, acmd_arg) = match bus_width { | ||
| 1076 | BusWidth::Eight => unimplemented!(), | ||
| 1077 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | ||
| 1078 | _ => (BusWidth::One, 0), | ||
| 1079 | }; | ||
| 1080 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 1081 | self.cmd(Cmd::cmd6(acmd_arg), false)?; | ||
| 1082 | |||
| 1083 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1084 | Self::wait_idle(); | ||
| 1085 | |||
| 1086 | regs.clkcr().modify(|w| { | ||
| 1087 | w.set_widbus(match width { | ||
| 1088 | BusWidth::One => 0, | ||
| 1089 | BusWidth::Four => 1, | ||
| 1090 | BusWidth::Eight => 2, | ||
| 1091 | _ => panic!("Invalid Bus Width"), | ||
| 1092 | }) | ||
| 1093 | }); | ||
| 1094 | |||
| 1095 | // Set Clock | ||
| 1096 | if freq.0 <= 25_000_000 { | ||
| 1097 | // Final clock frequency | ||
| 1098 | self.clkcr_set_clkdiv(freq.0, width)?; | ||
| 1099 | } else { | ||
| 1100 | // Switch to max clock for SDR12 | ||
| 1101 | self.clkcr_set_clkdiv(25_000_000, width)?; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | self.card = Some(card); | ||
| 1105 | |||
| 1106 | // Read status | ||
| 1107 | self.read_sd_status().await?; | ||
| 1108 | |||
| 1109 | if freq.0 > 25_000_000 { | ||
| 1110 | // Switch to SDR25 | ||
| 1111 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | ||
| 1112 | |||
| 1113 | if self.signalling == Signalling::SDR25 { | ||
| 1114 | // Set final clock frequency | ||
| 1115 | self.clkcr_set_clkdiv(freq.0, width)?; | ||
| 1116 | |||
| 1117 | if self.read_status(&card)?.state() != CurrentState::Transfer { | ||
| 1118 | return Err(Error::SignalingSwitchFailed); | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | } | ||
| 1122 | // Read status after signalling change | ||
| 1123 | self.read_sd_status().await?; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | Ok(()) | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | #[inline(always)] | ||
| 1130 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 1131 | let card_capacity = self.card()?.card_type; | ||
| 1132 | |||
| 1133 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1134 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1135 | |||
| 1136 | // Always read 1 block of 512 bytes | ||
| 1137 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1138 | let address = match card_capacity { | ||
| 1139 | CardCapacity::SDSC => block_idx * 512, | ||
| 1140 | _ => block_idx, | ||
| 1141 | }; | ||
| 1142 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 1143 | |||
| 1144 | let regs = T::regs(); | ||
| 1145 | let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); | ||
| 1146 | |||
| 1147 | unsafe { | ||
| 1148 | self.prepare_datapath_read(buffer, 512, 9); | ||
| 1149 | Self::data_interrupts(true); | ||
| 1150 | } | ||
| 1151 | self.cmd(Cmd::read_single_block(address), true)?; | ||
| 1152 | |||
| 1153 | let res = poll_fn(|cx| { | ||
| 1154 | T::state().register(cx.waker()); | ||
| 1155 | let status = unsafe { regs.star().read() }; | ||
| 1156 | |||
| 1157 | if status.dcrcfail() { | ||
| 1158 | return Poll::Ready(Err(Error::Crc)); | ||
| 1159 | } else if status.dtimeout() { | ||
| 1160 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1161 | } else if status.dataend() { | ||
| 1162 | return Poll::Ready(Ok(())); | ||
| 1163 | } | ||
| 1164 | Poll::Pending | ||
| 1165 | }) | ||
| 1166 | .await; | ||
| 1167 | Self::clear_interrupt_flags(); | ||
| 1168 | |||
| 1169 | if res.is_ok() { | ||
| 1170 | on_drop.defuse(); | ||
| 1171 | Self::stop_datapath(); | ||
| 1172 | } | ||
| 1173 | res | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 1177 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1178 | |||
| 1179 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1180 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 1181 | |||
| 1182 | // Always read 1 block of 512 bytes | ||
| 1183 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1184 | let address = match card.card_type { | ||
| 1185 | CardCapacity::SDSC => block_idx * 512, | ||
| 1186 | _ => block_idx, | ||
| 1187 | }; | ||
| 1188 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 1189 | |||
| 1190 | let regs = T::regs(); | ||
| 1191 | let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); | ||
| 1192 | |||
| 1193 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 1194 | #[cfg(sdmmc_v1)] | ||
| 1195 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 1196 | |||
| 1197 | unsafe { | ||
| 1198 | self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); | ||
| 1199 | Self::data_interrupts(true); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | #[cfg(sdmmc_v2)] | ||
| 1203 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 1204 | |||
| 1205 | let res = poll_fn(|cx| { | ||
| 1206 | T::state().register(cx.waker()); | ||
| 1207 | let status = unsafe { regs.star().read() }; | ||
| 1208 | |||
| 1209 | if status.dcrcfail() { | ||
| 1210 | return Poll::Ready(Err(Error::Crc)); | ||
| 1211 | } else if status.dtimeout() { | ||
| 1212 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1213 | } else if status.dataend() { | ||
| 1214 | return Poll::Ready(Ok(())); | ||
| 1215 | } | ||
| 1216 | Poll::Pending | ||
| 1217 | }) | ||
| 1218 | .await; | ||
| 1219 | Self::clear_interrupt_flags(); | ||
| 1220 | |||
| 1221 | match res { | ||
| 1222 | Ok(_) => { | ||
| 1223 | on_drop.defuse(); | ||
| 1224 | Self::stop_datapath(); | ||
| 1225 | |||
| 1226 | // TODO: Make this configurable | ||
| 1227 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1228 | |||
| 1229 | // Try to read card status (ACMD13) | ||
| 1230 | while timeout > 0 { | ||
| 1231 | match self.read_sd_status().await { | ||
| 1232 | Ok(_) => return Ok(()), | ||
| 1233 | Err(Error::Timeout) => (), // Try again | ||
| 1234 | Err(e) => return Err(e), | ||
| 1235 | } | ||
| 1236 | timeout -= 1; | ||
| 1237 | } | ||
| 1238 | Err(Error::SoftwareTimeout) | ||
| 1239 | } | ||
| 1240 | Err(e) => Err(e), | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | /// Get a reference to the initialized card | ||
| 1245 | /// | ||
| 1246 | /// # Errors | ||
| 1247 | /// | ||
| 1248 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | ||
| 1249 | /// has not previously succeeded | ||
| 1250 | #[inline(always)] | ||
| 1251 | pub fn card(&self) -> Result<&Card, Error> { | ||
| 1252 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | /// Get the current SDMMC bus clock | ||
| 1256 | pub fn clock(&self) -> Hertz { | ||
| 1257 | self.clock | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | #[inline(always)] | ||
| 1261 | fn on_interrupt(_: *mut ()) { | ||
| 1262 | Self::data_interrupts(false); | ||
| 1263 | T::state().wake(); | ||
| 1264 | } | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Drop for Sdmmc<'d, T, Dma> { | ||
| 1268 | fn drop(&mut self) { | ||
| 1269 | self.irq.disable(); | ||
| 1270 | unsafe { Self::on_drop() }; | ||
| 1271 | |||
| 1272 | critical_section::with(|_| unsafe { | ||
| 1273 | self.clk.set_as_disconnected(); | ||
| 1274 | self.cmd.set_as_disconnected(); | ||
| 1275 | self.d0.set_as_disconnected(); | ||
| 1276 | if let Some(x) = &mut self.d1 { | ||
| 1277 | x.set_as_disconnected(); | ||
| 1278 | } | ||
| 1279 | if let Some(x) = &mut self.d2 { | ||
| 1280 | x.set_as_disconnected(); | ||
| 1281 | } | ||
| 1282 | if let Some(x) = &mut self.d3 { | ||
| 1283 | x.set_as_disconnected(); | ||
| 1284 | } | ||
| 1285 | }); | ||
| 1445 | } | 1286 | } |
| 1446 | } | 1287 | } |
| 1447 | 1288 | ||
| @@ -1540,7 +1381,7 @@ pub(crate) mod sealed { | |||
| 1540 | pub trait Instance { | 1381 | pub trait Instance { |
| 1541 | type Interrupt: Interrupt; | 1382 | type Interrupt: Interrupt; |
| 1542 | 1383 | ||
| 1543 | fn inner() -> SdmmcInner; | 1384 | fn regs() -> RegBlock; |
| 1544 | fn state() -> &'static AtomicWaker; | 1385 | fn state() -> &'static AtomicWaker; |
| 1545 | fn kernel_clk() -> Hertz; | 1386 | fn kernel_clk() -> Hertz; |
| 1546 | } | 1387 | } |
| @@ -1560,15 +1401,14 @@ pin_trait!(D5Pin, Instance); | |||
| 1560 | pin_trait!(D6Pin, Instance); | 1401 | pin_trait!(D6Pin, Instance); |
| 1561 | pin_trait!(D7Pin, Instance); | 1402 | pin_trait!(D7Pin, Instance); |
| 1562 | 1403 | ||
| 1563 | cfg_if::cfg_if! { | 1404 | #[cfg(sdmmc_v1)] |
| 1564 | if #[cfg(sdmmc_v1)] { | 1405 | dma_trait!(SdmmcDma, Instance); |
| 1565 | dma_trait!(SdmmcDma, Instance); | 1406 | |
| 1566 | } else if #[cfg(sdmmc_v2)] { | 1407 | // SDMMCv2 uses internal DMA |
| 1567 | // SDMMCv2 uses internal DMA | 1408 | #[cfg(sdmmc_v2)] |
| 1568 | pub trait SdmmcDma<T: Instance> {} | 1409 | pub trait SdmmcDma<T: Instance> {} |
| 1569 | impl<T: Instance> SdmmcDma<T> for NoDma {} | 1410 | #[cfg(sdmmc_v2)] |
| 1570 | } | 1411 | impl<T: Instance> SdmmcDma<T> for NoDma {} |
| 1571 | } | ||
| 1572 | 1412 | ||
| 1573 | cfg_if::cfg_if! { | 1413 | cfg_if::cfg_if! { |
| 1574 | // TODO, these could not be implemented, because required clocks are not exposed in RCC: | 1414 | // TODO, these could not be implemented, because required clocks are not exposed in RCC: |
| @@ -1630,9 +1470,8 @@ foreach_peripheral!( | |||
| 1630 | impl sealed::Instance for peripherals::$inst { | 1470 | impl sealed::Instance for peripherals::$inst { |
| 1631 | type Interrupt = crate::interrupt::$inst; | 1471 | type Interrupt = crate::interrupt::$inst; |
| 1632 | 1472 | ||
| 1633 | fn inner() -> SdmmcInner { | 1473 | fn regs() -> RegBlock { |
| 1634 | const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); | 1474 | crate::pac::$inst |
| 1635 | INNER | ||
| 1636 | } | 1475 | } |
| 1637 | 1476 | ||
| 1638 | fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { | 1477 | fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { |
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index ebdfdb22d..eeecbd321 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -39,7 +39,18 @@ async fn main(_spawner: Spawner) { | |||
| 39 | // Should print 400kHz for initialization | 39 | // Should print 400kHz for initialization |
| 40 | info!("Configured clock: {}", sdmmc.clock().0); | 40 | info!("Configured clock: {}", sdmmc.clock().0); |
| 41 | 41 | ||
| 42 | unwrap!(sdmmc.init_card(mhz(48)).await); | 42 | let mut err = None; |
| 43 | loop { | ||
| 44 | match sdmmc.init_card(mhz(24)).await { | ||
| 45 | Ok(_) => break, | ||
| 46 | Err(e) => { | ||
| 47 | if err != Some(e) { | ||
| 48 | info!("waiting for card error, retrying: {:?}", e); | ||
| 49 | err = Some(e); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 43 | 54 | ||
| 44 | let card = unwrap!(sdmmc.card()); | 55 | let card = unwrap!(sdmmc.card()); |
| 45 | 56 | ||
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8b70a1015..3047c34ce 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill | 8 | stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill |
| 9 | stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo | 9 | stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo |
| 10 | stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo | 10 | stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo |
| 11 | stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo | 11 | stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo |
| 12 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo | 12 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo |
| @@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo | |||
| 15 | stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo | 15 | stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo |
| 16 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board | 16 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board |
| 17 | 17 | ||
| 18 | sdmmc = [] | ||
| 19 | |||
| 18 | [dependencies] | 20 | [dependencies] |
| 19 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } | 21 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 20 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 22 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| @@ -31,6 +33,45 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | |||
| 31 | embedded-hal-async = { version = "=0.2.0-alpha.1" } | 33 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 32 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | 34 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } |
| 33 | 35 | ||
| 36 | # BEGIN TESTS | ||
| 37 | # Generated by gen_test.py. DO NOT EDIT. | ||
| 38 | [[bin]] | ||
| 39 | name = "gpio" | ||
| 40 | path = "src/bin/gpio.rs" | ||
| 41 | required-features = [] | ||
| 42 | |||
| 43 | [[bin]] | ||
| 44 | name = "sdmmc" | ||
| 45 | path = "src/bin/sdmmc.rs" | ||
| 46 | required-features = [ "sdmmc",] | ||
| 47 | |||
| 48 | [[bin]] | ||
| 49 | name = "spi" | ||
| 50 | path = "src/bin/spi.rs" | ||
| 51 | required-features = [] | ||
| 52 | |||
| 53 | [[bin]] | ||
| 54 | name = "spi_dma" | ||
| 55 | path = "src/bin/spi_dma.rs" | ||
| 56 | required-features = [] | ||
| 57 | |||
| 58 | [[bin]] | ||
| 59 | name = "timer" | ||
| 60 | path = "src/bin/timer.rs" | ||
| 61 | required-features = [] | ||
| 62 | |||
| 63 | [[bin]] | ||
| 64 | name = "usart" | ||
| 65 | path = "src/bin/usart.rs" | ||
| 66 | required-features = [] | ||
| 67 | |||
| 68 | [[bin]] | ||
| 69 | name = "usart_dma" | ||
| 70 | path = "src/bin/usart_dma.rs" | ||
| 71 | required-features = [] | ||
| 72 | |||
| 73 | # END TESTS | ||
| 74 | |||
| 34 | [profile.dev] | 75 | [profile.dev] |
| 35 | debug = 2 | 76 | debug = 2 |
| 36 | debug-assertions = true | 77 | debug-assertions = true |
diff --git a/tests/stm32/gen_test.py b/tests/stm32/gen_test.py new file mode 100644 index 000000000..8ff156c0e --- /dev/null +++ b/tests/stm32/gen_test.py | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | import os | ||
| 2 | import toml | ||
| 3 | from glob import glob | ||
| 4 | |||
| 5 | abspath = os.path.abspath(__file__) | ||
| 6 | dname = os.path.dirname(abspath) | ||
| 7 | os.chdir(dname) | ||
| 8 | |||
| 9 | # ======= load test list | ||
| 10 | tests = {} | ||
| 11 | for f in sorted(glob('./src/bin/*.rs')): | ||
| 12 | name = os.path.splitext(os.path.basename(f))[0] | ||
| 13 | features = [] | ||
| 14 | with open(f, 'r') as f: | ||
| 15 | for line in f: | ||
| 16 | if line.startswith('// required-features:'): | ||
| 17 | features = line.split(':', 2)[1].strip().split(',') | ||
| 18 | |||
| 19 | tests[name] = features | ||
| 20 | |||
| 21 | # ========= Update Cargo.toml | ||
| 22 | |||
| 23 | things = { | ||
| 24 | 'bin': [ | ||
| 25 | { | ||
| 26 | 'name': f'{name}', | ||
| 27 | 'path': f'src/bin/{name}.rs', | ||
| 28 | 'required-features': features, | ||
| 29 | } | ||
| 30 | for name, features in tests.items() | ||
| 31 | ] | ||
| 32 | } | ||
| 33 | |||
| 34 | SEPARATOR_START = '# BEGIN TESTS\n' | ||
| 35 | SEPARATOR_END = '# END TESTS\n' | ||
| 36 | HELP = '# Generated by gen_test.py. DO NOT EDIT.\n' | ||
| 37 | with open('Cargo.toml', 'r') as f: | ||
| 38 | data = f.read() | ||
| 39 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 40 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 41 | data = before + SEPARATOR_START + HELP + \ | ||
| 42 | toml.dumps(things) + SEPARATOR_END + after | ||
| 43 | with open('Cargo.toml', 'w') as f: | ||
| 44 | f.write(data) | ||
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs new file mode 100644 index 000000000..c4e50cb4a --- /dev/null +++ b/tests/stm32/src/bin/sdmmc.rs | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | // required-features: sdmmc | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use defmt::{assert_eq, *}; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; | ||
| 9 | use embassy_stm32::time::mhz; | ||
| 10 | use embassy_stm32::{interrupt, Config}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let mut config = Config::default(); | ||
| 18 | config.rcc.sys_ck = Some(mhz(48)); | ||
| 19 | config.rcc.pll48 = true; | ||
| 20 | let p = embassy_stm32::init(config); | ||
| 21 | |||
| 22 | #[cfg(feature = "stm32f429zi")] | ||
| 23 | let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = ( | ||
| 24 | p.SDIO, | ||
| 25 | interrupt::take!(SDIO), | ||
| 26 | p.DMA2_CH3, | ||
| 27 | p.PC12, | ||
| 28 | p.PD2, | ||
| 29 | p.PC8, | ||
| 30 | p.PC9, | ||
| 31 | p.PC10, | ||
| 32 | p.PC11, | ||
| 33 | ); | ||
| 34 | |||
| 35 | // Arbitrary block index | ||
| 36 | let block_idx = 16; | ||
| 37 | |||
| 38 | let mut pattern1 = DataBlock([0u8; 512]); | ||
| 39 | let mut pattern2 = DataBlock([0u8; 512]); | ||
| 40 | for i in 0..512 { | ||
| 41 | pattern1[i] = i as u8; | ||
| 42 | pattern2[i] = !i as u8; | ||
| 43 | } | ||
| 44 | |||
| 45 | let mut block = DataBlock([0u8; 512]); | ||
| 46 | |||
| 47 | // ======== Try 4bit. ============== | ||
| 48 | info!("initializing in 4-bit mode..."); | ||
| 49 | let mut s = Sdmmc::new_4bit( | ||
| 50 | &mut sdmmc, | ||
| 51 | &mut irq, | ||
| 52 | &mut dma, | ||
| 53 | &mut clk, | ||
| 54 | &mut cmd, | ||
| 55 | &mut d0, | ||
| 56 | &mut d1, | ||
| 57 | &mut d2, | ||
| 58 | &mut d3, | ||
| 59 | Default::default(), | ||
| 60 | ); | ||
| 61 | |||
| 62 | let mut err = None; | ||
| 63 | loop { | ||
| 64 | match s.init_card(mhz(24)).await { | ||
| 65 | Ok(_) => break, | ||
| 66 | Err(e) => { | ||
| 67 | if err != Some(e) { | ||
| 68 | info!("waiting for card: {:?}", e); | ||
| 69 | err = Some(e); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | let card = unwrap!(s.card()); | ||
| 76 | |||
| 77 | info!("Card: {:#?}", Debug2Format(card)); | ||
| 78 | info!("Clock: {}", s.clock()); | ||
| 79 | |||
| 80 | info!("writing pattern1..."); | ||
| 81 | s.write_block(block_idx, &pattern1).await.unwrap(); | ||
| 82 | |||
| 83 | info!("reading..."); | ||
| 84 | s.read_block(block_idx, &mut block).await.unwrap(); | ||
| 85 | assert_eq!(block, pattern1); | ||
| 86 | |||
| 87 | info!("writing pattern2..."); | ||
| 88 | s.write_block(block_idx, &pattern2).await.unwrap(); | ||
| 89 | |||
| 90 | info!("reading..."); | ||
| 91 | s.read_block(block_idx, &mut block).await.unwrap(); | ||
| 92 | assert_eq!(block, pattern2); | ||
| 93 | |||
| 94 | drop(s); | ||
| 95 | |||
| 96 | // ======== Try 1bit. ============== | ||
| 97 | info!("initializing in 1-bit mode..."); | ||
| 98 | let mut s = Sdmmc::new_1bit( | ||
| 99 | &mut sdmmc, | ||
| 100 | &mut irq, | ||
| 101 | &mut dma, | ||
| 102 | &mut clk, | ||
| 103 | &mut cmd, | ||
| 104 | &mut d0, | ||
| 105 | Default::default(), | ||
| 106 | ); | ||
| 107 | |||
| 108 | let mut err = None; | ||
| 109 | loop { | ||
| 110 | match s.init_card(mhz(24)).await { | ||
| 111 | Ok(_) => break, | ||
| 112 | Err(e) => { | ||
| 113 | if err != Some(e) { | ||
| 114 | info!("waiting for card: {:?}", e); | ||
| 115 | err = Some(e); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | let card = unwrap!(s.card()); | ||
| 122 | |||
| 123 | info!("Card: {:#?}", Debug2Format(card)); | ||
| 124 | info!("Clock: {}", s.clock()); | ||
| 125 | |||
| 126 | info!("reading pattern2 written in 4bit mode..."); | ||
| 127 | s.read_block(block_idx, &mut block).await.unwrap(); | ||
| 128 | assert_eq!(block, pattern2); | ||
| 129 | |||
| 130 | info!("writing pattern1..."); | ||
| 131 | s.write_block(block_idx, &pattern1).await.unwrap(); | ||
| 132 | |||
| 133 | info!("reading..."); | ||
| 134 | s.read_block(block_idx, &mut block).await.unwrap(); | ||
| 135 | assert_eq!(block, pattern1); | ||
| 136 | |||
| 137 | info!("writing pattern2..."); | ||
| 138 | s.write_block(block_idx, &pattern2).await.unwrap(); | ||
| 139 | |||
| 140 | info!("reading..."); | ||
| 141 | s.read_block(block_idx, &mut block).await.unwrap(); | ||
| 142 | assert_eq!(block, pattern2); | ||
| 143 | |||
| 144 | drop(s); | ||
| 145 | |||
| 146 | info!("Test OK"); | ||
| 147 | cortex_m::asm::bkpt(); | ||
| 148 | } | ||
