diff options
Diffstat (limited to 'embassy-stm32/src/rcc')
| -rw-r--r-- | embassy-stm32/src/rcc/bd.rs | 140 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f247.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/l.rs | 17 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mco.rs | 37 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 250 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/n6.rs | 1066 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/u5.rs | 19 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/wba.rs | 9 |
9 files changed, 1472 insertions, 75 deletions
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 63fc195dd..219be208f 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | use core::sync::atomic::{compiler_fence, Ordering}; | 1 | #[cfg(not(stm32n6))] |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | 3 | ||
| 3 | use crate::pac::common::{Reg, RW}; | 4 | #[cfg(not(stm32n6))] |
| 5 | use crate::pac::common::{RW, Reg}; | ||
| 6 | #[cfg(backup_sram)] | ||
| 7 | use crate::pac::pwr::vals::Retention; | ||
| 4 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; | 8 | pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; |
| 5 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 6 | 10 | ||
| @@ -52,7 +56,7 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 52 | } | 56 | } |
| 53 | } | 57 | } |
| 54 | 58 | ||
| 55 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] | 59 | #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0, stm32n6)))] |
| 56 | type Bdcr = crate::pac::rcc::regs::Bdcr; | 60 | type Bdcr = crate::pac::rcc::regs::Bdcr; |
| 57 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 61 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 58 | type Bdcr = crate::pac::rcc::regs::Csr; | 62 | type Bdcr = crate::pac::rcc::regs::Csr; |
| @@ -62,19 +66,22 @@ type Bdcr = crate::pac::rcc::regs::Csr1; | |||
| 62 | #[cfg(any(stm32c0))] | 66 | #[cfg(any(stm32c0))] |
| 63 | fn unlock() {} | 67 | fn unlock() {} |
| 64 | 68 | ||
| 65 | #[cfg(not(any(stm32c0)))] | 69 | #[cfg(not(any(stm32c0, stm32n6)))] |
| 66 | fn unlock() { | 70 | fn unlock() { |
| 67 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] | 71 | #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))] |
| 68 | let cr = crate::pac::PWR.cr(); | 72 | let cr = crate::pac::PWR.cr(); |
| 69 | #[cfg(not(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba)))] | 73 | #[cfg(not(any( |
| 74 | stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32h5, stm32wba, stm32n6 | ||
| 75 | )))] | ||
| 70 | let cr = crate::pac::PWR.cr1(); | 76 | let cr = crate::pac::PWR.cr1(); |
| 71 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 77 | #[cfg(any(stm32u5, stm32h5, stm32wba, stm32n6))] |
| 72 | let cr = crate::pac::PWR.dbpcr(); | 78 | let cr = crate::pac::PWR.dbpcr(); |
| 73 | 79 | ||
| 74 | cr.modify(|w| w.set_dbp(true)); | 80 | cr.modify(|w| w.set_dbp(true)); |
| 75 | while !cr.read().dbp() {} | 81 | while !cr.read().dbp() {} |
| 76 | } | 82 | } |
| 77 | 83 | ||
| 84 | #[cfg(not(stm32n6))] | ||
| 78 | fn bdcr() -> Reg<Bdcr, RW> { | 85 | fn bdcr() -> Reg<Bdcr, RW> { |
| 79 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] | 86 | #[cfg(any(rtc_v2_l0, rtc_v2_l1))] |
| 80 | return crate::pac::RCC.csr(); | 87 | return crate::pac::RCC.csr(); |
| @@ -89,6 +96,8 @@ pub struct LsConfig { | |||
| 89 | pub rtc: RtcClockSource, | 96 | pub rtc: RtcClockSource, |
| 90 | pub lsi: bool, | 97 | pub lsi: bool, |
| 91 | pub lse: Option<LseConfig>, | 98 | pub lse: Option<LseConfig>, |
| 99 | #[cfg(backup_sram)] | ||
| 100 | pub enable_backup_sram: bool, | ||
| 92 | } | 101 | } |
| 93 | 102 | ||
| 94 | impl LsConfig { | 103 | impl LsConfig { |
| @@ -113,6 +122,8 @@ impl LsConfig { | |||
| 113 | peripherals_clocked: false, | 122 | peripherals_clocked: false, |
| 114 | }), | 123 | }), |
| 115 | lsi: false, | 124 | lsi: false, |
| 125 | #[cfg(backup_sram)] | ||
| 126 | enable_backup_sram: false, | ||
| 116 | } | 127 | } |
| 117 | } | 128 | } |
| 118 | 129 | ||
| @@ -121,6 +132,8 @@ impl LsConfig { | |||
| 121 | rtc: RtcClockSource::LSI, | 132 | rtc: RtcClockSource::LSI, |
| 122 | lsi: true, | 133 | lsi: true, |
| 123 | lse: None, | 134 | lse: None, |
| 135 | #[cfg(backup_sram)] | ||
| 136 | enable_backup_sram: false, | ||
| 124 | } | 137 | } |
| 125 | } | 138 | } |
| 126 | 139 | ||
| @@ -129,6 +142,8 @@ impl LsConfig { | |||
| 129 | rtc: RtcClockSource::DISABLE, | 142 | rtc: RtcClockSource::DISABLE, |
| 130 | lsi: false, | 143 | lsi: false, |
| 131 | lse: None, | 144 | lse: None, |
| 145 | #[cfg(backup_sram)] | ||
| 146 | enable_backup_sram: false, | ||
| 132 | } | 147 | } |
| 133 | } | 148 | } |
| 134 | } | 149 | } |
| @@ -140,6 +155,7 @@ impl Default for LsConfig { | |||
| 140 | } | 155 | } |
| 141 | 156 | ||
| 142 | impl LsConfig { | 157 | impl LsConfig { |
| 158 | #[cfg(not(stm32n6))] | ||
| 143 | pub(crate) fn init(&self) -> Option<Hertz> { | 159 | pub(crate) fn init(&self) -> Option<Hertz> { |
| 144 | let rtc_clk = match self.rtc { | 160 | let rtc_clk = match self.rtc { |
| 145 | RtcClockSource::LSI => { | 161 | RtcClockSource::LSI => { |
| @@ -175,14 +191,19 @@ impl LsConfig { | |||
| 175 | if self.lsi { | 191 | if self.lsi { |
| 176 | #[cfg(any(stm32u5, stm32h5, stm32wba))] | 192 | #[cfg(any(stm32u5, stm32h5, stm32wba))] |
| 177 | let csr = crate::pac::RCC.bdcr(); | 193 | let csr = crate::pac::RCC.bdcr(); |
| 178 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0)))] | 194 | #[cfg(stm32n6)] |
| 195 | let csr = crate::pac::RCC.sr(); | ||
| 196 | #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0, stm32n6)))] | ||
| 179 | let csr = crate::pac::RCC.csr(); | 197 | let csr = crate::pac::RCC.csr(); |
| 180 | #[cfg(any(stm32c0))] | 198 | #[cfg(stm32c0)] |
| 181 | let csr = crate::pac::RCC.csr2(); | 199 | let csr = crate::pac::RCC.csr2(); |
| 182 | 200 | ||
| 183 | #[cfg(not(any(rcc_wb, rcc_wba)))] | 201 | #[cfg(not(any(rcc_wb, rcc_wba, rcc_n6)))] |
| 184 | csr.modify(|w| w.set_lsion(true)); | 202 | csr.modify(|w| w.set_lsion(true)); |
| 185 | 203 | ||
| 204 | #[cfg(rcc_n6)] | ||
| 205 | crate::pac::RCC.cr().modify(|w| w.set_lsion(true)); | ||
| 206 | |||
| 186 | #[cfg(any(rcc_wb, rcc_wba))] | 207 | #[cfg(any(rcc_wb, rcc_wba))] |
| 187 | csr.modify(|w| w.set_lsi1on(true)); | 208 | csr.modify(|w| w.set_lsi1on(true)); |
| 188 | 209 | ||
| @@ -193,28 +214,77 @@ impl LsConfig { | |||
| 193 | while !csr.read().lsi1rdy() {} | 214 | while !csr.read().lsi1rdy() {} |
| 194 | } | 215 | } |
| 195 | 216 | ||
| 217 | // Enable backup regulator for peristent battery backed sram | ||
| 218 | #[cfg(backup_sram)] | ||
| 219 | { | ||
| 220 | unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED }; | ||
| 221 | |||
| 222 | crate::pac::PWR.bdcr().modify(|w| { | ||
| 223 | w.set_bren(match self.enable_backup_sram { | ||
| 224 | true => Retention::PRESERVED, | ||
| 225 | false => Retention::LOST, | ||
| 226 | }); | ||
| 227 | }); | ||
| 228 | |||
| 229 | // Wait for backup regulator voltage to stabilize | ||
| 230 | while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {} | ||
| 231 | } | ||
| 232 | |||
| 196 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. | 233 | // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. |
| 197 | // once set, changing it requires a backup domain reset. | 234 | // once set, changing it requires a backup domain reset. |
| 198 | // first check if the configuration matches what we want. | 235 | // first check if the configuration matches what we want. |
| 236 | // N6 has all the fields spread across multiple registers under RCC. | ||
| 199 | 237 | ||
| 200 | // check if it's already enabled and in the source we want. | 238 | // check if it's already enabled and in the source we want. |
| 239 | #[cfg(not(rcc_n6))] | ||
| 201 | let reg = bdcr().read(); | 240 | let reg = bdcr().read(); |
| 241 | #[cfg(rcc_n6)] | ||
| 242 | let reg = crate::pac::RCC.cr().read(); | ||
| 243 | #[cfg(rcc_n6)] | ||
| 244 | let apb4lenr = crate::pac::RCC.apb4lenr().read(); | ||
| 245 | #[cfg(rcc_n6)] | ||
| 246 | let ccipr7 = crate::pac::RCC.ccipr7().read(); | ||
| 247 | #[cfg(rcc_n6)] | ||
| 248 | let lsecfgr = crate::pac::RCC.lsecfgr().read(); | ||
| 249 | |||
| 202 | let mut ok = true; | 250 | let mut ok = true; |
| 203 | ok &= reg.rtcsel() == self.rtc; | 251 | #[cfg(not(rcc_n6))] |
| 204 | #[cfg(not(rcc_wba))] | 252 | { |
| 253 | ok &= reg.rtcsel() == self.rtc; | ||
| 254 | } | ||
| 255 | #[cfg(rcc_n6)] | ||
| 256 | { | ||
| 257 | ok &= ccipr7.rtcsel() == self.rtc; | ||
| 258 | } | ||
| 259 | #[cfg(not(any(rcc_wba, rcc_n6)))] | ||
| 205 | { | 260 | { |
| 206 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); | 261 | ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE); |
| 207 | } | 262 | } |
| 263 | #[cfg(rcc_n6)] | ||
| 264 | { | ||
| 265 | ok &= apb4lenr.rtcen() == (self.rtc != RtcClockSource::DISABLE); | ||
| 266 | } | ||
| 208 | ok &= reg.lseon() == lse_en; | 267 | ok &= reg.lseon() == lse_en; |
| 209 | ok &= reg.lsebyp() == lse_byp; | 268 | #[cfg(not(rcc_n6))] |
| 269 | { | ||
| 270 | ok &= reg.lsebyp() == lse_byp; | ||
| 271 | } | ||
| 272 | #[cfg(rcc_n6)] | ||
| 273 | { | ||
| 274 | ok &= lsecfgr.lsebyp() == lse_byp; | ||
| 275 | } | ||
| 210 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 276 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 211 | if let Some(lse_sysen) = lse_sysen { | 277 | if let Some(lse_sysen) = lse_sysen { |
| 212 | ok &= reg.lsesysen() == lse_sysen; | 278 | ok &= reg.lsesysen() == lse_sysen; |
| 213 | } | 279 | } |
| 214 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 280 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1, rcc_n6)))] |
| 215 | if let Some(lse_drv) = lse_drv { | 281 | if let Some(lse_drv) = lse_drv { |
| 216 | ok &= reg.lsedrv() == lse_drv.into(); | 282 | ok &= reg.lsedrv() == lse_drv.into(); |
| 217 | } | 283 | } |
| 284 | #[cfg(rcc_n6)] | ||
| 285 | if let Some(lse_drv) = lse_drv { | ||
| 286 | ok &= lsecfgr.lsedrv() == lse_drv.into(); | ||
| 287 | } | ||
| 218 | 288 | ||
| 219 | // if configuration is OK, we're done. | 289 | // if configuration is OK, we're done. |
| 220 | if ok { | 290 | if ok { |
| @@ -223,7 +293,7 @@ impl LsConfig { | |||
| 223 | } | 293 | } |
| 224 | 294 | ||
| 225 | // If not OK, reset backup domain and configure it. | 295 | // If not OK, reset backup domain and configure it. |
| 226 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0)))] | 296 | #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0, stm32n6)))] |
| 227 | { | 297 | { |
| 228 | bdcr().modify(|w| w.set_bdrst(true)); | 298 | bdcr().modify(|w| w.set_bdrst(true)); |
| 229 | bdcr().modify(|w| w.set_bdrst(false)); | 299 | bdcr().modify(|w| w.set_bdrst(false)); |
| @@ -236,7 +306,7 @@ impl LsConfig { | |||
| 236 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset | 306 | // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset |
| 237 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset | 307 | // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset |
| 238 | //#[cfg(any(stm32h5, stm32h7rs))] | 308 | //#[cfg(any(stm32h5, stm32h7rs))] |
| 239 | #[cfg(any(stm32h7rs))] | 309 | #[cfg(any(stm32h7rs, stm32n6))] |
| 240 | { | 310 | { |
| 241 | bdcr().modify(|w| w.set_vswrst(true)); | 311 | bdcr().modify(|w| w.set_vswrst(true)); |
| 242 | bdcr().modify(|w| w.set_vswrst(false)); | 312 | bdcr().modify(|w| w.set_vswrst(false)); |
| @@ -248,16 +318,31 @@ impl LsConfig { | |||
| 248 | } | 318 | } |
| 249 | 319 | ||
| 250 | if lse_en { | 320 | if lse_en { |
| 251 | bdcr().modify(|w| { | 321 | #[cfg(not(rcc_n6))] |
| 252 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 322 | { |
| 253 | if let Some(lse_drv) = lse_drv { | 323 | bdcr().modify(|w| { |
| 254 | w.set_lsedrv(lse_drv.into()); | 324 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] |
| 255 | } | 325 | if let Some(lse_drv) = lse_drv { |
| 256 | w.set_lsebyp(lse_byp); | 326 | w.set_lsedrv(lse_drv.into()); |
| 257 | w.set_lseon(true); | 327 | } |
| 258 | }); | 328 | w.set_lsebyp(lse_byp); |
| 329 | w.set_lseon(true); | ||
| 330 | }); | ||
| 331 | |||
| 332 | while !bdcr().read().lserdy() {} | ||
| 333 | } | ||
| 334 | #[cfg(rcc_n6)] | ||
| 335 | { | ||
| 336 | crate::pac::RCC.lsecfgr().modify(|w| { | ||
| 337 | if let Some(lse_drv) = lse_drv { | ||
| 338 | w.set_lsedrv(lse_drv.into()); | ||
| 339 | } | ||
| 340 | w.set_lsebyp(lse_byp); | ||
| 341 | }); | ||
| 342 | crate::pac::RCC.cr().modify(|w| w.set_lseon(true)); | ||
| 259 | 343 | ||
| 260 | while !bdcr().read().lserdy() {} | 344 | while !crate::pac::RCC.sr().read().lserdy() {} |
| 345 | } | ||
| 261 | 346 | ||
| 262 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | 347 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] |
| 263 | if let Some(lse_sysen) = lse_sysen { | 348 | if let Some(lse_sysen) = lse_sysen { |
| @@ -272,6 +357,7 @@ impl LsConfig { | |||
| 272 | } | 357 | } |
| 273 | 358 | ||
| 274 | if self.rtc != RtcClockSource::DISABLE { | 359 | if self.rtc != RtcClockSource::DISABLE { |
| 360 | #[cfg(not(rcc_n6))] | ||
| 275 | bdcr().modify(|w| { | 361 | bdcr().modify(|w| { |
| 276 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] | 362 | #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] |
| 277 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); | 363 | assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); |
| @@ -280,6 +366,12 @@ impl LsConfig { | |||
| 280 | w.set_rtcen(true); | 366 | w.set_rtcen(true); |
| 281 | w.set_rtcsel(self.rtc); | 367 | w.set_rtcsel(self.rtc); |
| 282 | }); | 368 | }); |
| 369 | |||
| 370 | #[cfg(rcc_n6)] | ||
| 371 | { | ||
| 372 | crate::pac::RCC.ccipr7().modify(|w| w.set_rtcsel(self.rtc)); | ||
| 373 | crate::pac::RCC.apb4lenr().modify(|w| w.set_rtcen(true)) | ||
| 374 | } | ||
| 283 | } | 375 | } |
| 284 | 376 | ||
| 285 | trace!("BDCR configured: {:08x}", bdcr().read().0); | 377 | trace!("BDCR configured: {:08x}", bdcr().read().0); |
diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 8f2e8db5f..d941054cd 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | 2 | ||
| 3 | #[cfg(any(stm32f4, stm32f7))] | ||
| 4 | use crate::pac::PWR; | ||
| 3 | #[cfg(any(stm32f413, stm32f423, stm32f412))] | 5 | #[cfg(any(stm32f413, stm32f423, stm32f412))] |
| 4 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; | 6 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; |
| 5 | pub use crate::pac::rcc::vals::{ | 7 | pub use crate::pac::rcc::vals::{ |
| 6 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, | 8 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 7 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 9 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 8 | }; | 10 | }; |
| 9 | #[cfg(any(stm32f4, stm32f7))] | ||
| 10 | use crate::pac::PWR; | ||
| 11 | use crate::pac::{FLASH, RCC}; | 11 | use crate::pac::{FLASH, RCC}; |
| 12 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | 13 | ||
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 331bab7a0..485edd390 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -597,7 +597,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 597 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 598 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 599 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 600 | _ => panic!("cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 600 | _ => panic!( |
| 601 | "cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 602 | clk_val | ||
| 603 | ), | ||
| 601 | }, | 604 | }, |
| 602 | None => Usbrefcksel::MHZ24, | 605 | None => Usbrefcksel::MHZ24, |
| 603 | }; | 606 | }; |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 81b89046e..0d668103c 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -135,7 +135,14 @@ pub const WPAN_DEFAULT: Config = Config { | |||
| 135 | apb1_pre: APBPrescaler::DIV1, | 135 | apb1_pre: APBPrescaler::DIV1, |
| 136 | apb2_pre: APBPrescaler::DIV1, | 136 | apb2_pre: APBPrescaler::DIV1, |
| 137 | 137 | ||
| 138 | mux: super::mux::ClockMux::default(), | 138 | mux: { |
| 139 | use crate::pac::rcc::vals::Rfwkpsel; | ||
| 140 | |||
| 141 | let mut mux = super::mux::ClockMux::default(); | ||
| 142 | |||
| 143 | mux.rfwkpsel = Rfwkpsel::LSE; | ||
| 144 | mux | ||
| 145 | }, | ||
| 139 | }; | 146 | }; |
| 140 | 147 | ||
| 141 | fn msi_enable(range: MSIRange) { | 148 | fn msi_enable(range: MSIRange) { |
| @@ -499,9 +506,9 @@ pub use pll::*; | |||
| 499 | 506 | ||
| 500 | #[cfg(any(stm32l0, stm32l1))] | 507 | #[cfg(any(stm32l0, stm32l1))] |
| 501 | mod pll { | 508 | mod pll { |
| 502 | use super::{pll_enable, PllInstance}; | 509 | use super::{PllInstance, pll_enable}; |
| 503 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 504 | use crate::pac::RCC; | 510 | use crate::pac::RCC; |
| 511 | pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; | ||
| 505 | use crate::time::Hertz; | 512 | use crate::time::Hertz; |
| 506 | 513 | ||
| 507 | #[derive(Clone, Copy)] | 514 | #[derive(Clone, Copy)] |
| @@ -563,11 +570,11 @@ mod pll { | |||
| 563 | 570 | ||
| 564 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] | 571 | #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] |
| 565 | mod pll { | 572 | mod pll { |
| 566 | use super::{pll_enable, PllInstance}; | 573 | use super::{PllInstance, pll_enable}; |
| 574 | use crate::pac::RCC; | ||
| 567 | pub use crate::pac::rcc::vals::{ | 575 | pub use crate::pac::rcc::vals::{ |
| 568 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, | 576 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, |
| 569 | }; | 577 | }; |
| 570 | use crate::pac::RCC; | ||
| 571 | use crate::time::Hertz; | 578 | use crate::time::Hertz; |
| 572 | 579 | ||
| 573 | #[derive(Clone, Copy)] | 580 | #[derive(Clone, Copy)] |
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 59ccc8cb5..0624fdf26 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -3,6 +3,7 @@ use core::marker::PhantomData; | |||
| 3 | use embassy_hal_internal::PeripheralType; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::gpio::{AfType, OutputType, Speed}; | 5 | use crate::gpio::{AfType, OutputType, Speed}; |
| 6 | use crate::pac::RCC; | ||
| 6 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] | 7 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] |
| 7 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | 8 | pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; |
| 8 | #[cfg(not(any( | 9 | #[cfg(not(any( |
| @@ -15,7 +16,8 @@ pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; | |||
| 15 | rcc_h7ab, | 16 | rcc_h7ab, |
| 16 | rcc_h7rm0433, | 17 | rcc_h7rm0433, |
| 17 | rcc_h7, | 18 | rcc_h7, |
| 18 | rcc_h7rs | 19 | rcc_h7rs, |
| 20 | rcc_n6 | ||
| 19 | )))] | 21 | )))] |
| 20 | pub use crate::pac::rcc::vals::Mcosel as McoSource; | 22 | pub use crate::pac::rcc::vals::Mcosel as McoSource; |
| 21 | #[cfg(any( | 23 | #[cfg(any( |
| @@ -28,11 +30,11 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 28 | rcc_h7ab, | 30 | rcc_h7ab, |
| 29 | rcc_h7rm0433, | 31 | rcc_h7rm0433, |
| 30 | rcc_h7, | 32 | rcc_h7, |
| 31 | rcc_h7rs | 33 | rcc_h7rs, |
| 34 | rcc_n6 | ||
| 32 | ))] | 35 | ))] |
| 33 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; | 36 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; |
| 34 | use crate::pac::RCC; | 37 | use crate::{Peri, peripherals}; |
| 35 | use crate::{peripherals, Peri}; | ||
| 36 | 38 | ||
| 37 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] | 39 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] |
| 38 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 40 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] |
| @@ -59,10 +61,12 @@ macro_rules! impl_peri { | |||
| 59 | type Source = $source; | 61 | type Source = $source; |
| 60 | 62 | ||
| 61 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { | 63 | unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { |
| 62 | #[cfg(not(any(stm32u5, stm32wba)))] | 64 | #[cfg(not(any(stm32u5, stm32wba, stm32n6)))] |
| 63 | let r = RCC.cfgr(); | 65 | let r = RCC.cfgr(); |
| 64 | #[cfg(any(stm32u5, stm32wba))] | 66 | #[cfg(any(stm32u5, stm32wba))] |
| 65 | let r = RCC.cfgr1(); | 67 | let r = RCC.cfgr1(); |
| 68 | #[cfg(any(stm32n6))] | ||
| 69 | let r = RCC.ccipr5(); | ||
| 66 | 70 | ||
| 67 | r.modify(|w| { | 71 | r.modify(|w| { |
| 68 | w.$set_source(source); | 72 | w.$set_source(source); |
| @@ -91,12 +95,29 @@ pub struct Mco<'d, T: McoInstance> { | |||
| 91 | 95 | ||
| 92 | impl<'d, T: McoInstance> Mco<'d, T> { | 96 | impl<'d, T: McoInstance> Mco<'d, T> { |
| 93 | /// Create a new MCO instance. | 97 | /// Create a new MCO instance. |
| 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { | 98 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, config: McoConfig) -> Self { |
| 95 | critical_section::with(|_| unsafe { | 99 | critical_section::with(|_| unsafe { |
| 96 | T::_apply_clock_settings(source, prescaler); | 100 | T::_apply_clock_settings(source, config.prescaler); |
| 97 | set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 101 | set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed)); |
| 98 | }); | 102 | }); |
| 99 | 103 | ||
| 100 | Self { phantom: PhantomData } | 104 | Self { phantom: PhantomData } |
| 101 | } | 105 | } |
| 102 | } | 106 | } |
| 107 | |||
| 108 | #[non_exhaustive] | ||
| 109 | pub struct McoConfig { | ||
| 110 | /// Master Clock Out prescaler | ||
| 111 | pub prescaler: McoPrescaler, | ||
| 112 | /// IO Drive Strength | ||
| 113 | pub speed: Speed, | ||
| 114 | } | ||
| 115 | |||
| 116 | impl Default for McoConfig { | ||
| 117 | fn default() -> Self { | ||
| 118 | Self { | ||
| 119 | prescaler: McoPrescaler::DIV1, | ||
| 120 | speed: Speed::VeryHigh, | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c41f81816..d25c922d8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #![allow(missing_docs)] // TODO | 4 | #![allow(missing_docs)] // TODO |
| 5 | 5 | ||
| 6 | use core::mem::MaybeUninit; | 6 | use core::mem::MaybeUninit; |
| 7 | use core::ops; | ||
| 7 | 8 | ||
| 8 | mod bd; | 9 | mod bd; |
| 9 | pub use bd::*; | 10 | pub use bd::*; |
| @@ -11,6 +12,7 @@ pub use bd::*; | |||
| 11 | #[cfg(any(mco, mco1, mco2))] | 12 | #[cfg(any(mco, mco1, mco2))] |
| 12 | mod mco; | 13 | mod mco; |
| 13 | use critical_section::CriticalSection; | 14 | use critical_section::CriticalSection; |
| 15 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 14 | #[cfg(any(mco, mco1, mco2))] | 16 | #[cfg(any(mco, mco1, mco2))] |
| 15 | pub use mco::*; | 17 | pub use mco::*; |
| 16 | 18 | ||
| @@ -28,12 +30,13 @@ pub use hsi48::*; | |||
| 28 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] | 30 | #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] |
| 29 | #[cfg_attr(stm32u5, path = "u5.rs")] | 31 | #[cfg_attr(stm32u5, path = "u5.rs")] |
| 30 | #[cfg_attr(stm32wba, path = "wba.rs")] | 32 | #[cfg_attr(stm32wba, path = "wba.rs")] |
| 33 | #[cfg_attr(stm32n6, path = "n6.rs")] | ||
| 31 | mod _version; | 34 | mod _version; |
| 32 | 35 | ||
| 33 | pub use _version::*; | 36 | pub use _version::*; |
| 34 | use stm32_metapac::RCC; | 37 | use stm32_metapac::RCC; |
| 35 | 38 | ||
| 36 | pub use crate::_generated::{mux, Clocks}; | 39 | pub use crate::_generated::{Clocks, mux}; |
| 37 | use crate::time::Hertz; | 40 | use crate::time::Hertz; |
| 38 | 41 | ||
| 39 | #[cfg(feature = "low-power")] | 42 | #[cfg(feature = "low-power")] |
| @@ -48,6 +51,12 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; | |||
| 48 | /// May be read without a critical section | 51 | /// May be read without a critical section |
| 49 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; | 52 | pub(crate) static mut REFCOUNT_STOP2: u32 = 0; |
| 50 | 53 | ||
| 54 | #[cfg(feature = "low-power")] | ||
| 55 | pub(crate) static mut RCC_CONFIG: Option<Config> = None; | ||
| 56 | |||
| 57 | #[cfg(backup_sram)] | ||
| 58 | pub(crate) static mut BKSRAM_RETAINED: bool = false; | ||
| 59 | |||
| 51 | #[cfg(not(feature = "_dual-core"))] | 60 | #[cfg(not(feature = "_dual-core"))] |
| 52 | /// Frozen clock frequencies | 61 | /// Frozen clock frequencies |
| 53 | /// | 62 | /// |
| @@ -104,6 +113,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo | |||
| 104 | unsafe { get_freqs() } | 113 | unsafe { get_freqs() } |
| 105 | } | 114 | } |
| 106 | 115 | ||
| 116 | #[cfg(feature = "low-power")] | ||
| 117 | fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 118 | match stop_mode { | ||
| 119 | StopMode::Standby => {} | ||
| 120 | StopMode::Stop2 => unsafe { | ||
| 121 | REFCOUNT_STOP2 += 1; | ||
| 122 | }, | ||
| 123 | StopMode::Stop1 => unsafe { | ||
| 124 | REFCOUNT_STOP1 += 1; | ||
| 125 | }, | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | #[cfg(feature = "low-power")] | ||
| 130 | fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | ||
| 131 | match stop_mode { | ||
| 132 | StopMode::Standby => {} | ||
| 133 | StopMode::Stop2 => unsafe { | ||
| 134 | REFCOUNT_STOP2 -= 1; | ||
| 135 | }, | ||
| 136 | StopMode::Stop1 => unsafe { | ||
| 137 | REFCOUNT_STOP1 -= 1; | ||
| 138 | }, | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 107 | pub(crate) trait SealedRccPeripheral { | 142 | pub(crate) trait SealedRccPeripheral { |
| 108 | fn frequency() -> Hertz; | 143 | fn frequency() -> Hertz; |
| 109 | #[allow(dead_code)] | 144 | #[allow(dead_code)] |
| @@ -134,14 +169,27 @@ pub(crate) struct RccInfo { | |||
| 134 | stop_mode: StopMode, | 169 | stop_mode: StopMode, |
| 135 | } | 170 | } |
| 136 | 171 | ||
| 172 | /// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered. | ||
| 173 | /// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. | ||
| 137 | #[cfg(feature = "low-power")] | 174 | #[cfg(feature = "low-power")] |
| 138 | #[allow(dead_code)] | 175 | #[allow(dead_code)] |
| 139 | pub(crate) enum StopMode { | 176 | #[derive(Debug, Clone, Copy, PartialEq, Default, defmt::Format)] |
| 140 | Standby, | 177 | pub enum StopMode { |
| 141 | Stop2, | 178 | #[default] |
| 179 | /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 | ||
| 142 | Stop1, | 180 | Stop1, |
| 181 | /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2 | ||
| 182 | Stop2, | ||
| 183 | /// Peripheral does not prevent chip from entering Stop | ||
| 184 | Standby, | ||
| 143 | } | 185 | } |
| 144 | 186 | ||
| 187 | #[cfg(feature = "low-power")] | ||
| 188 | type BusyRccPeripheral = BusyPeripheral<StopMode>; | ||
| 189 | |||
| 190 | #[cfg(not(feature = "low-power"))] | ||
| 191 | type BusyRccPeripheral = (); | ||
| 192 | |||
| 145 | impl RccInfo { | 193 | impl RccInfo { |
| 146 | /// Safety: | 194 | /// Safety: |
| 147 | /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit | 195 | /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit |
| @@ -194,17 +242,6 @@ impl RccInfo { | |||
| 194 | } | 242 | } |
| 195 | } | 243 | } |
| 196 | 244 | ||
| 197 | #[cfg(feature = "low-power")] | ||
| 198 | match self.stop_mode { | ||
| 199 | StopMode::Standby => {} | ||
| 200 | StopMode::Stop2 => unsafe { | ||
| 201 | REFCOUNT_STOP2 += 1; | ||
| 202 | }, | ||
| 203 | StopMode::Stop1 => unsafe { | ||
| 204 | REFCOUNT_STOP1 += 1; | ||
| 205 | }, | ||
| 206 | } | ||
| 207 | |||
| 208 | // set the xxxRST bit | 245 | // set the xxxRST bit |
| 209 | let reset_ptr = self.reset_ptr(); | 246 | let reset_ptr = self.reset_ptr(); |
| 210 | if let Some(reset_ptr) = reset_ptr { | 247 | if let Some(reset_ptr) = reset_ptr { |
| @@ -260,17 +297,6 @@ impl RccInfo { | |||
| 260 | } | 297 | } |
| 261 | } | 298 | } |
| 262 | 299 | ||
| 263 | #[cfg(feature = "low-power")] | ||
| 264 | match self.stop_mode { | ||
| 265 | StopMode::Standby => {} | ||
| 266 | StopMode::Stop2 => unsafe { | ||
| 267 | REFCOUNT_STOP2 -= 1; | ||
| 268 | }, | ||
| 269 | StopMode::Stop1 => unsafe { | ||
| 270 | REFCOUNT_STOP1 -= 1; | ||
| 271 | }, | ||
| 272 | } | ||
| 273 | |||
| 274 | // clear the xxxEN bit | 300 | // clear the xxxEN bit |
| 275 | let enable_ptr = self.enable_ptr(); | 301 | let enable_ptr = self.enable_ptr(); |
| 276 | unsafe { | 302 | unsafe { |
| @@ -279,14 +305,85 @@ impl RccInfo { | |||
| 279 | } | 305 | } |
| 280 | } | 306 | } |
| 281 | 307 | ||
| 308 | #[allow(dead_code)] | ||
| 309 | pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 310 | #[cfg(feature = "low-power")] | ||
| 311 | increment_stop_refcount(_cs, self.stop_mode); | ||
| 312 | } | ||
| 313 | |||
| 314 | #[allow(dead_code)] | ||
| 315 | fn increment_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 316 | #[cfg(all(stm32wlex, feature = "low-power"))] | ||
| 317 | match self.stop_mode { | ||
| 318 | StopMode::Stop1 | StopMode::Stop2 => increment_stop_refcount(_cs, StopMode::Stop2), | ||
| 319 | _ => {} | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | #[allow(dead_code)] | ||
| 324 | pub(crate) fn increment_stop_refcount(&self) { | ||
| 325 | #[cfg(feature = "low-power")] | ||
| 326 | critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) | ||
| 327 | } | ||
| 328 | |||
| 329 | #[allow(dead_code)] | ||
| 330 | pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 331 | #[cfg(feature = "low-power")] | ||
| 332 | decrement_stop_refcount(_cs, self.stop_mode); | ||
| 333 | } | ||
| 334 | |||
| 335 | #[allow(dead_code)] | ||
| 336 | fn decrement_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) { | ||
| 337 | #[cfg(all(stm32wlex, feature = "low-power"))] | ||
| 338 | match self.stop_mode { | ||
| 339 | StopMode::Stop1 | StopMode::Stop2 => decrement_stop_refcount(_cs, StopMode::Stop2), | ||
| 340 | _ => {} | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | #[allow(dead_code)] | ||
| 345 | pub(crate) fn decrement_stop_refcount(&self) { | ||
| 346 | #[cfg(feature = "low-power")] | ||
| 347 | critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) | ||
| 348 | } | ||
| 349 | |||
| 282 | // TODO: should this be `unsafe`? | 350 | // TODO: should this be `unsafe`? |
| 283 | pub(crate) fn enable_and_reset(&self) { | 351 | pub(crate) fn enable_and_reset(&self) { |
| 284 | critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) | 352 | critical_section::with(|cs| { |
| 353 | self.enable_and_reset_with_cs(cs); | ||
| 354 | self.increment_stop_refcount_with_cs(cs); | ||
| 355 | }) | ||
| 356 | } | ||
| 357 | |||
| 358 | #[allow(dead_code)] | ||
| 359 | pub(crate) fn enable_and_reset_without_stop(&self) { | ||
| 360 | critical_section::with(|cs| { | ||
| 361 | self.enable_and_reset_with_cs(cs); | ||
| 362 | self.increment_minimum_stop_refcount_with_cs(cs); | ||
| 363 | }) | ||
| 285 | } | 364 | } |
| 286 | 365 | ||
| 287 | // TODO: should this be `unsafe`? | 366 | // TODO: should this be `unsafe`? |
| 288 | pub(crate) fn disable(&self) { | 367 | pub(crate) fn disable(&self) { |
| 289 | critical_section::with(|cs| self.disable_with_cs(cs)) | 368 | critical_section::with(|cs| { |
| 369 | self.disable_with_cs(cs); | ||
| 370 | self.decrement_stop_refcount_with_cs(cs); | ||
| 371 | }) | ||
| 372 | } | ||
| 373 | |||
| 374 | // TODO: should this be `unsafe`? | ||
| 375 | #[allow(dead_code)] | ||
| 376 | pub(crate) fn disable_without_stop(&self) { | ||
| 377 | critical_section::with(|cs| { | ||
| 378 | self.disable_with_cs(cs); | ||
| 379 | self.decrement_minimum_stop_refcount_with_cs(cs); | ||
| 380 | }) | ||
| 381 | } | ||
| 382 | |||
| 383 | #[allow(dead_code)] | ||
| 384 | pub(crate) fn block_stop(&self) -> BusyRccPeripheral { | ||
| 385 | #[cfg(feature = "low-power")] | ||
| 386 | BusyPeripheral::new(self.stop_mode) | ||
| 290 | } | 387 | } |
| 291 | 388 | ||
| 292 | fn reset_ptr(&self) -> Option<*mut u32> { | 389 | fn reset_ptr(&self) -> Option<*mut u32> { |
| @@ -302,6 +399,60 @@ impl RccInfo { | |||
| 302 | } | 399 | } |
| 303 | } | 400 | } |
| 304 | 401 | ||
| 402 | pub(crate) trait StoppablePeripheral { | ||
| 403 | #[cfg(feature = "low-power")] | ||
| 404 | #[allow(dead_code)] | ||
| 405 | fn stop_mode(&self) -> StopMode; | ||
| 406 | } | ||
| 407 | |||
| 408 | #[cfg(feature = "low-power")] | ||
| 409 | impl StoppablePeripheral for StopMode { | ||
| 410 | fn stop_mode(&self) -> StopMode { | ||
| 411 | *self | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | impl<'a, T: StoppablePeripheral + PeripheralType> StoppablePeripheral for Peri<'a, T> { | ||
| 416 | #[cfg(feature = "low-power")] | ||
| 417 | fn stop_mode(&self) -> StopMode { | ||
| 418 | T::stop_mode(&self) | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { | ||
| 423 | peripheral: T, | ||
| 424 | } | ||
| 425 | |||
| 426 | impl<T: StoppablePeripheral> BusyPeripheral<T> { | ||
| 427 | pub fn new(peripheral: T) -> Self { | ||
| 428 | #[cfg(feature = "low-power")] | ||
| 429 | critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode())); | ||
| 430 | |||
| 431 | Self { peripheral } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | impl<T: StoppablePeripheral> Drop for BusyPeripheral<T> { | ||
| 436 | fn drop(&mut self) { | ||
| 437 | #[cfg(feature = "low-power")] | ||
| 438 | critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode())); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> { | ||
| 443 | type Target = T; | ||
| 444 | |||
| 445 | fn deref(&self) -> &Self::Target { | ||
| 446 | &self.peripheral | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | impl<T: StoppablePeripheral> ops::DerefMut for BusyPeripheral<T> { | ||
| 451 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 452 | &mut self.peripheral | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 305 | #[allow(unused)] | 456 | #[allow(unused)] |
| 306 | mod util { | 457 | mod util { |
| 307 | use crate::time::Hertz; | 458 | use crate::time::Hertz; |
| @@ -371,6 +522,16 @@ pub fn enable_and_reset<T: RccPeripheral>() { | |||
| 371 | T::RCC_INFO.enable_and_reset(); | 522 | T::RCC_INFO.enable_and_reset(); |
| 372 | } | 523 | } |
| 373 | 524 | ||
| 525 | /// Enables and resets peripheral `T` without incrementing the stop refcount. | ||
| 526 | /// | ||
| 527 | /// # Safety | ||
| 528 | /// | ||
| 529 | /// Peripheral must not be in use. | ||
| 530 | // TODO: should this be `unsafe`? | ||
| 531 | pub fn enable_and_reset_without_stop<T: RccPeripheral>() { | ||
| 532 | T::RCC_INFO.enable_and_reset_without_stop(); | ||
| 533 | } | ||
| 534 | |||
| 374 | /// Disables peripheral `T`. | 535 | /// Disables peripheral `T`. |
| 375 | /// | 536 | /// |
| 376 | /// # Safety | 537 | /// # Safety |
| @@ -390,7 +551,7 @@ pub fn disable<T: RccPeripheral>() { | |||
| 390 | /// | 551 | /// |
| 391 | /// This should only be called after `init`. | 552 | /// This should only be called after `init`. |
| 392 | #[cfg(not(feature = "_dual-core"))] | 553 | #[cfg(not(feature = "_dual-core"))] |
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | 554 | pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) { |
| 394 | critical_section::with(|cs| init_rcc(cs, config)) | 555 | critical_section::with(|cs| init_rcc(cs, config)) |
| 395 | } | 556 | } |
| 396 | 557 | ||
| @@ -404,8 +565,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | |||
| 404 | 565 | ||
| 405 | #[cfg(feature = "low-power")] | 566 | #[cfg(feature = "low-power")] |
| 406 | { | 567 | { |
| 568 | RCC_CONFIG = Some(config); | ||
| 407 | REFCOUNT_STOP2 = 0; | 569 | REFCOUNT_STOP2 = 0; |
| 408 | REFCOUNT_STOP1 = 0; | 570 | REFCOUNT_STOP1 = 0; |
| 409 | } | 571 | } |
| 410 | } | 572 | } |
| 411 | } | 573 | } |
| 574 | |||
| 575 | /// Calculate intermediate prescaler number used to calculate peripheral prescalers | ||
| 576 | /// | ||
| 577 | /// This function is intended to calculate a number indicating a minimum division | ||
| 578 | /// necessary to result in a frequency lower than the provided `freq_max`. | ||
| 579 | /// | ||
| 580 | /// The returned value indicates the `val + 1` divider is necessary to result in | ||
| 581 | /// the output frequency that is below the maximum provided. | ||
| 582 | /// | ||
| 583 | /// For example: | ||
| 584 | /// 0 = divider of 1 => no division necessary as the input frequency is below max | ||
| 585 | /// 1 = divider of 2 => division by 2 necessary | ||
| 586 | /// ... | ||
| 587 | /// | ||
| 588 | /// The provided max frequency is inclusive. So if `freq_in == freq_max` the result | ||
| 589 | /// will be 0, indicating that no division is necessary. To accomplish that we subtract | ||
| 590 | /// 1 from the input frequency so that the integer rounding plays in our favor. | ||
| 591 | /// | ||
| 592 | /// For example: | ||
| 593 | /// Let the input frequency be 110 and the max frequency be 55. | ||
| 594 | /// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 | ||
| 595 | /// which in reality will be rounded up to 4 as usually a 3 division is not available. | ||
| 596 | /// In either case the resulting frequency will be either 36 or 27 which is lower than | ||
| 597 | /// what we would want. The result should be 1. | ||
| 598 | /// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 | ||
| 599 | /// which will result in the correct 55. | ||
| 600 | #[allow(unused)] | ||
| 601 | pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { | ||
| 602 | freq_in.saturating_sub(1) / freq_max | ||
| 603 | } | ||
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs new file mode 100644 index 000000000..178ec57d4 --- /dev/null +++ b/embassy-stm32/src/rcc/n6.rs | |||
| @@ -0,0 +1,1066 @@ | |||
| 1 | use stm32_metapac::rcc::vals::{ | ||
| 2 | Cpusw, Cpusws, Hseext, Hsitrim, Icint, Icsel, Msifreqsel, Plldivm, Pllmodssdis, Pllpdiv, Pllsel, Syssw, Syssws, | ||
| 3 | Timpre, | ||
| 4 | }; | ||
| 5 | pub use stm32_metapac::rcc::vals::{ | ||
| 6 | Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Ppre as ApbPrescaler, | ||
| 7 | }; | ||
| 8 | |||
| 9 | use crate::pac::{PWR, RCC, SYSCFG}; | ||
| 10 | use crate::time::Hertz; | ||
| 11 | |||
| 12 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 13 | pub const LSE_FREQ: Hertz = Hertz(32_768); | ||
| 14 | |||
| 15 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 16 | pub enum HseMode { | ||
| 17 | /// crystal/ceramic oscillator | ||
| 18 | Oscillator, | ||
| 19 | /// oscillator bypassed with external clock (analog) | ||
| 20 | Bypass, | ||
| 21 | /// oscillator bypassed with external digital clock | ||
| 22 | BypassDigital, | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 26 | pub struct Hse { | ||
| 27 | /// HSE frequency. | ||
| 28 | pub freq: Hertz, | ||
| 29 | /// HSE oscillator mode. | ||
| 30 | pub mode: HseMode, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 34 | pub struct Hsi { | ||
| 35 | pub pre: HsiPrescaler, | ||
| 36 | pub trim: Hsitrim, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[derive(Clone, Copy, PartialEq)] | ||
| 40 | pub enum SupplyConfig { | ||
| 41 | Smps, | ||
| 42 | External, | ||
| 43 | } | ||
| 44 | |||
| 45 | #[derive(Clone, Copy, PartialEq)] | ||
| 46 | pub enum CpuClk { | ||
| 47 | Hse, | ||
| 48 | Ic1 { source: Icsel, divider: Icint }, | ||
| 49 | Msi, | ||
| 50 | Hsi, | ||
| 51 | } | ||
| 52 | |||
| 53 | impl CpuClk { | ||
| 54 | const fn to_bits(self) -> u8 { | ||
| 55 | match self { | ||
| 56 | Self::Hsi => 0x0, | ||
| 57 | Self::Msi => 0x1, | ||
| 58 | Self::Hse => 0x2, | ||
| 59 | Self::Ic1 { .. } => 0x3, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #[derive(Clone, Copy, PartialEq)] | ||
| 65 | pub struct IcConfig { | ||
| 66 | source: Icsel, | ||
| 67 | divider: Icint, | ||
| 68 | } | ||
| 69 | |||
| 70 | #[derive(Clone, Copy, PartialEq)] | ||
| 71 | pub enum SysClk { | ||
| 72 | Hse, | ||
| 73 | Ic2 { | ||
| 74 | ic2: IcConfig, | ||
| 75 | ic6: IcConfig, | ||
| 76 | ic11: IcConfig, | ||
| 77 | }, | ||
| 78 | Msi, | ||
| 79 | Hsi, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl SysClk { | ||
| 83 | const fn to_bits(self) -> u8 { | ||
| 84 | match self { | ||
| 85 | Self::Hsi => 0x0, | ||
| 86 | Self::Msi => 0x1, | ||
| 87 | Self::Hse => 0x2, | ||
| 88 | Self::Ic2 { .. } => 0x3, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | #[derive(Clone, Copy, PartialEq)] | ||
| 94 | pub struct Msi { | ||
| 95 | pub freq: Msifreqsel, | ||
| 96 | pub trim: u8, | ||
| 97 | } | ||
| 98 | |||
| 99 | #[derive(Clone, Copy, PartialEq)] | ||
| 100 | pub enum Pll { | ||
| 101 | Oscillator { | ||
| 102 | source: Pllsel, | ||
| 103 | divm: Plldivm, | ||
| 104 | fractional: u32, | ||
| 105 | divn: u16, | ||
| 106 | divp1: Pllpdiv, | ||
| 107 | divp2: Pllpdiv, | ||
| 108 | }, | ||
| 109 | Bypass { | ||
| 110 | source: Pllsel, | ||
| 111 | }, | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Configuration of the core clocks | ||
| 115 | #[non_exhaustive] | ||
| 116 | #[derive(Clone, Copy)] | ||
| 117 | pub struct Config { | ||
| 118 | pub hsi: Option<Hsi>, | ||
| 119 | pub hse: Option<Hse>, | ||
| 120 | pub msi: Option<Msi>, | ||
| 121 | pub lsi: bool, | ||
| 122 | pub lse: bool, | ||
| 123 | |||
| 124 | pub sys: SysClk, | ||
| 125 | pub cpu: CpuClk, | ||
| 126 | |||
| 127 | pub pll1: Option<Pll>, | ||
| 128 | pub pll2: Option<Pll>, | ||
| 129 | pub pll3: Option<Pll>, | ||
| 130 | pub pll4: Option<Pll>, | ||
| 131 | |||
| 132 | pub ahb: AhbPrescaler, | ||
| 133 | pub apb1: ApbPrescaler, | ||
| 134 | pub apb2: ApbPrescaler, | ||
| 135 | pub apb4: ApbPrescaler, | ||
| 136 | pub apb5: ApbPrescaler, | ||
| 137 | |||
| 138 | pub supply_config: SupplyConfig, | ||
| 139 | } | ||
| 140 | |||
| 141 | impl Config { | ||
| 142 | pub const fn new() -> Self { | ||
| 143 | Self { | ||
| 144 | hsi: Some(Hsi { | ||
| 145 | pre: HsiPrescaler::DIV1, | ||
| 146 | trim: HsiCalibration::from_bits(32), | ||
| 147 | }), | ||
| 148 | hse: None, | ||
| 149 | msi: None, | ||
| 150 | lsi: true, | ||
| 151 | lse: false, | ||
| 152 | sys: SysClk::Hsi, | ||
| 153 | cpu: CpuClk::Hsi, | ||
| 154 | pll1: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 155 | pll2: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 156 | pll3: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 157 | pll4: Some(Pll::Bypass { source: Pllsel::HSI }), | ||
| 158 | |||
| 159 | ahb: AhbPrescaler::DIV2, | ||
| 160 | apb1: ApbPrescaler::DIV1, | ||
| 161 | apb2: ApbPrescaler::DIV1, | ||
| 162 | apb4: ApbPrescaler::DIV1, | ||
| 163 | apb5: ApbPrescaler::DIV1, | ||
| 164 | |||
| 165 | supply_config: SupplyConfig::Smps, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | #[allow(dead_code)] | ||
| 171 | struct ClocksOutput { | ||
| 172 | cpuclk: Hertz, | ||
| 173 | sysclk: Hertz, | ||
| 174 | pclk_tim: Hertz, | ||
| 175 | ahb: Hertz, | ||
| 176 | apb1: Hertz, | ||
| 177 | apb2: Hertz, | ||
| 178 | apb4: Hertz, | ||
| 179 | apb5: Hertz, | ||
| 180 | } | ||
| 181 | |||
| 182 | struct ClocksInput { | ||
| 183 | hsi: Option<Hertz>, | ||
| 184 | msi: Option<Hertz>, | ||
| 185 | hse: Option<Hertz>, | ||
| 186 | } | ||
| 187 | |||
| 188 | fn init_clocks(config: Config, input: &ClocksInput) -> ClocksOutput { | ||
| 189 | // handle increasing dividers | ||
| 190 | debug!("configuring increasing pclk dividers"); | ||
| 191 | RCC.cfgr2().modify(|w| { | ||
| 192 | if config.apb1 > w.ppre1() { | ||
| 193 | debug!(" - APB1"); | ||
| 194 | w.set_ppre1(config.apb1); | ||
| 195 | } | ||
| 196 | if config.apb2 > w.ppre2() { | ||
| 197 | debug!(" - APB2"); | ||
| 198 | w.set_ppre2(config.apb2); | ||
| 199 | } | ||
| 200 | if config.apb4 > w.ppre4() { | ||
| 201 | debug!(" - APB4"); | ||
| 202 | w.set_ppre4(config.apb4); | ||
| 203 | } | ||
| 204 | if config.apb5 > w.ppre5() { | ||
| 205 | debug!(" - APB5"); | ||
| 206 | w.set_ppre5(config.apb5); | ||
| 207 | } | ||
| 208 | if config.ahb > w.hpre() { | ||
| 209 | debug!(" - AHB"); | ||
| 210 | w.set_hpre(config.ahb); | ||
| 211 | } | ||
| 212 | }); | ||
| 213 | // cpuclk | ||
| 214 | debug!("configuring cpuclk"); | ||
| 215 | match config.cpu { | ||
| 216 | CpuClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 217 | CpuClk::Ic1 { source, divider } => { | ||
| 218 | if !pll_sources_ready(RCC.iccfgr(0).read().icsel().to_bits(), source.to_bits()) { | ||
| 219 | panic!("ICx clock switch requires both origin and destination clock source to be active") | ||
| 220 | } | ||
| 221 | |||
| 222 | RCC.iccfgr(0).write(|w| { | ||
| 223 | w.set_icsel(source); | ||
| 224 | w.set_icint(divider); | ||
| 225 | }); | ||
| 226 | RCC.divensr().modify(|w| w.set_ic1ens(true)); | ||
| 227 | } | ||
| 228 | CpuClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 229 | CpuClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 230 | _ => {} | ||
| 231 | } | ||
| 232 | // set source | ||
| 233 | let cpusw = Cpusw::from_bits(config.cpu.to_bits()); | ||
| 234 | RCC.cfgr().modify(|w| w.set_cpusw(cpusw)); | ||
| 235 | // wait for changes to take effect | ||
| 236 | while RCC.cfgr().read().cpusws() != Cpusws::from_bits(config.cpu.to_bits()) {} | ||
| 237 | |||
| 238 | // sysclk | ||
| 239 | debug!("configuring sysclk"); | ||
| 240 | match config.sys { | ||
| 241 | SysClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), | ||
| 242 | SysClk::Ic2 { ic2, ic6, ic11 } => { | ||
| 243 | if !pll_sources_ready(RCC.iccfgr(1).read().icsel().to_bits(), ic2.source.to_bits()) { | ||
| 244 | panic!("IC2 clock switch requires both origin and destination clock source to be active") | ||
| 245 | } | ||
| 246 | if !pll_sources_ready(RCC.iccfgr(5).read().icsel().to_bits(), ic6.source.to_bits()) { | ||
| 247 | panic!("IC6 clock switch requires both origin and destination clock source to be active") | ||
| 248 | } | ||
| 249 | if !pll_sources_ready(RCC.iccfgr(10).read().icsel().to_bits(), ic11.source.to_bits()) { | ||
| 250 | panic!("IC11 clock switch requires both origin and destination clock source to be active") | ||
| 251 | } | ||
| 252 | |||
| 253 | RCC.iccfgr(1).write(|w| { | ||
| 254 | w.set_icsel(ic2.source); | ||
| 255 | w.set_icint(ic2.divider); | ||
| 256 | }); | ||
| 257 | RCC.iccfgr(5).write(|w| { | ||
| 258 | w.set_icsel(ic6.source); | ||
| 259 | w.set_icint(ic6.divider); | ||
| 260 | }); | ||
| 261 | RCC.iccfgr(10).write(|w| { | ||
| 262 | w.set_icsel(ic11.source); | ||
| 263 | w.set_icint(ic11.divider); | ||
| 264 | }); | ||
| 265 | RCC.divensr().modify(|w| { | ||
| 266 | w.set_ic2ens(true); | ||
| 267 | w.set_ic6ens(true); | ||
| 268 | w.set_ic11ens(true); | ||
| 269 | }); | ||
| 270 | } | ||
| 271 | SysClk::Msi if !RCC.sr().read().msirdy() => panic!("MSI is not ready to be selected as CPU clock source"), | ||
| 272 | SysClk::Hsi if !RCC.sr().read().hsirdy() => panic!("HSI is not ready to be selected as CPU clock source"), | ||
| 273 | _ => {} | ||
| 274 | } | ||
| 275 | // switch the system bus clock | ||
| 276 | let syssw = Syssw::from_bits(config.sys.to_bits()); | ||
| 277 | RCC.cfgr().modify(|w| w.set_syssw(syssw)); | ||
| 278 | // wait for changes to be applied | ||
| 279 | while RCC.cfgr().read().syssws() != Syssws::from_bits(config.sys.to_bits()) {} | ||
| 280 | |||
| 281 | // decreasing dividers | ||
| 282 | debug!("configuring decreasing pclk dividers"); | ||
| 283 | RCC.cfgr2().modify(|w| { | ||
| 284 | if config.ahb < w.hpre() { | ||
| 285 | debug!(" - AHB"); | ||
| 286 | w.set_hpre(config.ahb); | ||
| 287 | } | ||
| 288 | if config.apb1 < w.ppre1() { | ||
| 289 | debug!(" - APB1"); | ||
| 290 | w.set_ppre1(config.apb1); | ||
| 291 | } | ||
| 292 | if config.apb2 < w.ppre2() { | ||
| 293 | debug!(" - APB2"); | ||
| 294 | w.set_ppre2(config.apb2); | ||
| 295 | } | ||
| 296 | if config.apb4 < w.ppre4() { | ||
| 297 | debug!(" - APB4"); | ||
| 298 | w.set_ppre4(config.apb4); | ||
| 299 | } | ||
| 300 | if config.apb5 < w.ppre5() { | ||
| 301 | debug!(" - APB5"); | ||
| 302 | w.set_ppre5(config.apb5); | ||
| 303 | } | ||
| 304 | }); | ||
| 305 | |||
| 306 | let cpuclk = match config.cpu { | ||
| 307 | CpuClk::Hsi => unwrap!(input.hsi), | ||
| 308 | CpuClk::Msi => unwrap!(input.msi), | ||
| 309 | CpuClk::Hse => unwrap!(input.hse), | ||
| 310 | CpuClk::Ic1 { .. } => todo!(), | ||
| 311 | }; | ||
| 312 | |||
| 313 | let sysclk = match config.sys { | ||
| 314 | SysClk::Hsi => unwrap!(input.hsi), | ||
| 315 | SysClk::Msi => unwrap!(input.msi), | ||
| 316 | SysClk::Hse => unwrap!(input.hse), | ||
| 317 | SysClk::Ic2 { .. } => todo!(), | ||
| 318 | }; | ||
| 319 | |||
| 320 | let timpre: u32 = match RCC.cfgr2().read().timpre() { | ||
| 321 | Timpre::DIV1 => 1, | ||
| 322 | Timpre::DIV2 => 2, | ||
| 323 | Timpre::DIV4 => 4, | ||
| 324 | Timpre::_RESERVED_3 => 8, | ||
| 325 | }; | ||
| 326 | |||
| 327 | let hpre = periph_prescaler_to_value(config.ahb.to_bits()); | ||
| 328 | let ppre1 = periph_prescaler_to_value(config.apb1.to_bits()); | ||
| 329 | let ppre2 = periph_prescaler_to_value(config.apb2.to_bits()); | ||
| 330 | let ppre4 = periph_prescaler_to_value(config.apb4.to_bits()); | ||
| 331 | let ppre5 = periph_prescaler_to_value(config.apb5.to_bits()); | ||
| 332 | |||
| 333 | // enable all peripherals in sleep mode | ||
| 334 | enable_low_power_peripherals(); | ||
| 335 | |||
| 336 | // enable interrupts | ||
| 337 | unsafe { | ||
| 338 | core::arch::asm!("cpsie i"); | ||
| 339 | } | ||
| 340 | |||
| 341 | ClocksOutput { | ||
| 342 | sysclk, | ||
| 343 | cpuclk, | ||
| 344 | pclk_tim: sysclk / timpre, | ||
| 345 | ahb: Hertz(sysclk.0 / hpre as u32), | ||
| 346 | apb1: sysclk / hpre / ppre1, | ||
| 347 | apb2: sysclk / hpre / ppre2, | ||
| 348 | apb4: sysclk / hpre / ppre4, | ||
| 349 | apb5: sysclk / hpre / ppre5, | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | fn enable_low_power_peripherals() { | ||
| 354 | // AHB1-5 | ||
| 355 | RCC.ahb1lpenr().modify(|w| { | ||
| 356 | w.set_adc12lpen(true); | ||
| 357 | w.set_gpdma1lpen(true); | ||
| 358 | }); | ||
| 359 | RCC.ahb2lpenr().modify(|w| { | ||
| 360 | w.set_adf1lpen(true); | ||
| 361 | w.set_mdf1lpen(true); | ||
| 362 | w.set_ramcfglpen(true); | ||
| 363 | }); | ||
| 364 | RCC.ahb3lpenr().modify(|w| { | ||
| 365 | w.set_risaflpen(true); | ||
| 366 | w.set_iaclpen(true); | ||
| 367 | w.set_rifsclpen(true); | ||
| 368 | w.set_pkalpen(true); | ||
| 369 | w.set_saeslpen(true); | ||
| 370 | w.set_cryplpen(true); | ||
| 371 | w.set_hashlpen(true); | ||
| 372 | w.set_rnglpen(true); | ||
| 373 | }); | ||
| 374 | RCC.ahb4lpenr().modify(|w| { | ||
| 375 | w.set_crclpen(true); | ||
| 376 | w.set_pwrlpen(true); | ||
| 377 | w.set_gpioqlpen(true); | ||
| 378 | w.set_gpioplpen(true); | ||
| 379 | w.set_gpioolpen(true); | ||
| 380 | w.set_gpionlpen(true); | ||
| 381 | w.set_gpiohlpen(true); | ||
| 382 | w.set_gpioglpen(true); | ||
| 383 | w.set_gpioflpen(true); | ||
| 384 | w.set_gpioelpen(true); | ||
| 385 | w.set_gpiodlpen(true); | ||
| 386 | w.set_gpioclpen(true); | ||
| 387 | w.set_gpioblpen(true); | ||
| 388 | w.set_gpioalpen(true); | ||
| 389 | }); | ||
| 390 | RCC.ahb5lpenr().modify(|w| { | ||
| 391 | w.set_npulpen(true); | ||
| 392 | w.set_npucachelpen(true); | ||
| 393 | w.set_otg2lpen(true); | ||
| 394 | w.set_otgphy2lpen(true); | ||
| 395 | w.set_otgphy1lpen(true); | ||
| 396 | w.set_otg1lpen(true); | ||
| 397 | w.set_eth1lpen(true); | ||
| 398 | w.set_eth1rxlpen(true); | ||
| 399 | w.set_eth1txlpen(true); | ||
| 400 | w.set_eth1maclpen(true); | ||
| 401 | w.set_gpulpen(true); | ||
| 402 | w.set_gfxmmulpen(true); | ||
| 403 | w.set_mce4lpen(true); | ||
| 404 | w.set_xspi3lpen(true); | ||
| 405 | w.set_mce3lpen(true); | ||
| 406 | w.set_mce2lpen(true); | ||
| 407 | w.set_mce1lpen(true); | ||
| 408 | w.set_xspimlpen(true); | ||
| 409 | w.set_xspi2lpen(true); | ||
| 410 | w.set_sdmmc1lpen(true); | ||
| 411 | w.set_sdmmc2lpen(true); | ||
| 412 | w.set_pssilpen(true); | ||
| 413 | w.set_xspi1lpen(true); | ||
| 414 | w.set_fmclpen(true); | ||
| 415 | w.set_jpeglpen(true); | ||
| 416 | w.set_dma2dlpen(true); | ||
| 417 | w.set_hpdma1lpen(true); | ||
| 418 | }); | ||
| 419 | |||
| 420 | // APB1-5 | ||
| 421 | RCC.apb1llpenr().modify(|w| { | ||
| 422 | w.set_uart8lpen(true); | ||
| 423 | w.set_uart7lpen(true); | ||
| 424 | w.set_i3c2lpen(true); | ||
| 425 | w.set_i3c1lpen(true); | ||
| 426 | w.set_i2c3lpen(true); | ||
| 427 | w.set_i2c2lpen(true); | ||
| 428 | w.set_i2c1lpen(true); | ||
| 429 | w.set_uart5lpen(true); | ||
| 430 | w.set_uart4lpen(true); | ||
| 431 | w.set_usart3lpen(true); | ||
| 432 | w.set_usart2lpen(true); | ||
| 433 | w.set_spdifrx1lpen(true); | ||
| 434 | w.set_spi3lpen(true); | ||
| 435 | w.set_spi2lpen(true); | ||
| 436 | w.set_tim11lpen(true); | ||
| 437 | w.set_tim10lpen(true); | ||
| 438 | w.set_wwdglpen(true); | ||
| 439 | w.set_lptim1lpen(true); | ||
| 440 | w.set_tim14lpen(true); | ||
| 441 | w.set_tim13lpen(true); | ||
| 442 | w.set_tim12lpen(true); | ||
| 443 | w.set_tim7lpen(true); | ||
| 444 | w.set_tim6lpen(true); | ||
| 445 | w.set_tim5lpen(true); | ||
| 446 | w.set_tim4lpen(true); | ||
| 447 | w.set_tim3lpen(true); | ||
| 448 | w.set_tim2lpen(true); | ||
| 449 | }); | ||
| 450 | RCC.apb1hlpenr().modify(|w| { | ||
| 451 | w.set_ucpd1lpen(true); | ||
| 452 | w.set_fdcanlpen(true); | ||
| 453 | w.set_mdioslpen(true); | ||
| 454 | }); | ||
| 455 | RCC.apb2lpenr().modify(|w| { | ||
| 456 | w.set_sai2lpen(true); | ||
| 457 | w.set_sai1lpen(true); | ||
| 458 | w.set_spi5lpen(true); | ||
| 459 | w.set_tim9lpen(true); | ||
| 460 | w.set_tim17lpen(true); | ||
| 461 | w.set_tim16lpen(true); | ||
| 462 | w.set_tim15lpen(true); | ||
| 463 | w.set_tim18lpen(true); | ||
| 464 | w.set_spi4lpen(true); | ||
| 465 | w.set_spi1lpen(true); | ||
| 466 | w.set_usart10lpen(true); | ||
| 467 | w.set_uart9lpen(true); | ||
| 468 | w.set_usart6lpen(true); | ||
| 469 | w.set_usart1lpen(true); | ||
| 470 | w.set_tim8lpen(true); | ||
| 471 | w.set_tim1lpen(true); | ||
| 472 | }); | ||
| 473 | RCC.apb3lpenr().modify(|w| { | ||
| 474 | w.set_dftlpen(true); | ||
| 475 | }); | ||
| 476 | RCC.apb4llpenr().modify(|w| { | ||
| 477 | w.set_rtcapblpen(true); | ||
| 478 | w.set_rtclpen(true); | ||
| 479 | w.set_vrefbuflpen(true); | ||
| 480 | w.set_lptim5lpen(true); | ||
| 481 | w.set_lptim4lpen(true); | ||
| 482 | w.set_lptim3lpen(true); | ||
| 483 | w.set_lptim2lpen(true); | ||
| 484 | w.set_i2c4lpen(true); | ||
| 485 | w.set_spi6lpen(true); | ||
| 486 | w.set_lpuart1lpen(true); | ||
| 487 | w.set_hdplpen(true); | ||
| 488 | }); | ||
| 489 | RCC.apb4hlpenr().modify(|w| { | ||
| 490 | w.set_dtslpen(true); | ||
| 491 | w.set_bseclpen(true); | ||
| 492 | w.set_syscfglpen(true); | ||
| 493 | }); | ||
| 494 | RCC.apb5lpenr().modify(|w| { | ||
| 495 | w.set_csilpen(true); | ||
| 496 | w.set_venclpen(true); | ||
| 497 | w.set_gfxtimlpen(true); | ||
| 498 | w.set_dcmilpen(true); | ||
| 499 | w.set_ltdclpen(true); | ||
| 500 | }); | ||
| 501 | |||
| 502 | RCC.buslpenr().modify(|w| { | ||
| 503 | w.set_aclknclpen(true); | ||
| 504 | w.set_aclknlpen(true); | ||
| 505 | }); | ||
| 506 | |||
| 507 | RCC.memlpenr().modify(|w| { | ||
| 508 | w.set_bootromlpen(true); | ||
| 509 | w.set_vencramlpen(true); | ||
| 510 | w.set_npucacheramlpen(true); | ||
| 511 | w.set_flexramlpen(true); | ||
| 512 | w.set_axisram2lpen(true); | ||
| 513 | w.set_axisram1lpen(true); | ||
| 514 | w.set_bkpsramlpen(true); | ||
| 515 | w.set_ahbsram2lpen(true); | ||
| 516 | w.set_ahbsram1lpen(true); | ||
| 517 | w.set_axisram6lpen(true); | ||
| 518 | w.set_axisram5lpen(true); | ||
| 519 | w.set_axisram4lpen(true); | ||
| 520 | w.set_axisram3lpen(true); | ||
| 521 | }); | ||
| 522 | |||
| 523 | RCC.misclpenr().modify(|w| { | ||
| 524 | w.set_perlpen(true); | ||
| 525 | w.set_xspiphycomplpen(true); | ||
| 526 | w.set_dbglpen(true); | ||
| 527 | }); | ||
| 528 | } | ||
| 529 | |||
| 530 | const fn periph_prescaler_to_value(bits: u8) -> u8 { | ||
| 531 | match bits { | ||
| 532 | 0 => 1, | ||
| 533 | 1 => 2, | ||
| 534 | 2 => 4, | ||
| 535 | 3 => 8, | ||
| 536 | 4 => 16, | ||
| 537 | 5 => 32, | ||
| 538 | 6 => 64, | ||
| 539 | 7.. => 128, | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | fn pll_source_ready(source: u8) -> bool { | ||
| 544 | match source { | ||
| 545 | 0x0 if !RCC.sr().read().pllrdy(0) && !RCC.pllcfgr1(0).read().pllbyp() => false, | ||
| 546 | 0x1 if !RCC.sr().read().pllrdy(1) && !RCC.pllcfgr1(1).read().pllbyp() => false, | ||
| 547 | 0x2 if !RCC.sr().read().pllrdy(2) && !RCC.pllcfgr1(2).read().pllbyp() => false, | ||
| 548 | 0x3 if !RCC.sr().read().pllrdy(3) && !RCC.pllcfgr1(3).read().pllbyp() => false, | ||
| 549 | _ => true, | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | fn pll_sources_ready(source1: u8, source2: u8) -> bool { | ||
| 554 | pll_source_ready(source1) && pll_source_ready(source2) | ||
| 555 | } | ||
| 556 | |||
| 557 | impl Default for Config { | ||
| 558 | fn default() -> Self { | ||
| 559 | Self::new() | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | fn power_supply_config(supply_config: SupplyConfig) { | ||
| 564 | // power supply config | ||
| 565 | PWR.cr1().modify(|w| { | ||
| 566 | w.set_sden(match supply_config { | ||
| 567 | SupplyConfig::External => false, | ||
| 568 | SupplyConfig::Smps => true, | ||
| 569 | }); | ||
| 570 | }); | ||
| 571 | |||
| 572 | // Validate supply configuration | ||
| 573 | while !PWR.voscr().read().actvosrdy() {} | ||
| 574 | } | ||
| 575 | |||
| 576 | struct PllInput { | ||
| 577 | hsi: Option<Hertz>, | ||
| 578 | msi: Option<Hertz>, | ||
| 579 | hse: Option<Hertz>, | ||
| 580 | i2s_ckin: Option<Hertz>, | ||
| 581 | } | ||
| 582 | |||
| 583 | #[derive(Clone, Copy, Default)] | ||
| 584 | #[allow(dead_code)] | ||
| 585 | struct PllOutput { | ||
| 586 | divm: Option<Hertz>, | ||
| 587 | divn: Option<Hertz>, | ||
| 588 | divp1: Option<Hertz>, | ||
| 589 | divp2: Option<Hertz>, | ||
| 590 | output: Option<Hertz>, | ||
| 591 | } | ||
| 592 | |||
| 593 | fn init_pll(pll_config: Option<Pll>, pll_index: usize, input: &PllInput) -> PllOutput { | ||
| 594 | let cfgr1 = RCC.pllcfgr1(pll_index); | ||
| 595 | let cfgr2 = RCC.pllcfgr2(pll_index); | ||
| 596 | let cfgr3 = RCC.pllcfgr3(pll_index); | ||
| 597 | |||
| 598 | match pll_config { | ||
| 599 | Some(Pll::Oscillator { | ||
| 600 | source, | ||
| 601 | divm, | ||
| 602 | fractional, | ||
| 603 | divn, | ||
| 604 | divp1, | ||
| 605 | divp2, | ||
| 606 | }) => { | ||
| 607 | // ensure pll is disabled | ||
| 608 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 609 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 610 | |||
| 611 | // ensure PLLxMODSSDIS=1 to work in fractional mode | ||
| 612 | cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE)); | ||
| 613 | // clear bypass mode | ||
| 614 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 615 | // configure the pll clock source, mul and div factors | ||
| 616 | cfgr1.modify(|w| { | ||
| 617 | w.set_pllsel(source); | ||
| 618 | w.set_plldivm(divm); | ||
| 619 | w.set_plldivn(divn); | ||
| 620 | }); | ||
| 621 | |||
| 622 | let in_clk = match source { | ||
| 623 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 624 | Pllsel::MSI => unwrap!(input.msi), | ||
| 625 | Pllsel::HSE => unwrap!(input.hse), | ||
| 626 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 627 | _ => panic!("reserved PLL source not allowed"), | ||
| 628 | }; | ||
| 629 | |||
| 630 | let m = divm.to_bits() as u32; | ||
| 631 | let n = divn as u32; | ||
| 632 | |||
| 633 | cfgr3.modify(|w| { | ||
| 634 | w.set_pllpdiv1(divp1); | ||
| 635 | w.set_pllpdiv2(divp2); | ||
| 636 | }); | ||
| 637 | |||
| 638 | let p1 = divp1.to_bits() as u32; | ||
| 639 | let p2 = divp2.to_bits() as u32; | ||
| 640 | |||
| 641 | // configure pll divnfrac | ||
| 642 | cfgr2.modify(|w| w.set_plldivnfrac(fractional)); | ||
| 643 | // clear pllxmoddsen | ||
| 644 | cfgr3.modify(|w| w.set_pllmoddsen(false)); | ||
| 645 | // fractional mode | ||
| 646 | if fractional != 0 { | ||
| 647 | cfgr3.modify(|w| { | ||
| 648 | w.set_pllmoddsen(true); | ||
| 649 | w.set_plldacen(true); | ||
| 650 | }) | ||
| 651 | } | ||
| 652 | // enable pll post divider output | ||
| 653 | cfgr3.modify(|w| { | ||
| 654 | w.set_pllmodssrst(true); | ||
| 655 | w.set_pllpdiven(true); | ||
| 656 | }); | ||
| 657 | // enable the pll | ||
| 658 | RCC.csr().write(|w| w.pllons(pll_index)); | ||
| 659 | // wait until ready | ||
| 660 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 661 | |||
| 662 | PllOutput { | ||
| 663 | divm: Some(Hertz(m)), | ||
| 664 | divn: Some(Hertz(n)), | ||
| 665 | divp1: Some(Hertz(p1)), | ||
| 666 | divp2: Some(Hertz(p2)), | ||
| 667 | output: Some(Hertz(in_clk.0 / m / n / p1 / p2)), | ||
| 668 | } | ||
| 669 | } | ||
| 670 | Some(Pll::Bypass { source }) => { | ||
| 671 | // check if source is ready | ||
| 672 | if !pll_source_ready(source.to_bits()) { | ||
| 673 | panic!("PLL source is not ready") | ||
| 674 | } | ||
| 675 | |||
| 676 | // ensure pll is disabled | ||
| 677 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 678 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 679 | |||
| 680 | cfgr1.modify(|w| { | ||
| 681 | w.set_pllbyp(true); | ||
| 682 | w.set_pllsel(source); | ||
| 683 | }); | ||
| 684 | |||
| 685 | let in_clk = match source { | ||
| 686 | Pllsel::HSI => unwrap!(input.hsi), | ||
| 687 | Pllsel::MSI => unwrap!(input.msi), | ||
| 688 | Pllsel::HSE => unwrap!(input.hse), | ||
| 689 | Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin), | ||
| 690 | _ => panic!("reserved PLL source not allowed"), | ||
| 691 | }; | ||
| 692 | |||
| 693 | PllOutput { | ||
| 694 | output: Some(in_clk), | ||
| 695 | ..Default::default() | ||
| 696 | } | ||
| 697 | } | ||
| 698 | None => { | ||
| 699 | cfgr3.modify(|w| w.set_pllpdiven(false)); | ||
| 700 | RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); | ||
| 701 | // wait till disabled | ||
| 702 | while RCC.sr().read().pllrdy(pll_index) {} | ||
| 703 | |||
| 704 | // clear bypass mode | ||
| 705 | cfgr1.modify(|w| w.set_pllbyp(false)); | ||
| 706 | |||
| 707 | PllOutput::default() | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | #[allow(dead_code)] | ||
| 713 | struct OscOutput { | ||
| 714 | hsi: Option<Hertz>, | ||
| 715 | hse: Option<Hertz>, | ||
| 716 | msi: Option<Hertz>, | ||
| 717 | lsi: Option<Hertz>, | ||
| 718 | lse: Option<Hertz>, | ||
| 719 | pll1: Option<Hertz>, | ||
| 720 | pll2: Option<Hertz>, | ||
| 721 | pll3: Option<Hertz>, | ||
| 722 | pll4: Option<Hertz>, | ||
| 723 | ic1sel: Icsel, | ||
| 724 | ic2sel: Icsel, | ||
| 725 | ic6sel: Icsel, | ||
| 726 | ic11sel: Icsel, | ||
| 727 | } | ||
| 728 | |||
| 729 | fn init_osc(config: Config) -> OscOutput { | ||
| 730 | let (cpu_src, sys_src) = { | ||
| 731 | let reg = RCC.cfgr().read(); | ||
| 732 | (reg.cpusws(), reg.syssws()) | ||
| 733 | }; | ||
| 734 | let pll1_src = RCC.pllcfgr1(0).read().pllsel(); | ||
| 735 | let pll2_src = RCC.pllcfgr1(1).read().pllsel(); | ||
| 736 | let pll3_src = RCC.pllcfgr1(2).read().pllsel(); | ||
| 737 | let pll4_src = RCC.pllcfgr1(3).read().pllsel(); | ||
| 738 | let rcc_sr = RCC.sr().read(); | ||
| 739 | |||
| 740 | debug!("configuring HSE"); | ||
| 741 | |||
| 742 | // hse configuration | ||
| 743 | let hse = if let Some(hse) = config.hse { | ||
| 744 | match hse.mode { | ||
| 745 | HseMode::Oscillator => { | ||
| 746 | debug!("HSE in oscillator mode"); | ||
| 747 | } | ||
| 748 | HseMode::Bypass => { | ||
| 749 | debug!("HSE in bypass mode"); | ||
| 750 | RCC.hsecfgr().modify(|w| { | ||
| 751 | w.set_hsebyp(true); | ||
| 752 | w.set_hseext(Hseext::ANALOG); | ||
| 753 | }); | ||
| 754 | } | ||
| 755 | HseMode::BypassDigital => { | ||
| 756 | debug!("HSE in bypass digital mode"); | ||
| 757 | RCC.hsecfgr().modify(|w| { | ||
| 758 | w.set_hsebyp(true); | ||
| 759 | w.set_hseext(Hseext::DIGITAL); | ||
| 760 | }); | ||
| 761 | } | ||
| 762 | } | ||
| 763 | RCC.csr().write(|w| w.set_hseons(true)); | ||
| 764 | |||
| 765 | // wait until the hse is ready | ||
| 766 | while !RCC.sr().read().hserdy() {} | ||
| 767 | |||
| 768 | Some(hse.freq) | ||
| 769 | } else if cpu_src == Cpusws::HSE | ||
| 770 | || sys_src == Syssws::HSE | ||
| 771 | || (pll1_src == Pllsel::HSE && rcc_sr.pllrdy(0)) | ||
| 772 | || (pll2_src == Pllsel::HSE && rcc_sr.pllrdy(1)) | ||
| 773 | || (pll3_src == Pllsel::HSE && rcc_sr.pllrdy(2)) | ||
| 774 | || (pll4_src == Pllsel::HSE && rcc_sr.pllrdy(3)) | ||
| 775 | { | ||
| 776 | panic!( | ||
| 777 | "When the HSE is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 778 | ); | ||
| 779 | } else { | ||
| 780 | debug!("HSE off"); | ||
| 781 | |||
| 782 | RCC.ccr().write(|w| w.set_hseonc(true)); | ||
| 783 | RCC.hsecfgr().modify(|w| { | ||
| 784 | w.set_hseext(Hseext::ANALOG); | ||
| 785 | w.set_hsebyp(false); | ||
| 786 | }); | ||
| 787 | |||
| 788 | // wait until the hse is disabled | ||
| 789 | while RCC.sr().read().hserdy() {} | ||
| 790 | |||
| 791 | None | ||
| 792 | }; | ||
| 793 | |||
| 794 | // hsi configuration | ||
| 795 | debug!("configuring HSI"); | ||
| 796 | let hsi = if let Some(hsi) = config.hsi { | ||
| 797 | RCC.csr().write(|w| w.set_hsions(true)); | ||
| 798 | while !RCC.sr().read().hsirdy() {} | ||
| 799 | |||
| 800 | // set divider and calibration | ||
| 801 | RCC.hsicfgr().modify(|w| { | ||
| 802 | w.set_hsidiv(hsi.pre); | ||
| 803 | w.set_hsitrim(hsi.trim); | ||
| 804 | }); | ||
| 805 | |||
| 806 | Some(HSI_FREQ / hsi.pre) | ||
| 807 | } else if cpu_src == Cpusws::HSI | ||
| 808 | || sys_src == Syssws::HSI | ||
| 809 | || (pll1_src == Pllsel::HSI && rcc_sr.pllrdy(0)) | ||
| 810 | || (pll2_src == Pllsel::HSI && rcc_sr.pllrdy(1)) | ||
| 811 | || (pll3_src == Pllsel::HSI && rcc_sr.pllrdy(2)) | ||
| 812 | || (pll4_src == Pllsel::HSI && rcc_sr.pllrdy(3)) | ||
| 813 | { | ||
| 814 | panic!( | ||
| 815 | "When the HSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 816 | ); | ||
| 817 | } else { | ||
| 818 | debug!("HSI off"); | ||
| 819 | |||
| 820 | RCC.ccr().write(|w| w.set_hsionc(true)); | ||
| 821 | while RCC.sr().read().hsirdy() {} | ||
| 822 | |||
| 823 | None | ||
| 824 | }; | ||
| 825 | |||
| 826 | // msi configuration | ||
| 827 | debug!("configuring MSI"); | ||
| 828 | let msi = if let Some(msi) = config.msi { | ||
| 829 | RCC.msicfgr().modify(|w| w.set_msifreqsel(msi.freq)); | ||
| 830 | RCC.csr().write(|w| w.set_msions(true)); | ||
| 831 | while !RCC.sr().read().msirdy() {} | ||
| 832 | RCC.msicfgr().modify(|w| w.set_msitrim(msi.trim)); | ||
| 833 | |||
| 834 | Some(match msi.freq { | ||
| 835 | Msifreqsel::_4MHZ => Hertz::mhz(4), | ||
| 836 | Msifreqsel::_16MHZ => Hertz::mhz(16), | ||
| 837 | }) | ||
| 838 | } else if cpu_src == Cpusws::MSI | ||
| 839 | || sys_src == Syssws::MSI | ||
| 840 | || (pll1_src == Pllsel::MSI && rcc_sr.pllrdy(0)) | ||
| 841 | || (pll2_src == Pllsel::MSI && rcc_sr.pllrdy(1)) | ||
| 842 | || (pll3_src == Pllsel::MSI && rcc_sr.pllrdy(2)) | ||
| 843 | || (pll4_src == Pllsel::MSI && rcc_sr.pllrdy(3)) | ||
| 844 | { | ||
| 845 | panic!( | ||
| 846 | "When the MSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled" | ||
| 847 | ); | ||
| 848 | } else { | ||
| 849 | RCC.ccr().write(|w| w.set_msionc(true)); | ||
| 850 | while RCC.sr().read().msirdy() {} | ||
| 851 | |||
| 852 | None | ||
| 853 | }; | ||
| 854 | |||
| 855 | // lsi configuration | ||
| 856 | debug!("configuring LSI"); | ||
| 857 | let lsi = if config.lsi { | ||
| 858 | RCC.csr().write(|w| w.set_lsions(true)); | ||
| 859 | while !RCC.sr().read().lsirdy() {} | ||
| 860 | Some(super::LSI_FREQ) | ||
| 861 | } else { | ||
| 862 | RCC.ccr().write(|w| w.set_lsionc(true)); | ||
| 863 | while RCC.sr().read().lsirdy() {} | ||
| 864 | None | ||
| 865 | }; | ||
| 866 | |||
| 867 | // lse configuration | ||
| 868 | debug!("configuring LSE"); | ||
| 869 | let lse = if config.lse { | ||
| 870 | RCC.csr().write(|w| w.set_lseons(true)); | ||
| 871 | while !RCC.sr().read().lserdy() {} | ||
| 872 | Some(LSE_FREQ) | ||
| 873 | } else { | ||
| 874 | RCC.ccr().write(|w| w.set_lseonc(true)); | ||
| 875 | while RCC.sr().read().lserdy() {} | ||
| 876 | None | ||
| 877 | }; | ||
| 878 | |||
| 879 | let pll_input = PllInput { | ||
| 880 | hse, | ||
| 881 | msi, | ||
| 882 | hsi, | ||
| 883 | i2s_ckin: None, | ||
| 884 | }; | ||
| 885 | |||
| 886 | // pll1,2,3,4 config | ||
| 887 | let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4]; | ||
| 888 | let mut pll_outputs: [PllOutput; 4] = [PllOutput::default(); 4]; | ||
| 889 | |||
| 890 | let ic1_src = RCC.iccfgr(0).read().icsel(); | ||
| 891 | let ic2_src = RCC.iccfgr(1).read().icsel(); | ||
| 892 | let ic6_src = RCC.iccfgr(5).read().icsel(); | ||
| 893 | let ic11_src = RCC.iccfgr(10).read().icsel(); | ||
| 894 | |||
| 895 | for (n, (&pll, out)) in pll_configs.iter().zip(pll_outputs.iter_mut()).enumerate() { | ||
| 896 | debug!("configuring PLL{}", n + 1); | ||
| 897 | let pll_ready = RCC.sr().read().pllrdy(n); | ||
| 898 | |||
| 899 | if is_new_pll_config(pll, 0) { | ||
| 900 | let this_pll = Icsel::from_bits(n as u8); | ||
| 901 | |||
| 902 | if cpu_src == Cpusws::IC1 && ic1_src == this_pll { | ||
| 903 | panic!("PLL should not be disabled / reconfigured if used for IC1 (cpuclksrc)") | ||
| 904 | } | ||
| 905 | |||
| 906 | if sys_src == Syssws::IC2 && (ic2_src == this_pll || ic6_src == this_pll || ic11_src == this_pll) { | ||
| 907 | panic!("PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)") | ||
| 908 | } | ||
| 909 | |||
| 910 | *out = init_pll(pll, 0, &pll_input); | ||
| 911 | } else if pll.is_some() && !pll_ready { | ||
| 912 | RCC.csr().write(|w| w.pllons(n)); | ||
| 913 | while !RCC.sr().read().pllrdy(n) {} | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | OscOutput { | ||
| 918 | hsi, | ||
| 919 | hse, | ||
| 920 | msi, | ||
| 921 | lsi, | ||
| 922 | lse, | ||
| 923 | pll1: pll_outputs[0].output, | ||
| 924 | pll2: pll_outputs[1].output, | ||
| 925 | pll3: pll_outputs[2].output, | ||
| 926 | pll4: pll_outputs[3].output, | ||
| 927 | ic1sel: ic1_src, | ||
| 928 | ic2sel: ic2_src, | ||
| 929 | ic6sel: ic6_src, | ||
| 930 | ic11sel: ic11_src, | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | fn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool { | ||
| 935 | let cfgr1 = RCC.pllcfgr1(pll_index).read(); | ||
| 936 | let cfgr2 = RCC.pllcfgr2(pll_index).read(); | ||
| 937 | let cfgr3 = RCC.pllcfgr3(pll_index).read(); | ||
| 938 | |||
| 939 | let ready = RCC.sr().read().pllrdy(pll_index); | ||
| 940 | let bypass = cfgr1.pllbyp(); | ||
| 941 | |||
| 942 | match (pll, ready, bypass) { | ||
| 943 | (None, true, _) => return true, | ||
| 944 | (Some(_), false, _) => return true, | ||
| 945 | (Some(conf), true, bypass) => match (conf, bypass) { | ||
| 946 | (Pll::Bypass { .. }, false) => return true, | ||
| 947 | (Pll::Oscillator { .. }, true) => return true, | ||
| 948 | _ => {} | ||
| 949 | }, | ||
| 950 | _ => {} | ||
| 951 | } | ||
| 952 | |||
| 953 | match pll { | ||
| 954 | Some(Pll::Bypass { source }) => cfgr1.pllsel() != source, | ||
| 955 | Some(Pll::Oscillator { | ||
| 956 | source, | ||
| 957 | divm: m, | ||
| 958 | fractional, | ||
| 959 | divn: n, | ||
| 960 | divp1: p1, | ||
| 961 | divp2: p2, | ||
| 962 | }) => { | ||
| 963 | cfgr1.pllsel() != source | ||
| 964 | || cfgr1.plldivm() != m | ||
| 965 | || cfgr1.plldivn() != n | ||
| 966 | || cfgr2.plldivnfrac() != fractional | ||
| 967 | || cfgr3.pllpdiv1() != p1 | ||
| 968 | || cfgr3.pllpdiv2() != p2 | ||
| 969 | } | ||
| 970 | None => false, | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | pub(crate) unsafe fn init(config: Config) { | ||
| 975 | debug!("enabling SYSCFG"); | ||
| 976 | // system configuration setup | ||
| 977 | RCC.apb4hensr().write(|w| w.set_syscfgens(true)); | ||
| 978 | // delay after RCC peripheral clock enabling | ||
| 979 | RCC.apb4hensr().read(); | ||
| 980 | |||
| 981 | debug!("setting VTOR"); | ||
| 982 | |||
| 983 | let vtor = unsafe { | ||
| 984 | let p = cortex_m::Peripherals::steal(); | ||
| 985 | p.SCB.vtor.read() | ||
| 986 | }; | ||
| 987 | |||
| 988 | // set default vector table location after reset or standby | ||
| 989 | SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor)); | ||
| 990 | // read back the value to ensure it is written before deactivating SYSCFG | ||
| 991 | SYSCFG.initsvtorcr().read(); | ||
| 992 | |||
| 993 | debug!("deactivating SYSCFG"); | ||
| 994 | |||
| 995 | // deactivate SYSCFG | ||
| 996 | RCC.apb4hensr().write(|w| w.set_syscfgens(false)); | ||
| 997 | |||
| 998 | debug!("enabling FPU"); | ||
| 999 | |||
| 1000 | // enable fpu | ||
| 1001 | unsafe { | ||
| 1002 | let p = cortex_m::Peripherals::steal(); | ||
| 1003 | p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22)); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | // TODO: ugly workaround for DMA accesses until RIF is properly implemented | ||
| 1007 | debug!("deactivating RIF"); | ||
| 1008 | const RISAF3_BASE_NS: *mut u32 = stm32_metapac::RNG.wrapping_byte_offset(0x8000) as _; // AHB3PERIPH_BASE_NS + 0x8000UL | ||
| 1009 | const RISAF3_REG0_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x40); | ||
| 1010 | const RISAF3_REG0_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x48); | ||
| 1011 | const RISAF3_REG0_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x4C); | ||
| 1012 | const RISAF3_REG1_CFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x80); | ||
| 1013 | const RISAF3_REG1_ENDR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x88); | ||
| 1014 | const RISAF3_REG1_CIDCFGR: *mut u32 = RISAF3_BASE_NS.wrapping_byte_offset(0x8C); | ||
| 1015 | unsafe { | ||
| 1016 | *RISAF3_REG0_CIDCFGR = 0x000F000F; /* RW for everyone */ | ||
| 1017 | *RISAF3_REG0_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1018 | *RISAF3_REG0_CFGR = 0x00000101; /* enabled, secure, unprivileged for everyone */ | ||
| 1019 | *RISAF3_REG1_CIDCFGR = 0x00FF00FF; /* RW for everyone */ | ||
| 1020 | *RISAF3_REG1_ENDR = 0xFFFFFFFF; /* all-encompassing */ | ||
| 1021 | *RISAF3_REG1_CFGR = 0x00000001; /* enabled, non-secure, unprivileged*/ | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | debug!("setting power supply config"); | ||
| 1025 | |||
| 1026 | power_supply_config(config.supply_config); | ||
| 1027 | |||
| 1028 | let osc = init_osc(config); | ||
| 1029 | let clock_inputs = ClocksInput { | ||
| 1030 | hsi: osc.hsi, | ||
| 1031 | msi: osc.msi, | ||
| 1032 | hse: osc.hse, | ||
| 1033 | }; | ||
| 1034 | let clocks = init_clocks(config, &clock_inputs); | ||
| 1035 | |||
| 1036 | // TODO: sysb, sysc, sysd must have the same clock source | ||
| 1037 | |||
| 1038 | set_clocks!( | ||
| 1039 | sys: Some(clocks.sysclk), | ||
| 1040 | hsi: osc.hsi, | ||
| 1041 | hsi_div: None, | ||
| 1042 | hse: osc.hse, | ||
| 1043 | msi: osc.msi, | ||
| 1044 | hclk1: Some(clocks.ahb), | ||
| 1045 | hclk2: Some(clocks.ahb), | ||
| 1046 | hclk3: Some(clocks.ahb), | ||
| 1047 | hclk4: Some(clocks.ahb), | ||
| 1048 | hclk5: Some(clocks.ahb), | ||
| 1049 | pclk1: Some(clocks.apb1), | ||
| 1050 | pclk2: Some(clocks.apb2), | ||
| 1051 | pclk1_tim: Some(clocks.pclk_tim), | ||
| 1052 | pclk2_tim: Some(clocks.pclk_tim), | ||
| 1053 | pclk4: Some(clocks.apb4), | ||
| 1054 | pclk5: Some(clocks.apb5), | ||
| 1055 | per: None, | ||
| 1056 | rtc: None, | ||
| 1057 | i2s_ckin: None, | ||
| 1058 | ic8: None, | ||
| 1059 | ic9: None, | ||
| 1060 | ic10: None, | ||
| 1061 | ic14: None, | ||
| 1062 | ic15: None, | ||
| 1063 | ic17: None, | ||
| 1064 | ic20: None, | ||
| 1065 | ); | ||
| 1066 | } | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 06895a99a..47cc29c6f 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -6,9 +6,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 7 | }; | 7 | }; |
| 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; | 8 | use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg(all(peri_usb_otg_hs))] | ||
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 11 | use crate::pac::{FLASH, PWR, RCC}; | 9 | use crate::pac::{FLASH, PWR, RCC}; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 12 | use crate::rcc::LSI_FREQ; | 12 | use crate::rcc::LSI_FREQ; |
| 13 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 14 | 14 | ||
| @@ -343,6 +343,16 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 343 | 343 | ||
| 344 | let hsi48 = config.hsi48.map(super::init_hsi48); | 344 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 345 | 345 | ||
| 346 | // There's a possibility that a bootloader that ran before us has configured the system clock | ||
| 347 | // source to be PLL1_R. In that case we'd get forever stuck on (de)configuring PLL1 as the chip | ||
| 348 | // prohibits disabling PLL1 when it's used as a source for system clock. Change the system | ||
| 349 | // clock source to MSIS which doesn't suffer from this conflict. The correct source per the | ||
| 350 | // provided config is then set further down. | ||
| 351 | // See https://github.com/embassy-rs/embassy/issues/5072 | ||
| 352 | let default_system_clock_source = Config::default().sys; | ||
| 353 | RCC.cfgr1().modify(|w| w.set_sw(default_system_clock_source)); | ||
| 354 | while RCC.cfgr1().read().sws() != default_system_clock_source {} | ||
| 355 | |||
| 346 | let pll_input = PllInput { hse, hsi, msi: msis }; | 356 | let pll_input = PllInput { hse, hsi, msi: msis }; |
| 347 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); | 357 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); |
| 348 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); | 358 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); |
| @@ -442,7 +452,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 442 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 452 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 443 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 453 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 444 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 454 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 445 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 455 | _ => panic!( |
| 456 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 457 | clk_val | ||
| 458 | ), | ||
| 446 | }, | 459 | }, |
| 447 | None => Usbrefcksel::MHZ24, | 460 | None => Usbrefcksel::MHZ24, |
| 448 | }; | 461 | }; |
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 481437939..2528996d5 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -7,9 +7,9 @@ pub use crate::pac::rcc::vals::{ | |||
| 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, | 7 | Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, |
| 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, | 8 | Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, |
| 9 | }; | 9 | }; |
| 10 | #[cfg(all(peri_usb_otg_hs))] | ||
| 11 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 12 | use crate::pac::{FLASH, RCC}; | 10 | use crate::pac::{FLASH, RCC}; |
| 11 | #[cfg(all(peri_usb_otg_hs))] | ||
| 12 | pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; | ||
| 13 | use crate::rcc::LSI_FREQ; | 13 | use crate::rcc::LSI_FREQ; |
| 14 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 15 | 15 | ||
| @@ -245,7 +245,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | 245 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | 246 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | 247 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 248 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | 248 | _ => panic!( |
| 249 | "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", | ||
| 250 | clk_val | ||
| 251 | ), | ||
| 249 | }, | 252 | }, |
| 250 | None => Usbrefcksel::MHZ24, | 253 | None => Usbrefcksel::MHZ24, |
| 251 | }; | 254 | }; |
