diff options
| author | chemicstry <[email protected]> | 2023-02-23 16:57:21 +0200 |
|---|---|---|
| committer | chemicstry <[email protected]> | 2023-02-23 16:57:21 +0200 |
| commit | 42462681bd604750dfe8fa709453edf43c25b09d (patch) | |
| tree | 257d94018dcf6c869f799044974a34e35262f3d7 | |
| parent | dda5a4cc9dc25dba681aa7469e9d73fe0d20cce7 (diff) | |
stm32/sdmmc: Implement proper clock configuration
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 87 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 3 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/sdmmc.rs | 1 |
3 files changed, 75 insertions, 16 deletions
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3c99a0b6c..0bcd42bc2 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -140,10 +140,21 @@ cfg_if::cfg_if! { | |||
| 140 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 140 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 141 | /// `sdmmc_ck` in Hertz. | 141 | /// `sdmmc_ck` in Hertz. |
| 142 | /// | 142 | /// |
| 143 | /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register | 143 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), |
| 144 | /// value and `clk_f` is the resulting new clock frequency. | 144 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. |
| 145 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u8, Hertz), Error> { | 145 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { |
| 146 | let clk_div = match ker_ck.0 / sdmmc_ck { | 146 | // sdmmc_v1 maximum clock is 50 MHz |
| 147 | if sdmmc_ck > 50_000_000 { | ||
| 148 | return Err(Error::BadClock); | ||
| 149 | } | ||
| 150 | |||
| 151 | // bypass divisor | ||
| 152 | if ker_ck.0 <= sdmmc_ck { | ||
| 153 | return Ok((true, 0, ker_ck)); | ||
| 154 | } | ||
| 155 | |||
| 156 | // `ker_ck / sdmmc_ck` rounded up | ||
| 157 | let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | ||
| 147 | 0 | 1 => Ok(0), | 158 | 0 | 1 => Ok(0), |
| 148 | x @ 2..=258 => { | 159 | x @ 2..=258 => { |
| 149 | Ok((x - 2) as u8) | 160 | Ok((x - 2) as u8) |
| @@ -153,22 +164,24 @@ cfg_if::cfg_if! { | |||
| 153 | 164 | ||
| 154 | // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] | 165 | // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] |
| 155 | let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); | 166 | let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); |
| 156 | Ok((clk_div, clk_f)) | 167 | Ok((false, clk_div, clk_f)) |
| 157 | } | 168 | } |
| 158 | } else if #[cfg(sdmmc_v2)] { | 169 | } else if #[cfg(sdmmc_v2)] { |
| 159 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 170 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| 160 | /// `sdmmc_ck` in Hertz. | 171 | /// `sdmmc_ck` in Hertz. |
| 161 | /// | 172 | /// |
| 162 | /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register | 173 | /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), |
| 163 | /// value and `clk_f` is the resulting new clock frequency. | 174 | /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. |
| 164 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u16, Hertz), Error> { | 175 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { |
| 176 | // `ker_ck / sdmmc_ck` rounded up | ||
| 165 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | 177 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { |
| 166 | 0 | 1 => Ok((0, ker_ck)), | 178 | 0 | 1 => Ok((false, 0, ker_ck)), |
| 167 | x @ 2..=2046 => { | 179 | x @ 2..=2046 => { |
| 180 | // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] | ||
| 168 | let clk_div = ((x + 1) / 2) as u16; | 181 | let clk_div = ((x + 1) / 2) as u16; |
| 169 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | 182 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); |
| 170 | 183 | ||
| 171 | Ok((clk_div, clk)) | 184 | Ok((false, clk_div, clk)) |
| 172 | } | 185 | } |
| 173 | _ => Err(Error::BadClock), | 186 | _ => Err(Error::BadClock), |
| 174 | } | 187 | } |
| @@ -478,7 +491,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 478 | bus_width, | 491 | bus_width, |
| 479 | &mut self.card, | 492 | &mut self.card, |
| 480 | &mut self.signalling, | 493 | &mut self.signalling, |
| 481 | T::frequency(), | 494 | Self::kernel_clock(), |
| 482 | &mut self.clock, | 495 | &mut self.clock, |
| 483 | T::state(), | 496 | T::state(), |
| 484 | self.config.data_transfer_timeout, | 497 | self.config.data_transfer_timeout, |
| @@ -550,6 +563,44 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 550 | regs.data_interrupts(false); | 563 | regs.data_interrupts(false); |
| 551 | state.wake(); | 564 | state.wake(); |
| 552 | } | 565 | } |
| 566 | |||
| 567 | /// Returns kernel clock (SDIOCLK) for the SD-card facing domain | ||
| 568 | fn kernel_clock() -> Hertz { | ||
| 569 | cfg_if::cfg_if! { | ||
| 570 | // TODO, these could not be implemented, because required clocks are not exposed in RCC: | ||
| 571 | // - H7 uses pll1_q_ck or pll2_r_ck depending on SDMMCSEL | ||
| 572 | // - L1 uses pll48 | ||
| 573 | // - L4 uses clk48(pll48) | ||
| 574 | // - L4+, L5, U5 uses clk48(pll48) or PLLSAI3CLK(PLLP) depending on SDMMCSEL | ||
| 575 | if #[cfg(stm32f1)] { | ||
| 576 | // F1 uses AHB1(HCLK), which is correct in PAC | ||
| 577 | T::frequency() | ||
| 578 | } else if #[cfg(any(stm32f2, stm32f4))] { | ||
| 579 | // F2, F4 always use pll48 | ||
| 580 | critical_section::with(|_| unsafe { | ||
| 581 | crate::rcc::get_freqs().pll48 | ||
| 582 | }).expect("PLL48 is required for SDIO") | ||
| 583 | } else if #[cfg(stm32f7)] { | ||
| 584 | critical_section::with(|_| unsafe { | ||
| 585 | use core::any::TypeId; | ||
| 586 | let sdmmcsel = if TypeId::of::<T>() == TypeId::of::<crate::peripherals::SDMMC1>() { | ||
| 587 | crate::pac::RCC.dckcfgr2().read().sdmmc1sel() | ||
| 588 | } else { | ||
| 589 | crate::pac::RCC.dckcfgr2().read().sdmmc2sel() | ||
| 590 | }; | ||
| 591 | |||
| 592 | if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK { | ||
| 593 | crate::rcc::get_freqs().sys | ||
| 594 | } else { | ||
| 595 | crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC") | ||
| 596 | } | ||
| 597 | }) | ||
| 598 | } else { | ||
| 599 | // Use default peripheral clock and hope it works | ||
| 600 | T::frequency() | ||
| 601 | } | ||
| 602 | } | ||
| 603 | } | ||
| 553 | } | 604 | } |
| 554 | 605 | ||
| 555 | impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { | 606 | impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { |
| @@ -625,7 +676,7 @@ impl SdmmcInner { | |||
| 625 | unsafe { | 676 | unsafe { |
| 626 | // While the SD/SDIO card or eMMC is in identification mode, | 677 | // While the SD/SDIO card or eMMC is in identification mode, |
| 627 | // the SDMMC_CK frequency must be no more than 400 kHz. | 678 | // the SDMMC_CK frequency must be no more than 400 kHz. |
| 628 | let (clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | 679 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); |
| 629 | *clock = init_clock; | 680 | *clock = init_clock; |
| 630 | 681 | ||
| 631 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | 682 | // CPSMACT and DPSMACT must be 0 to set WIDBUS |
| @@ -634,6 +685,8 @@ impl SdmmcInner { | |||
| 634 | regs.clkcr().modify(|w| { | 685 | regs.clkcr().modify(|w| { |
| 635 | w.set_widbus(0); | 686 | w.set_widbus(0); |
| 636 | w.set_clkdiv(clkdiv); | 687 | w.set_clkdiv(clkdiv); |
| 688 | #[cfg(sdmmc_v1)] | ||
| 689 | w.set_bypass(_bypass); | ||
| 637 | }); | 690 | }); |
| 638 | 691 | ||
| 639 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | 692 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); |
| @@ -1052,7 +1105,8 @@ impl SdmmcInner { | |||
| 1052 | _ => panic!("Invalid Bus Width"), | 1105 | _ => panic!("Invalid Bus Width"), |
| 1053 | }; | 1106 | }; |
| 1054 | 1107 | ||
| 1055 | let (clkdiv, new_clock) = clk_div(ker_ck, freq)?; | 1108 | let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; |
| 1109 | |||
| 1056 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | 1110 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 |
| 1057 | // Section 55.5.8 | 1111 | // Section 55.5.8 |
| 1058 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; | 1112 | let sdmmc_bus_bandwidth = new_clock.0 * width_u32; |
| @@ -1063,7 +1117,11 @@ impl SdmmcInner { | |||
| 1063 | unsafe { | 1117 | unsafe { |
| 1064 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | 1118 | // CPSMACT and DPSMACT must be 0 to set CLKDIV |
| 1065 | self.wait_idle(); | 1119 | self.wait_idle(); |
| 1066 | regs.clkcr().modify(|w| w.set_clkdiv(clkdiv)); | 1120 | regs.clkcr().modify(|w| { |
| 1121 | w.set_clkdiv(clkdiv); | ||
| 1122 | #[cfg(sdmmc_v1)] | ||
| 1123 | w.set_bypass(_bypass); | ||
| 1124 | }); | ||
| 1067 | } | 1125 | } |
| 1068 | 1126 | ||
| 1069 | Ok(()) | 1127 | Ok(()) |
| @@ -1152,7 +1210,6 @@ impl SdmmcInner { | |||
| 1152 | } | 1210 | } |
| 1153 | 1211 | ||
| 1154 | /// Query the card status (CMD13, returns R1) | 1212 | /// Query the card status (CMD13, returns R1) |
| 1155 | /// | ||
| 1156 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { | 1213 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { |
| 1157 | let regs = self.0; | 1214 | let regs = self.0; |
| 1158 | let rca = card.rca; | 1215 | let rca = card.rca; |
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index b57e955f6..1d0e60cb8 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -17,6 +17,7 @@ const ALLOW_WRITES: bool = false; | |||
| 17 | async fn main(_spawner: Spawner) -> ! { | 17 | async fn main(_spawner: Spawner) -> ! { |
| 18 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 19 | config.rcc.sys_ck = Some(mhz(48)); | 19 | config.rcc.sys_ck = Some(mhz(48)); |
| 20 | config.rcc.pll48 = true; | ||
| 20 | let p = embassy_stm32::init(config); | 21 | let p = embassy_stm32::init(config); |
| 21 | info!("Hello World!"); | 22 | info!("Hello World!"); |
| 22 | 23 | ||
| @@ -38,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 38 | // Should print 400kHz for initialization | 39 | // Should print 400kHz for initialization |
| 39 | info!("Configured clock: {}", sdmmc.clock().0); | 40 | info!("Configured clock: {}", sdmmc.clock().0); |
| 40 | 41 | ||
| 41 | unwrap!(sdmmc.init_card(mhz(24)).await); | 42 | unwrap!(sdmmc.init_card(mhz(48)).await); |
| 42 | 43 | ||
| 43 | let card = unwrap!(sdmmc.card()); | 44 | let card = unwrap!(sdmmc.card()); |
| 44 | 45 | ||
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 3bf427eca..cf8128e27 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -13,6 +13,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 13 | async fn main(_spawner: Spawner) -> ! { | 13 | async fn main(_spawner: Spawner) -> ! { |
| 14 | let mut config = Config::default(); | 14 | let mut config = Config::default(); |
| 15 | config.rcc.sys_ck = Some(mhz(200)); | 15 | config.rcc.sys_ck = Some(mhz(200)); |
| 16 | config.rcc.pll48 = true; | ||
| 16 | let p = embassy_stm32::init(config); | 17 | let p = embassy_stm32::init(config); |
| 17 | 18 | ||
| 18 | info!("Hello World!"); | 19 | info!("Hello World!"); |
