aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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!");