diff options
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 109 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 3 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/sdmmc.rs | 1 |
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 | ||
| 1573 | cfg_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 | |||
| 1553 | foreach_peripheral!( | 1628 | foreach_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; | |||
| 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!"); |
