aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-02-23 16:22:31 +0000
committerGitHub <[email protected]>2023-02-23 16:22:31 +0000
commit3255e0a17200774a6b434e9e1bd2a0f2dc2be6bd (patch)
tree78d873da6ad939865a579218ce09c9ed4f940149
parentf0f92909c11c4f147e1d2fb3d32c7413a999affc (diff)
parent73ef85b7650eea65c2f52e570f26062dd8ec38d0 (diff)
Merge #1228
1228: stm32/sdmmc: Implement proper clock configuration r=chemicstry a=chemicstry This implements proper clock configuration for sdmmc based on chip family, because `RccPeripheral::frequency()` is almost always incorrect. This can't be fixed in PAC, because sdmmc uses two clock domains, one for memory bus and one for sd card. `RccPeripheral::frequency()` usually returns the memory bus clock, but SDIO clock calculations need sd card domain clock. Moreover, chips have multiple clock source selection bits, which makes this even more complicated. I'm not sure if it's worth implementing all this logic in `RccPeripheral::frequency()` instead of cfg's in sdmmc. Some chips (Lx, U5, H7) require RCC updates to expose required clocks. I didn't want to mash everything in a single PR so left a TODO comment. I also left a `T::frequency()` fallback, which seemed to work in H7 case even though the clock is most certainly incorrect. In addition, added support for clock divider bypass for sdmmc_v1, which allows reaching a maximum clock of 48 MHz. The peripheral theoretically supports up to 50 MHz, but for that ST recommends setting pll48 frequency to 50 MHz 🤔 Co-authored-by: chemicstry <[email protected]>
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs109
-rw-r--r--examples/stm32f4/src/bin/sdmmc.rs3
-rw-r--r--examples/stm32f7/src/bin/sdmmc.rs1
3 files changed, 97 insertions, 16 deletions
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 3c99a0b6c..03d24dcb1 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 T::kernel_clk(),
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,
@@ -625,7 +638,7 @@ impl SdmmcInner {
625 unsafe { 638 unsafe {
626 // While the SD/SDIO card or eMMC is in identification mode, 639 // While the SD/SDIO card or eMMC is in identification mode,
627 // the SDMMC_CK frequency must be no more than 400 kHz. 640 // 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)); 641 let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
629 *clock = init_clock; 642 *clock = init_clock;
630 643
631 // CPSMACT and DPSMACT must be 0 to set WIDBUS 644 // CPSMACT and DPSMACT must be 0 to set WIDBUS
@@ -634,6 +647,8 @@ impl SdmmcInner {
634 regs.clkcr().modify(|w| { 647 regs.clkcr().modify(|w| {
635 w.set_widbus(0); 648 w.set_widbus(0);
636 w.set_clkdiv(clkdiv); 649 w.set_clkdiv(clkdiv);
650 #[cfg(sdmmc_v1)]
651 w.set_bypass(_bypass);
637 }); 652 });
638 653
639 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); 654 regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
@@ -1052,7 +1067,8 @@ impl SdmmcInner {
1052 _ => panic!("Invalid Bus Width"), 1067 _ => panic!("Invalid Bus Width"),
1053 }; 1068 };
1054 1069
1055 let (clkdiv, new_clock) = clk_div(ker_ck, freq)?; 1070 let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?;
1071
1056 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 1072 // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7
1057 // Section 55.5.8 1073 // Section 55.5.8
1058 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; 1074 let sdmmc_bus_bandwidth = new_clock.0 * width_u32;
@@ -1063,7 +1079,11 @@ impl SdmmcInner {
1063 unsafe { 1079 unsafe {
1064 // CPSMACT and DPSMACT must be 0 to set CLKDIV 1080 // CPSMACT and DPSMACT must be 0 to set CLKDIV
1065 self.wait_idle(); 1081 self.wait_idle();
1066 regs.clkcr().modify(|w| w.set_clkdiv(clkdiv)); 1082 regs.clkcr().modify(|w| {
1083 w.set_clkdiv(clkdiv);
1084 #[cfg(sdmmc_v1)]
1085 w.set_bypass(_bypass);
1086 });
1067 } 1087 }
1068 1088
1069 Ok(()) 1089 Ok(())
@@ -1152,7 +1172,6 @@ impl SdmmcInner {
1152 } 1172 }
1153 1173
1154 /// Query the card status (CMD13, returns R1) 1174 /// Query the card status (CMD13, returns R1)
1155 ///
1156 fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { 1175 fn read_status(&self, card: &Card) -> Result<CardStatus, Error> {
1157 let regs = self.0; 1176 let regs = self.0;
1158 let rca = card.rca; 1177 let rca = card.rca;
@@ -1523,6 +1542,7 @@ pub(crate) mod sealed {
1523 1542
1524 fn inner() -> SdmmcInner; 1543 fn inner() -> SdmmcInner;
1525 fn state() -> &'static AtomicWaker; 1544 fn state() -> &'static AtomicWaker;
1545 fn kernel_clk() -> Hertz;
1526 } 1546 }
1527 1547
1528 pub trait Pins<T: Instance> {} 1548 pub trait Pins<T: Instance> {}
@@ -1550,6 +1570,61 @@ cfg_if::cfg_if! {
1550 } 1570 }
1551} 1571}
1552 1572
1573cfg_if::cfg_if! {
1574 // TODO, these could not be implemented, because required clocks are not exposed in RCC:
1575 // - H7 uses pll1_q_ck or pll2_r_ck depending on SDMMCSEL
1576 // - L1 uses pll48
1577 // - L4 uses clk48(pll48)
1578 // - L4+, L5, U5 uses clk48(pll48) or PLLSAI3CLK(PLLP) depending on SDMMCSEL
1579 if #[cfg(stm32f1)] {
1580 // F1 uses AHB1(HCLK), which is correct in PAC
1581 macro_rules! kernel_clk {
1582 ($inst:ident) => {
1583 <peripherals::$inst as crate::rcc::sealed::RccPeripheral>::frequency()
1584 }
1585 }
1586 } else if #[cfg(any(stm32f2, stm32f4))] {
1587 // F2, F4 always use pll48
1588 macro_rules! kernel_clk {
1589 ($inst:ident) => {
1590 critical_section::with(|_| unsafe {
1591 crate::rcc::get_freqs().pll48
1592 }).expect("PLL48 is required for SDIO")
1593 }
1594 }
1595 } else if #[cfg(stm32f7)] {
1596 macro_rules! kernel_clk {
1597 (SDMMC1) => {
1598 critical_section::with(|_| unsafe {
1599 let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc1sel();
1600 if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
1601 crate::rcc::get_freqs().sys
1602 } else {
1603 crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
1604 }
1605 })
1606 };
1607 (SDMMC2) => {
1608 critical_section::with(|_| unsafe {
1609 let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc2sel();
1610 if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
1611 crate::rcc::get_freqs().sys
1612 } else {
1613 crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
1614 }
1615 })
1616 };
1617 }
1618 } else {
1619 // Use default peripheral clock and hope it works
1620 macro_rules! kernel_clk {
1621 ($inst:ident) => {
1622 <peripherals::$inst as crate::rcc::sealed::RccPeripheral>::frequency()
1623 }
1624 }
1625 }
1626}
1627
1553foreach_peripheral!( 1628foreach_peripheral!(
1554 (sdmmc, $inst:ident) => { 1629 (sdmmc, $inst:ident) => {
1555 impl sealed::Instance for peripherals::$inst { 1630 impl sealed::Instance for peripherals::$inst {
@@ -1564,6 +1639,10 @@ foreach_peripheral!(
1564 static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); 1639 static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new();
1565 &WAKER 1640 &WAKER
1566 } 1641 }
1642
1643 fn kernel_clk() -> Hertz {
1644 kernel_clk!($inst)
1645 }
1567 } 1646 }
1568 1647
1569 impl Instance for peripherals::$inst {} 1648 impl Instance for peripherals::$inst {}
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!");