aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchemicstry <[email protected]>2023-02-23 16:57:21 +0200
committerchemicstry <[email protected]>2023-02-23 16:57:21 +0200
commit42462681bd604750dfe8fa709453edf43c25b09d (patch)
tree257d94018dcf6c869f799044974a34e35262f3d7
parentdda5a4cc9dc25dba681aa7469e9d73fe0d20cce7 (diff)
stm32/sdmmc: Implement proper clock configuration
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs87
-rw-r--r--examples/stm32f4/src/bin/sdmmc.rs3
-rw-r--r--examples/stm32f7/src/bin/sdmmc.rs1
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
555impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { 606impl<'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;
17async fn main(_spawner: Spawner) -> ! { 17async 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 _};
13async fn main(_spawner: Spawner) -> ! { 13async 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!");