diff options
Diffstat (limited to 'embassy-imxrt/src/clocks.rs')
| -rw-r--r-- | embassy-imxrt/src/clocks.rs | 1687 |
1 files changed, 1687 insertions, 0 deletions
diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs new file mode 100644 index 000000000..1d36fb142 --- /dev/null +++ b/embassy-imxrt/src/clocks.rs | |||
| @@ -0,0 +1,1687 @@ | |||
| 1 | //! Clock configuration for the `RT6xx` | ||
| 2 | use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; | ||
| 3 | |||
| 4 | #[cfg(feature = "defmt")] | ||
| 5 | use defmt; | ||
| 6 | use paste::paste; | ||
| 7 | |||
| 8 | use crate::pac; | ||
| 9 | |||
| 10 | /// Clock configuration; | ||
| 11 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub enum Clocks { | ||
| 14 | /// Low power oscillator | ||
| 15 | Lposc, | ||
| 16 | /// System Frequency Resonance Oscillator (SFRO) | ||
| 17 | Sfro, | ||
| 18 | /// Real Time Clock | ||
| 19 | Rtc, | ||
| 20 | /// Feed-forward Ring Oscillator | ||
| 21 | Ffro, // This includes that div2 and div4 variations | ||
| 22 | /// External Clock Input | ||
| 23 | ClkIn, | ||
| 24 | /// AHB Clock | ||
| 25 | Hclk, | ||
| 26 | /// Main Clock | ||
| 27 | MainClk, | ||
| 28 | /// Main PLL Clock | ||
| 29 | MainPllClk, // also has aux0,aux1,dsp, and audio pll's downstream | ||
| 30 | /// System Clock | ||
| 31 | SysClk, | ||
| 32 | /// System Oscillator | ||
| 33 | SysOscClk, | ||
| 34 | /// ADC Clock | ||
| 35 | Adc, | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Clock configuration. | ||
| 39 | pub struct ClockConfig { | ||
| 40 | /// low-power oscillator config | ||
| 41 | pub lposc: LposcConfig, | ||
| 42 | /// 16Mhz internal oscillator config | ||
| 43 | pub sfro: SfroConfig, | ||
| 44 | /// Real Time Clock config | ||
| 45 | pub rtc: RtcClkConfig, | ||
| 46 | /// 48/60 Mhz internal oscillator config | ||
| 47 | pub ffro: FfroConfig, | ||
| 48 | // pub pll: Option<PllPfdConfig>, //potentially covered in main pll clk | ||
| 49 | /// External Clock-In config | ||
| 50 | pub clk_in: ClkInConfig, | ||
| 51 | /// AHB bus clock config | ||
| 52 | pub hclk: HclkConfig, | ||
| 53 | /// Main Clock config | ||
| 54 | pub main_clk: MainClkConfig, | ||
| 55 | /// Main Pll clock config | ||
| 56 | pub main_pll_clk: MainPllClkConfig, | ||
| 57 | /// Software concept to be used with systick, doesn't map to a register | ||
| 58 | pub sys_clk: SysClkConfig, | ||
| 59 | /// System Oscillator Config | ||
| 60 | pub sys_osc: SysOscConfig, | ||
| 61 | // todo: move ADC here | ||
| 62 | } | ||
| 63 | |||
| 64 | impl ClockConfig { | ||
| 65 | /// Clock configuration derived from external crystal. | ||
| 66 | #[must_use] | ||
| 67 | pub fn crystal() -> Self { | ||
| 68 | const CORE_CPU_FREQ: u32 = 500_000_000; | ||
| 69 | const PLL_CLK_FREQ: u32 = 528_000_000; | ||
| 70 | const SYS_CLK_FREQ: u32 = CORE_CPU_FREQ / 2; | ||
| 71 | Self { | ||
| 72 | lposc: LposcConfig { | ||
| 73 | state: State::Enabled, | ||
| 74 | freq: AtomicU32::new(Into::into(LposcFreq::Lp1m)), | ||
| 75 | }, | ||
| 76 | sfro: SfroConfig { state: State::Enabled }, | ||
| 77 | rtc: RtcClkConfig { | ||
| 78 | state: State::Enabled, | ||
| 79 | wake_alarm_state: State::Disabled, | ||
| 80 | sub_second_state: State::Disabled, | ||
| 81 | freq: AtomicU32::new(Into::into(RtcFreq::Default1Hz)), | ||
| 82 | rtc_int: RtcInterrupts::None, | ||
| 83 | }, | ||
| 84 | ffro: FfroConfig { | ||
| 85 | state: State::Enabled, | ||
| 86 | freq: AtomicU32::new(Into::into(FfroFreq::Ffro48m)), | ||
| 87 | }, | ||
| 88 | //pll: Some(PllConfig {}),//includes aux0 and aux1 pll | ||
| 89 | clk_in: ClkInConfig { | ||
| 90 | state: State::Disabled, | ||
| 91 | // This is an externally sourced clock | ||
| 92 | // Don't give it an initial frequency | ||
| 93 | freq: Some(AtomicU32::new(0)), | ||
| 94 | }, | ||
| 95 | hclk: HclkConfig { state: State::Disabled }, | ||
| 96 | main_clk: MainClkConfig { | ||
| 97 | state: State::Enabled, | ||
| 98 | //FFRO divided by 4 is reset values of Main Clk Sel A, Sel B | ||
| 99 | src: MainClkSrc::FFRO, | ||
| 100 | div_int: AtomicU32::new(4), | ||
| 101 | freq: AtomicU32::new(CORE_CPU_FREQ), | ||
| 102 | }, | ||
| 103 | main_pll_clk: MainPllClkConfig { | ||
| 104 | state: State::Enabled, | ||
| 105 | src: MainPllClkSrc::SFRO, | ||
| 106 | freq: AtomicU32::new(PLL_CLK_FREQ), | ||
| 107 | mult: AtomicU8::new(16), | ||
| 108 | pfd0: 19, // | ||
| 109 | pfd1: 0, // future field | ||
| 110 | pfd2: 19, // 0x13 | ||
| 111 | pfd3: 0, // future field | ||
| 112 | aux0_div: 0, | ||
| 113 | aux1_div: 0, | ||
| 114 | }, | ||
| 115 | sys_clk: SysClkConfig { | ||
| 116 | sysclkfreq: AtomicU32::new(SYS_CLK_FREQ), | ||
| 117 | }, | ||
| 118 | sys_osc: SysOscConfig { state: State::Enabled }, | ||
| 119 | //adc: Some(AdcConfig {}), // TODO: add config | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 125 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 126 | /// Clock state enum | ||
| 127 | pub enum State { | ||
| 128 | /// Clock is enabled | ||
| 129 | Enabled, | ||
| 130 | /// Clock is disabled | ||
| 131 | Disabled, | ||
| 132 | } | ||
| 133 | |||
| 134 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 135 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 136 | /// Low Power Oscillator valid frequencies | ||
| 137 | pub enum LposcFreq { | ||
| 138 | /// 1 `MHz` oscillator | ||
| 139 | Lp1m, | ||
| 140 | /// 32kHz oscillator | ||
| 141 | Lp32k, | ||
| 142 | } | ||
| 143 | |||
| 144 | impl From<LposcFreq> for u32 { | ||
| 145 | fn from(value: LposcFreq) -> Self { | ||
| 146 | match value { | ||
| 147 | LposcFreq::Lp1m => 1_000_000, | ||
| 148 | LposcFreq::Lp32k => 32_768, | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl TryFrom<u32> for LposcFreq { | ||
| 154 | type Error = ClockError; | ||
| 155 | fn try_from(value: u32) -> Result<Self, Self::Error> { | ||
| 156 | match value { | ||
| 157 | 1_000_000 => Ok(LposcFreq::Lp1m), | ||
| 158 | 32_768 => Ok(LposcFreq::Lp32k), | ||
| 159 | _ => Err(ClockError::InvalidFrequency), | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Low power oscillator config | ||
| 165 | pub struct LposcConfig { | ||
| 166 | state: State, | ||
| 167 | // low power osc | ||
| 168 | freq: AtomicU32, | ||
| 169 | } | ||
| 170 | |||
| 171 | const SFRO_FREQ: u32 = 16_000_000; | ||
| 172 | /// SFRO config | ||
| 173 | pub struct SfroConfig { | ||
| 174 | state: State, | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Valid RTC frequencies | ||
| 178 | pub enum RtcFreq { | ||
| 179 | /// "Alarm" aka 1Hz clock | ||
| 180 | Default1Hz, | ||
| 181 | /// "Wake" aka 1kHz clock | ||
| 182 | HighResolution1khz, | ||
| 183 | /// 32kHz clock | ||
| 184 | SubSecond32kHz, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl From<RtcFreq> for u32 { | ||
| 188 | fn from(value: RtcFreq) -> Self { | ||
| 189 | match value { | ||
| 190 | RtcFreq::Default1Hz => 1, | ||
| 191 | RtcFreq::HighResolution1khz => 1_000, | ||
| 192 | RtcFreq::SubSecond32kHz => 32_768, | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | impl TryFrom<u32> for RtcFreq { | ||
| 198 | type Error = ClockError; | ||
| 199 | fn try_from(value: u32) -> Result<Self, Self::Error> { | ||
| 200 | match value { | ||
| 201 | 1 => Ok(RtcFreq::Default1Hz), | ||
| 202 | 1_000 => Ok(RtcFreq::HighResolution1khz), | ||
| 203 | 32_768 => Ok(RtcFreq::SubSecond32kHz), | ||
| 204 | _ => Err(ClockError::InvalidFrequency), | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// RTC Interrupt options | ||
| 210 | pub enum RtcInterrupts { | ||
| 211 | /// No interrupts are set | ||
| 212 | None, | ||
| 213 | /// 1Hz RTC clock aka Alarm interrupt set | ||
| 214 | Alarm, | ||
| 215 | /// 1kHz RTC clock aka Wake interrupt set | ||
| 216 | Wake, | ||
| 217 | } | ||
| 218 | |||
| 219 | impl From<RtcInterrupts> for u8 { | ||
| 220 | fn from(value: RtcInterrupts) -> Self { | ||
| 221 | match value { | ||
| 222 | RtcInterrupts::None => 0b00, | ||
| 223 | RtcInterrupts::Alarm => 0b01, | ||
| 224 | RtcInterrupts::Wake => 0b10, | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | /// RTC clock config. | ||
| 229 | pub struct RtcClkConfig { | ||
| 230 | /// 1 Hz Clock state | ||
| 231 | pub state: State, | ||
| 232 | /// 1kHz Clock state | ||
| 233 | pub wake_alarm_state: State, | ||
| 234 | /// 32kHz Clock state | ||
| 235 | pub sub_second_state: State, | ||
| 236 | /// RTC clock source. | ||
| 237 | pub freq: AtomicU32, | ||
| 238 | /// RTC Interrupt | ||
| 239 | pub rtc_int: RtcInterrupts, | ||
| 240 | } | ||
| 241 | |||
| 242 | /// Valid FFRO Frequencies | ||
| 243 | pub enum FfroFreq { | ||
| 244 | /// 48 Mhz Internal Oscillator | ||
| 245 | Ffro48m, | ||
| 246 | /// 60 `MHz` Internal Oscillator | ||
| 247 | Ffro60m, | ||
| 248 | } | ||
| 249 | |||
| 250 | /// FFRO Clock Config | ||
| 251 | pub struct FfroConfig { | ||
| 252 | /// FFRO Clock state | ||
| 253 | state: State, | ||
| 254 | /// FFRO Frequency | ||
| 255 | freq: AtomicU32, | ||
| 256 | } | ||
| 257 | |||
| 258 | impl From<FfroFreq> for u32 { | ||
| 259 | fn from(value: FfroFreq) -> Self { | ||
| 260 | match value { | ||
| 261 | FfroFreq::Ffro48m => 48_000_000, | ||
| 262 | FfroFreq::Ffro60m => 60_000_000, | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | impl TryFrom<u32> for FfroFreq { | ||
| 268 | type Error = ClockError; | ||
| 269 | fn try_from(value: u32) -> Result<Self, Self::Error> { | ||
| 270 | match value { | ||
| 271 | 48_000_000 => Ok(FfroFreq::Ffro48m), | ||
| 272 | 60_000_000 => Ok(FfroFreq::Ffro60m), | ||
| 273 | _ => Err(ClockError::InvalidFrequency), | ||
| 274 | } | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | /// PLL clock source | ||
| 279 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 280 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 281 | pub enum MainPllClkSrc { | ||
| 282 | /// SFRO | ||
| 283 | SFRO, | ||
| 284 | /// External Clock | ||
| 285 | ClkIn, | ||
| 286 | /// FFRO | ||
| 287 | FFRO, | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Transform from Source Clock enum to Clocks | ||
| 291 | impl From<MainPllClkSrc> for Clocks { | ||
| 292 | fn from(value: MainPllClkSrc) -> Self { | ||
| 293 | match value { | ||
| 294 | MainPllClkSrc::SFRO => Clocks::Sfro, | ||
| 295 | MainPllClkSrc::ClkIn => Clocks::ClkIn, | ||
| 296 | MainPllClkSrc::FFRO => Clocks::Ffro, | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | impl TryFrom<Clocks> for MainPllClkSrc { | ||
| 302 | type Error = ClockError; | ||
| 303 | fn try_from(value: Clocks) -> Result<Self, Self::Error> { | ||
| 304 | match value { | ||
| 305 | Clocks::Sfro => Ok(MainPllClkSrc::SFRO), | ||
| 306 | Clocks::Ffro => Ok(MainPllClkSrc::FFRO), | ||
| 307 | Clocks::ClkIn => Ok(MainPllClkSrc::ClkIn), | ||
| 308 | _ => Err(ClockError::ClockNotSupported), | ||
| 309 | } | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | /// PLL configuration. | ||
| 314 | pub struct MainPllClkConfig { | ||
| 315 | /// Clock active state | ||
| 316 | pub state: State, | ||
| 317 | /// Main clock source. | ||
| 318 | pub src: MainPllClkSrc, | ||
| 319 | /// Main clock frequency | ||
| 320 | pub freq: AtomicU32, | ||
| 321 | //TODO: numerator and denominator not used but present in register | ||
| 322 | /// Multiplication factor. | ||
| 323 | pub mult: AtomicU8, | ||
| 324 | // the following are actually 6-bits not 8 | ||
| 325 | /// Fractional divider 0, main pll clock | ||
| 326 | pub pfd0: u8, | ||
| 327 | /// Fractional divider 1 | ||
| 328 | pub pfd1: u8, | ||
| 329 | /// Fractional divider 2 | ||
| 330 | pub pfd2: u8, | ||
| 331 | /// Fractional divider 3 | ||
| 332 | pub pfd3: u8, | ||
| 333 | // Aux dividers | ||
| 334 | /// aux divider 0 | ||
| 335 | pub aux0_div: u8, | ||
| 336 | /// aux divider 1 | ||
| 337 | pub aux1_div: u8, | ||
| 338 | } | ||
| 339 | /// External input clock config | ||
| 340 | pub struct ClkInConfig { | ||
| 341 | /// External clock input state | ||
| 342 | state: State, | ||
| 343 | /// External clock input rate | ||
| 344 | freq: Option<AtomicU32>, | ||
| 345 | } | ||
| 346 | |||
| 347 | /// AHB clock config | ||
| 348 | pub struct HclkConfig { | ||
| 349 | /// divider to turn main clk into hclk for AHB bus | ||
| 350 | pub state: State, | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Main clock source. | ||
| 354 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 355 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 356 | pub enum MainClkSrc { | ||
| 357 | /// FFRO divided by 4 | ||
| 358 | FFROdiv4, // probably don't need since it'll be covered by div_int | ||
| 359 | /// External Clock | ||
| 360 | ClkIn, | ||
| 361 | /// Low Power Oscillator | ||
| 362 | Lposc, | ||
| 363 | /// FFRO | ||
| 364 | FFRO, | ||
| 365 | /// SFRO | ||
| 366 | SFRO, | ||
| 367 | /// Main PLL Clock | ||
| 368 | PllMain, | ||
| 369 | /// RTC 32kHz oscillator. | ||
| 370 | RTC32k, | ||
| 371 | } | ||
| 372 | |||
| 373 | impl From<MainClkSrc> for Clocks { | ||
| 374 | fn from(value: MainClkSrc) -> Self { | ||
| 375 | match value { | ||
| 376 | MainClkSrc::ClkIn => Clocks::ClkIn, | ||
| 377 | MainClkSrc::Lposc => Clocks::Lposc, | ||
| 378 | MainClkSrc::FFRO => Clocks::Ffro, | ||
| 379 | MainClkSrc::SFRO => Clocks::Sfro, | ||
| 380 | MainClkSrc::PllMain => Clocks::MainPllClk, | ||
| 381 | MainClkSrc::RTC32k => Clocks::Rtc, | ||
| 382 | MainClkSrc::FFROdiv4 => Clocks::Ffro, | ||
| 383 | } | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | impl TryFrom<Clocks> for MainClkSrc { | ||
| 388 | type Error = ClockError; | ||
| 389 | fn try_from(value: Clocks) -> Result<Self, Self::Error> { | ||
| 390 | match value { | ||
| 391 | Clocks::ClkIn => Ok(MainClkSrc::ClkIn), | ||
| 392 | Clocks::Lposc => Ok(MainClkSrc::Lposc), | ||
| 393 | Clocks::Sfro => Ok(MainClkSrc::SFRO), | ||
| 394 | Clocks::MainPllClk => Ok(MainClkSrc::PllMain), | ||
| 395 | Clocks::Rtc => Ok(MainClkSrc::RTC32k), | ||
| 396 | Clocks::Ffro => Ok(MainClkSrc::FFRO), | ||
| 397 | _ => Err(ClockError::ClockNotSupported), | ||
| 398 | } | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | /// Main clock config. | ||
| 403 | pub struct MainClkConfig { | ||
| 404 | /// Main clock state | ||
| 405 | pub state: State, | ||
| 406 | /// Main clock source. | ||
| 407 | pub src: MainClkSrc, | ||
| 408 | /// Main clock divider. | ||
| 409 | pub div_int: AtomicU32, | ||
| 410 | /// Clock Frequency | ||
| 411 | pub freq: AtomicU32, | ||
| 412 | } | ||
| 413 | |||
| 414 | /// System Core Clock config, SW concept for systick | ||
| 415 | pub struct SysClkConfig { | ||
| 416 | /// keeps track of the system core clock frequency | ||
| 417 | /// future use with systick | ||
| 418 | pub sysclkfreq: AtomicU32, | ||
| 419 | } | ||
| 420 | |||
| 421 | /// System Oscillator Config | ||
| 422 | pub struct SysOscConfig { | ||
| 423 | /// Clock State | ||
| 424 | pub state: State, | ||
| 425 | } | ||
| 426 | const SYS_OSC_DEFAULT_FREQ: u32 = 24_000_000; | ||
| 427 | |||
| 428 | /// Clock Errors | ||
| 429 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 430 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 431 | pub enum ClockError { | ||
| 432 | /// Error due to attempting to change a clock with the wrong config block | ||
| 433 | ClockMismatch, | ||
| 434 | /// Error due to attempting to modify a clock that's not yet been enabled | ||
| 435 | ClockNotEnabled, | ||
| 436 | /// Error due to attempting to set a clock source that's not a supported option | ||
| 437 | ClockNotSupported, | ||
| 438 | /// Error due to attempting to set a clock to an invalid frequency | ||
| 439 | InvalidFrequency, | ||
| 440 | /// Error due to attempting to modify a clock output with an invalid divider | ||
| 441 | InvalidDiv, | ||
| 442 | /// Error due to attempting to modify a clock output with an invalid multiplier | ||
| 443 | InvalidMult, | ||
| 444 | } | ||
| 445 | |||
| 446 | /// Trait to configure one of the clocks | ||
| 447 | pub trait ConfigurableClock { | ||
| 448 | /// Reset the clock, will enable it | ||
| 449 | fn disable(&self) -> Result<(), ClockError>; | ||
| 450 | /// Enable the clock | ||
| 451 | fn enable_and_reset(&self) -> Result<(), ClockError>; | ||
| 452 | /// Return the clock rate (Hz) | ||
| 453 | fn get_clock_rate(&self) -> Result<u32, ClockError>; | ||
| 454 | /// Set the desired clock rate (Hz) | ||
| 455 | fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError>; | ||
| 456 | /// Returns whether this clock is enabled | ||
| 457 | fn is_enabled(&self) -> bool; | ||
| 458 | } | ||
| 459 | |||
| 460 | impl LposcConfig { | ||
| 461 | /// Initializes low-power oscillator. | ||
| 462 | fn init_lposc() { | ||
| 463 | // Enable low power oscillator | ||
| 464 | // SAFETY: unsafe needed to take pointer to Sysctl0, only happens once during init | ||
| 465 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 466 | sysctl0.pdruncfg0_clr().write(|w| w.lposc_pd().clr_pdruncfg0()); | ||
| 467 | |||
| 468 | // Wait for low-power oscillator to be ready (typically 64 us) | ||
| 469 | // Busy loop seems better here than trying to shoe-in an async delay | ||
| 470 | // SAFETY: unsafe needed to take pointer to Clkctl0, needed to validate HW is ready | ||
| 471 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 472 | while clkctl0.lposcctl0().read().clkrdy().bit_is_clear() {} | ||
| 473 | } | ||
| 474 | } | ||
| 475 | impl ConfigurableClock for LposcConfig { | ||
| 476 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 477 | LposcConfig::init_lposc(); | ||
| 478 | Ok(()) | ||
| 479 | } | ||
| 480 | fn disable(&self) -> Result<(), ClockError> { | ||
| 481 | // SAFETY: unsafe needed to take pointer to Sysctl0, needed to power down the LPOSC HW | ||
| 482 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 483 | sysctl0.pdruncfg0_set().write(|w| w.lposc_pd().set_pdruncfg0()); | ||
| 484 | // Wait until LPOSC disabled | ||
| 485 | while !sysctl0.pdruncfg0().read().lposc_pd().is_power_down() {} | ||
| 486 | Ok(()) | ||
| 487 | } | ||
| 488 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 489 | Ok(self.freq.load(Ordering::Relaxed)) | ||
| 490 | } | ||
| 491 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 492 | if let Ok(r) = <u32 as TryInto<LposcFreq>>::try_into(freq) { | ||
| 493 | match r { | ||
| 494 | LposcFreq::Lp1m => { | ||
| 495 | self.freq | ||
| 496 | .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed); | ||
| 497 | Ok(()) | ||
| 498 | } | ||
| 499 | LposcFreq::Lp32k => { | ||
| 500 | self.freq | ||
| 501 | .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed); | ||
| 502 | Ok(()) | ||
| 503 | } | ||
| 504 | } | ||
| 505 | } else { | ||
| 506 | error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq); | ||
| 507 | Err(ClockError::InvalidFrequency) | ||
| 508 | } | ||
| 509 | } | ||
| 510 | fn is_enabled(&self) -> bool { | ||
| 511 | self.state == State::Enabled | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | impl FfroConfig { | ||
| 516 | /// Necessary register writes to initialize the FFRO clock | ||
| 517 | pub fn init_ffro_clk() { | ||
| 518 | // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up FFRO | ||
| 519 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 520 | |||
| 521 | /* Power on FFRO (48/60MHz) */ | ||
| 522 | sysctl0.pdruncfg0_clr().write(|w| w.ffro_pd().clr_pdruncfg0()); | ||
| 523 | |||
| 524 | // SAFETY: unsafe needed to take pointer to Clkctl0, only to set proper ffro update mode | ||
| 525 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 526 | |||
| 527 | clkctl0.ffroctl1().write(|w| w.update().normal_mode()); | ||
| 528 | |||
| 529 | // No FFRO enable/disable control in CLKCTL. | ||
| 530 | // Delay enough for FFRO to be stable in case it was just powered on | ||
| 531 | delay_loop_clocks(50, 12_000_000); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | impl ConfigurableClock for FfroConfig { | ||
| 536 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 537 | // SAFETY: should be called once | ||
| 538 | FfroConfig::init_ffro_clk(); | ||
| 539 | // default is 48 MHz | ||
| 540 | Ok(()) | ||
| 541 | } | ||
| 542 | fn disable(&self) -> Result<(), ClockError> { | ||
| 543 | // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down FFRO | ||
| 544 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 545 | sysctl0.pdruncfg0_set().write(|w| w.ffro_pd().set_pdruncfg0()); | ||
| 546 | delay_loop_clocks(30, 12_000_000); | ||
| 547 | // Wait until FFRO disabled | ||
| 548 | while !sysctl0.pdruncfg0().read().ffro_pd().is_power_down() {} | ||
| 549 | Ok(()) | ||
| 550 | } | ||
| 551 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 552 | trace!("getting ffro clock rate"); | ||
| 553 | Ok(self.freq.load(Ordering::Relaxed)) | ||
| 554 | } | ||
| 555 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 556 | if let Ok(r) = <u32 as TryInto<FfroFreq>>::try_into(freq) { | ||
| 557 | match r { | ||
| 558 | FfroFreq::Ffro48m => { | ||
| 559 | // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency | ||
| 560 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 561 | clkctl0.ffroctl1().write(|w| w.update().update_safe_mode()); | ||
| 562 | clkctl0.ffroctl0().write(|w| w.trim_range().ffro_48mhz()); | ||
| 563 | clkctl0.ffroctl1().write(|w| w.update().normal_mode()); | ||
| 564 | |||
| 565 | self.freq | ||
| 566 | .store(FfroFreq::Ffro48m as u32, core::sync::atomic::Ordering::Relaxed); | ||
| 567 | Ok(()) | ||
| 568 | } | ||
| 569 | FfroFreq::Ffro60m => { | ||
| 570 | // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency | ||
| 571 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 572 | clkctl0.ffroctl1().write(|w| w.update().update_safe_mode()); | ||
| 573 | clkctl0.ffroctl0().write(|w| w.trim_range().ffro_60mhz()); | ||
| 574 | clkctl0.ffroctl1().write(|w| w.update().normal_mode()); | ||
| 575 | |||
| 576 | self.freq | ||
| 577 | .store(FfroFreq::Ffro60m as u32, core::sync::atomic::Ordering::Relaxed); | ||
| 578 | Ok(()) | ||
| 579 | } | ||
| 580 | } | ||
| 581 | } else { | ||
| 582 | error!("failed to convert desired clock rate, {:#}, to FFRO Freq", freq); | ||
| 583 | Err(ClockError::InvalidFrequency) | ||
| 584 | } | ||
| 585 | } | ||
| 586 | fn is_enabled(&self) -> bool { | ||
| 587 | self.state == State::Enabled | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | impl ConfigurableClock for SfroConfig { | ||
| 592 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 593 | // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up SFRO | ||
| 594 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 595 | sysctl0.pdruncfg0_clr().write(|w| w.sfro_pd().clr_pdruncfg0()); | ||
| 596 | // wait until ready | ||
| 597 | while !sysctl0.pdruncfg0().read().sfro_pd().is_enabled() {} | ||
| 598 | Ok(()) | ||
| 599 | } | ||
| 600 | fn disable(&self) -> Result<(), ClockError> { | ||
| 601 | // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down SFRO | ||
| 602 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 603 | sysctl0.pdruncfg0_set().write(|w| w.sfro_pd().set_pdruncfg0()); | ||
| 604 | delay_loop_clocks(30, 12_000_000); | ||
| 605 | // Wait until SFRO disabled | ||
| 606 | while !sysctl0.pdruncfg0().read().sfro_pd().is_power_down() {} | ||
| 607 | Ok(()) | ||
| 608 | } | ||
| 609 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 610 | if self.state == State::Enabled { | ||
| 611 | Ok(SFRO_FREQ) | ||
| 612 | } else { | ||
| 613 | Err(ClockError::ClockNotEnabled) | ||
| 614 | } | ||
| 615 | } | ||
| 616 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 617 | if self.state == State::Enabled { | ||
| 618 | if freq == SFRO_FREQ { | ||
| 619 | trace!("Sfro frequency is already set at 16MHz"); | ||
| 620 | Ok(()) | ||
| 621 | } else { | ||
| 622 | Err(ClockError::InvalidFrequency) | ||
| 623 | } | ||
| 624 | } else { | ||
| 625 | Err(ClockError::ClockNotEnabled) | ||
| 626 | } | ||
| 627 | } | ||
| 628 | fn is_enabled(&self) -> bool { | ||
| 629 | self.state == State::Enabled | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | /// A Clock with multiple options for clock source | ||
| 634 | pub trait MultiSourceClock { | ||
| 635 | /// Returns which clock is being used as the clock source and its rate | ||
| 636 | fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError>; | ||
| 637 | /// Sets a specific clock source and its associated rate | ||
| 638 | fn set_clock_source_and_rate( | ||
| 639 | &mut self, | ||
| 640 | clock_src_config: &mut impl ConfigurableClock, | ||
| 641 | clock_src: &Clocks, | ||
| 642 | rate: u32, | ||
| 643 | ) -> Result<(), ClockError>; | ||
| 644 | } | ||
| 645 | |||
| 646 | impl MultiSourceClock for MainPllClkConfig { | ||
| 647 | fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> { | ||
| 648 | match clock { | ||
| 649 | Clocks::MainPllClk => { | ||
| 650 | let converted_clock = Clocks::from(self.src); | ||
| 651 | Ok((converted_clock, self.freq.load(Ordering::Relaxed))) | ||
| 652 | } | ||
| 653 | _ => Err(ClockError::ClockMismatch), | ||
| 654 | } | ||
| 655 | } | ||
| 656 | fn set_clock_source_and_rate( | ||
| 657 | &mut self, | ||
| 658 | clock_src_config: &mut impl ConfigurableClock, | ||
| 659 | clock_src: &Clocks, | ||
| 660 | rate: u32, | ||
| 661 | ) -> Result<(), ClockError> { | ||
| 662 | if let Ok(c) = <Clocks as TryInto<MainPllClkSrc>>::try_into(*clock_src) { | ||
| 663 | match c { | ||
| 664 | MainPllClkSrc::ClkIn => { | ||
| 665 | self.src = MainPllClkSrc::ClkIn; | ||
| 666 | // div mult and rate don't matter since this is an external clock | ||
| 667 | self.set_clock_rate(1, 1, rate) | ||
| 668 | } | ||
| 669 | MainPllClkSrc::FFRO => { | ||
| 670 | // FFRO Clock is divided by 2 | ||
| 671 | let r = clock_src_config.get_clock_rate()?; | ||
| 672 | let base_rate = r / 2; | ||
| 673 | let m = MainPllClkConfig::calc_mult(rate, base_rate)?; | ||
| 674 | |||
| 675 | self.src = MainPllClkSrc::FFRO; | ||
| 676 | self.set_clock_rate(2, m, rate) | ||
| 677 | } | ||
| 678 | MainPllClkSrc::SFRO => { | ||
| 679 | if !clock_src_config.is_enabled() { | ||
| 680 | error!("Can't set SFRO as source for MainPll as it's not enabled"); | ||
| 681 | return Err(ClockError::ClockNotEnabled); | ||
| 682 | } | ||
| 683 | // check if desired frequency is a valid multiple of 16m SFRO clock | ||
| 684 | let m = MainPllClkConfig::calc_mult(rate, SFRO_FREQ)?; | ||
| 685 | self.src = MainPllClkSrc::SFRO; | ||
| 686 | self.set_clock_rate(1, m, rate) | ||
| 687 | } | ||
| 688 | } | ||
| 689 | } else { | ||
| 690 | Err(ClockError::ClockNotSupported) | ||
| 691 | } | ||
| 692 | } | ||
| 693 | } | ||
| 694 | |||
| 695 | impl ConfigurableClock for MainPllClkConfig { | ||
| 696 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 697 | MainPllClkConfig::init_syspll(); | ||
| 698 | |||
| 699 | MainPllClkConfig::init_syspll_pfd0(self.pfd0); | ||
| 700 | |||
| 701 | MainPllClkConfig::init_syspll_pfd2(self.pfd2); | ||
| 702 | Ok(()) | ||
| 703 | } | ||
| 704 | fn disable(&self) -> Result<(), ClockError> { | ||
| 705 | if self.is_enabled() { | ||
| 706 | error!("Attempting to reset the Main Pll Clock, should be resetting its source"); | ||
| 707 | Err(ClockError::ClockNotSupported) | ||
| 708 | } else { | ||
| 709 | Err(ClockError::ClockNotEnabled) | ||
| 710 | } | ||
| 711 | } | ||
| 712 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 713 | if self.is_enabled() { | ||
| 714 | let (_c, rate) = self.get_clock_source_and_rate(&Clocks::MainPllClk)?; | ||
| 715 | Ok(rate) | ||
| 716 | } else { | ||
| 717 | Err(ClockError::ClockNotEnabled) | ||
| 718 | } | ||
| 719 | } | ||
| 720 | fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 721 | if self.is_enabled() { | ||
| 722 | trace!("attempting to set main pll clock rate"); | ||
| 723 | // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 | ||
| 724 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 725 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 726 | |||
| 727 | // Power down pll before changes | ||
| 728 | sysctl0 | ||
| 729 | .pdruncfg0_set() | ||
| 730 | .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0()); | ||
| 731 | |||
| 732 | let desired_freq: u64 = self.freq.load(Ordering::Relaxed).into(); | ||
| 733 | |||
| 734 | match self.src { | ||
| 735 | c if c == MainPllClkSrc::ClkIn || c == MainPllClkSrc::FFRO || c == MainPllClkSrc::SFRO => { | ||
| 736 | let mut base_rate; | ||
| 737 | match c { | ||
| 738 | MainPllClkSrc::ClkIn => { | ||
| 739 | clkctl0.syspll0clksel().write(|w| w.sel().sysxtal_clk()); | ||
| 740 | let r = self.get_clock_rate()?; | ||
| 741 | base_rate = r; | ||
| 742 | } | ||
| 743 | MainPllClkSrc::FFRO => { | ||
| 744 | trace!("found FFRO as source, wait a bit"); | ||
| 745 | delay_loop_clocks(1000, desired_freq); | ||
| 746 | match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() { | ||
| 747 | true => base_rate = Into::into(FfroFreq::Ffro48m), | ||
| 748 | false => base_rate = Into::into(FfroFreq::Ffro60m), | ||
| 749 | } | ||
| 750 | trace!("found ffro rate to be: {:#}", base_rate); | ||
| 751 | if div == 2 { | ||
| 752 | trace!("dividing FFRO rate by 2"); | ||
| 753 | clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); | ||
| 754 | delay_loop_clocks(150, desired_freq); | ||
| 755 | base_rate /= 2; | ||
| 756 | } else { | ||
| 757 | return Err(ClockError::InvalidDiv); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | MainPllClkSrc::SFRO => { | ||
| 761 | base_rate = SFRO_FREQ; | ||
| 762 | clkctl0.syspll0clksel().write(|w| w.sel().sfro_clk()); | ||
| 763 | } | ||
| 764 | }; | ||
| 765 | base_rate *= u32::from(mult); | ||
| 766 | trace!("calculated base rate at: {:#}", base_rate); | ||
| 767 | if base_rate != freq { | ||
| 768 | // make sure to power syspll back up before returning the error | ||
| 769 | error!("invalid frequency found, powering syspll back up before returning error. Check div and mult"); | ||
| 770 | // Clear System PLL reset | ||
| 771 | clkctl0.syspll0ctl0().write(|w| w.reset().normal()); | ||
| 772 | // Power up SYSPLL | ||
| 773 | sysctl0 | ||
| 774 | .pdruncfg0_clr() | ||
| 775 | .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); | ||
| 776 | return Err(ClockError::InvalidFrequency); | ||
| 777 | } | ||
| 778 | trace!("setting default num and denom"); | ||
| 779 | // SAFETY: unsafe needed to write the bits for the num and demon fields | ||
| 780 | clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) }); | ||
| 781 | clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) }); | ||
| 782 | delay_loop_clocks(30, desired_freq); | ||
| 783 | self.mult.store(mult, Ordering::Relaxed); | ||
| 784 | trace!("setting self.mult as: {:#}", mult); | ||
| 785 | match mult { | ||
| 786 | 16 => { | ||
| 787 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16()); | ||
| 788 | } | ||
| 789 | 17 => { | ||
| 790 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_17()); | ||
| 791 | } | ||
| 792 | 20 => { | ||
| 793 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_20()); | ||
| 794 | } | ||
| 795 | 22 => { | ||
| 796 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_22()); | ||
| 797 | } | ||
| 798 | 27 => { | ||
| 799 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_27()); | ||
| 800 | } | ||
| 801 | 33 => { | ||
| 802 | clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_33()); | ||
| 803 | } | ||
| 804 | _ => return Err(ClockError::InvalidMult), | ||
| 805 | } | ||
| 806 | trace!("clear syspll reset"); | ||
| 807 | // Clear System PLL reset | ||
| 808 | clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal()); | ||
| 809 | // Power up SYSPLL | ||
| 810 | sysctl0 | ||
| 811 | .pdruncfg0_clr() | ||
| 812 | .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); | ||
| 813 | |||
| 814 | // Set System PLL HOLDRINGOFF_ENA | ||
| 815 | clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable()); | ||
| 816 | delay_loop_clocks(75, desired_freq); | ||
| 817 | |||
| 818 | // Clear System PLL HOLDRINGOFF_ENA | ||
| 819 | clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); | ||
| 820 | delay_loop_clocks(15, desired_freq); | ||
| 821 | |||
| 822 | trace!("setting new PFD0 bits"); | ||
| 823 | // gate the output and clear bits. | ||
| 824 | // SAFETY: unsafe needed to write the bits for pfd0 | ||
| 825 | clkctl0 | ||
| 826 | .syspll0pfd() | ||
| 827 | .modify(|_, w| unsafe { w.pfd0_clkgate().gated().pfd0().bits(0x0) }); | ||
| 828 | // set pfd bits and un-gate the clock output | ||
| 829 | // output is multiplied by syspll * 18/pfd0_bits | ||
| 830 | // SAFETY: unsafe needed to write the bits for pfd0 | ||
| 831 | clkctl0 | ||
| 832 | .syspll0pfd() | ||
| 833 | .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) }); | ||
| 834 | // wait for ready bit to be set | ||
| 835 | delay_loop_clocks(50, desired_freq); | ||
| 836 | trace!("waiting for mainpll clock to be ready"); | ||
| 837 | while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} | ||
| 838 | // clear by writing a 1 | ||
| 839 | clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit()); | ||
| 840 | |||
| 841 | Ok(()) | ||
| 842 | } | ||
| 843 | _ => Err(ClockError::ClockNotSupported), | ||
| 844 | } | ||
| 845 | } else { | ||
| 846 | Err(ClockError::ClockNotEnabled) | ||
| 847 | } | ||
| 848 | } | ||
| 849 | fn is_enabled(&self) -> bool { | ||
| 850 | self.state == State::Enabled | ||
| 851 | } | ||
| 852 | } | ||
| 853 | |||
| 854 | impl MainPllClkConfig { | ||
| 855 | /// Calculate the mult value of a desired frequency, return error if invalid | ||
| 856 | pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result<u8, ClockError> { | ||
| 857 | trace!("calculating mult for {:#} / {:#}", rate, base_freq); | ||
| 858 | const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33]; | ||
| 859 | if rate > base_freq && rate % base_freq == 0 { | ||
| 860 | let mult = (rate / base_freq) as u8; | ||
| 861 | trace!("verifying that calculated mult {:#} is a valid one", mult); | ||
| 862 | if VALIDMULTS.into_iter().any(|i| i == mult) { | ||
| 863 | Ok(mult) | ||
| 864 | } else { | ||
| 865 | Err(ClockError::InvalidFrequency) | ||
| 866 | } | ||
| 867 | } else { | ||
| 868 | Err(ClockError::InvalidFrequency) | ||
| 869 | } | ||
| 870 | } | ||
| 871 | pub(self) fn init_syspll() { | ||
| 872 | // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 | ||
| 873 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 874 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 875 | |||
| 876 | // Power down SYSPLL before change fractional settings | ||
| 877 | sysctl0 | ||
| 878 | .pdruncfg0_set() | ||
| 879 | .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0()); | ||
| 880 | |||
| 881 | clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); | ||
| 882 | // SAFETY: unsafe needed to write the bits for both num and denom | ||
| 883 | clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0x0) }); | ||
| 884 | clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0x1) }); | ||
| 885 | |||
| 886 | // kCLOCK_SysPllMult22 | ||
| 887 | clkctl0.syspll0ctl0().modify(|_, w| w.mult().div_22()); | ||
| 888 | |||
| 889 | // Clear System PLL reset | ||
| 890 | clkctl0.syspll0ctl0().modify(|_, w| w.reset().normal()); | ||
| 891 | |||
| 892 | // Power up SYSPLL | ||
| 893 | sysctl0 | ||
| 894 | .pdruncfg0_clr() | ||
| 895 | .write(|w| w.syspllldo_pd().clr_pdruncfg0().syspllana_pd().clr_pdruncfg0()); | ||
| 896 | delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000); | ||
| 897 | |||
| 898 | // Set System PLL HOLDRINGOFF_ENA | ||
| 899 | clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable()); | ||
| 900 | delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000); | ||
| 901 | |||
| 902 | // Clear System PLL HOLDRINGOFF_ENA | ||
| 903 | clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); | ||
| 904 | delay_loop_clocks((15 & 0xFFFF) / 2, 12_000_000); | ||
| 905 | } | ||
| 906 | /// enables default settings for pfd2 bits | ||
| 907 | pub(self) fn init_syspll_pfd2(config_bits: u8) { | ||
| 908 | // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits | ||
| 909 | // needed to change the output of pfd0 | ||
| 910 | unsafe { | ||
| 911 | let clkctl0 = crate::pac::Clkctl0::steal(); | ||
| 912 | |||
| 913 | // Disable the clock output first. | ||
| 914 | // SAFETY: unsafe needed to write the bits for pfd2 | ||
| 915 | clkctl0 | ||
| 916 | .syspll0pfd() | ||
| 917 | .modify(|_, w| w.pfd2_clkgate().gated().pfd2().bits(0x0)); | ||
| 918 | |||
| 919 | // Set the new value and enable output. | ||
| 920 | // SAFETY: unsafe needed to write the bits for pfd2 | ||
| 921 | clkctl0 | ||
| 922 | .syspll0pfd() | ||
| 923 | .modify(|_, w| w.pfd2_clkgate().not_gated().pfd2().bits(config_bits)); | ||
| 924 | |||
| 925 | // Wait for output becomes stable. | ||
| 926 | while clkctl0.syspll0pfd().read().pfd2_clkrdy().bit_is_clear() {} | ||
| 927 | |||
| 928 | // Clear ready status flag. | ||
| 929 | clkctl0.syspll0pfd().modify(|_, w| w.pfd2_clkrdy().clear_bit()); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | /// Enables default settings for pfd0 | ||
| 933 | pub(self) fn init_syspll_pfd0(config_bits: u8) { | ||
| 934 | // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits | ||
| 935 | // needed to change the output of pfd0 | ||
| 936 | unsafe { | ||
| 937 | let clkctl0 = crate::pac::Clkctl0::steal(); | ||
| 938 | // Disable the clock output first | ||
| 939 | clkctl0 | ||
| 940 | .syspll0pfd() | ||
| 941 | .modify(|_, w| w.pfd0_clkgate().gated().pfd0().bits(0x0)); | ||
| 942 | |||
| 943 | // Set the new value and enable output | ||
| 944 | clkctl0 | ||
| 945 | .syspll0pfd() | ||
| 946 | .modify(|_, w| w.pfd0_clkgate().not_gated().pfd0().bits(config_bits)); | ||
| 947 | |||
| 948 | // Wait for output becomes stable | ||
| 949 | while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} | ||
| 950 | |||
| 951 | // Clear ready status flag | ||
| 952 | clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().clear_bit()); | ||
| 953 | } | ||
| 954 | } | ||
| 955 | } | ||
| 956 | |||
| 957 | impl MainClkConfig { | ||
| 958 | fn init_main_clk() { | ||
| 959 | // SAFETY:: unsafe needed to take pointers to Clkctl0 and Clkctl1 | ||
| 960 | // used to set the right HW frequency | ||
| 961 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 962 | let clkctl1 = unsafe { crate::pac::Clkctl1::steal() }; | ||
| 963 | |||
| 964 | clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk()); | ||
| 965 | |||
| 966 | // Set PFC0DIV divider to value 2, Subtract 1 since 0-> 1, 1-> 2, etc... | ||
| 967 | clkctl0.pfcdiv(0).modify(|_, w| w.reset().set_bit()); | ||
| 968 | // SAFETY: unsafe needed to write the bits for pfcdiv | ||
| 969 | clkctl0 | ||
| 970 | .pfcdiv(0) | ||
| 971 | .write(|w| unsafe { w.div().bits(2 - 1).halt().clear_bit() }); | ||
| 972 | while clkctl0.pfcdiv(0).read().reqflag().bit_is_set() {} | ||
| 973 | |||
| 974 | // Set FRGPLLCLKDIV divider to value 12, Subtract 1 since 0-> 1, 1-> 2, etc... | ||
| 975 | clkctl1.frgpllclkdiv().modify(|_, w| w.reset().set_bit()); | ||
| 976 | // SAFETY: unsafe needed to write the bits for frgpllclkdiv | ||
| 977 | clkctl1 | ||
| 978 | .frgpllclkdiv() | ||
| 979 | .write(|w| unsafe { w.div().bits(12 - 1).halt().clear_bit() }); | ||
| 980 | while clkctl1.frgpllclkdiv().read().reqflag().bit_is_set() {} | ||
| 981 | } | ||
| 982 | } | ||
| 983 | impl MultiSourceClock for MainClkConfig { | ||
| 984 | fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> { | ||
| 985 | match clock { | ||
| 986 | Clocks::MainClk => { | ||
| 987 | let div: u32 = if self.src == MainClkSrc::FFROdiv4 { 4 } else { 1 }; | ||
| 988 | let converted_clock = Clocks::from(self.src); | ||
| 989 | match ConfigurableClock::get_clock_rate(self) { | ||
| 990 | Ok(_rate) => { | ||
| 991 | // SAFETY: unsafe needed to take pointer to Clkctl0 | ||
| 992 | // needed to calculate the clock rate from the bits written in the registers | ||
| 993 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 994 | if self.src == MainClkSrc::PllMain && clkctl0.syspll0ctl0().read().bypass().is_programmed_clk() | ||
| 995 | { | ||
| 996 | let mut temp; | ||
| 997 | temp = self.freq.load(Ordering::Relaxed) | ||
| 998 | * u32::from(clkctl0.syspll0ctl0().read().mult().bits()); | ||
| 999 | temp = (u64::from(temp) * 18 / u64::from(clkctl0.syspll0pfd().read().pfd0().bits())) as u32; | ||
| 1000 | return Ok((converted_clock, temp)); | ||
| 1001 | } | ||
| 1002 | Ok((converted_clock, self.freq.load(Ordering::Relaxed) / div)) | ||
| 1003 | } | ||
| 1004 | Err(clk_err) => Err(clk_err), | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | _ => Err(ClockError::ClockMismatch), | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | fn set_clock_source_and_rate( | ||
| 1011 | &mut self, | ||
| 1012 | clock_src_config: &mut impl ConfigurableClock, | ||
| 1013 | clock_src: &Clocks, | ||
| 1014 | rate: u32, | ||
| 1015 | ) -> Result<(), ClockError> { | ||
| 1016 | if !clock_src_config.is_enabled() { | ||
| 1017 | return Err(ClockError::ClockNotEnabled); | ||
| 1018 | } | ||
| 1019 | if let Ok(c) = <Clocks as TryInto<MainClkSrc>>::try_into(*clock_src) { | ||
| 1020 | // SAFETY: unsafe needed to take pointer to Clkctl0 | ||
| 1021 | // needed to change the clock source | ||
| 1022 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 1023 | match c { | ||
| 1024 | MainClkSrc::ClkIn => { | ||
| 1025 | self.src = MainClkSrc::ClkIn; | ||
| 1026 | |||
| 1027 | clkctl0.mainclksela().write(|w| w.sel().sysxtal_clk()); | ||
| 1028 | clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); | ||
| 1029 | Ok(()) | ||
| 1030 | } | ||
| 1031 | // the following will yield the same result as if compared to FFROdiv4 | ||
| 1032 | MainClkSrc::FFRO | MainClkSrc::FFROdiv4 => match rate { | ||
| 1033 | div4 if div4 == (FfroFreq::Ffro60m as u32) / 4 || div4 == (FfroFreq::Ffro48m as u32) / 4 => { | ||
| 1034 | self.src = MainClkSrc::FFROdiv4; | ||
| 1035 | self.freq.store(div4, Ordering::Relaxed); | ||
| 1036 | |||
| 1037 | clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); | ||
| 1038 | clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); | ||
| 1039 | Ok(()) | ||
| 1040 | } | ||
| 1041 | div1 if div1 == FfroFreq::Ffro60m as u32 || div1 == FfroFreq::Ffro48m as u32 => { | ||
| 1042 | self.src = MainClkSrc::FFRO; | ||
| 1043 | self.freq.store(div1, Ordering::Relaxed); | ||
| 1044 | |||
| 1045 | clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); | ||
| 1046 | clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); | ||
| 1047 | Ok(()) | ||
| 1048 | } | ||
| 1049 | _ => Err(ClockError::InvalidFrequency), | ||
| 1050 | }, | ||
| 1051 | MainClkSrc::Lposc => { | ||
| 1052 | if let Ok(r) = <u32 as TryInto<LposcFreq>>::try_into(rate) { | ||
| 1053 | match r { | ||
| 1054 | LposcFreq::Lp1m => { | ||
| 1055 | self.src = MainClkSrc::Lposc; | ||
| 1056 | self.freq.store(rate, Ordering::Relaxed); | ||
| 1057 | |||
| 1058 | clkctl0.mainclksela().write(|w| w.sel().lposc()); | ||
| 1059 | clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk()); | ||
| 1060 | Ok(()) | ||
| 1061 | } | ||
| 1062 | LposcFreq::Lp32k => Err(ClockError::InvalidFrequency), | ||
| 1063 | } | ||
| 1064 | } else { | ||
| 1065 | Err(ClockError::InvalidFrequency) | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | MainClkSrc::SFRO => { | ||
| 1069 | if rate == SFRO_FREQ { | ||
| 1070 | self.src = MainClkSrc::SFRO; | ||
| 1071 | self.freq.store(rate, Ordering::Relaxed); | ||
| 1072 | clkctl0.mainclkselb().write(|w| w.sel().sfro_clk()); | ||
| 1073 | Ok(()) | ||
| 1074 | } else { | ||
| 1075 | Err(ClockError::InvalidFrequency) | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | MainClkSrc::PllMain => { | ||
| 1079 | let r = rate; | ||
| 1080 | // From Section 4.6.1.1 Pll Limitations of the RT6xx User manual | ||
| 1081 | let pll_max = 572_000_000; | ||
| 1082 | let pll_min = 80_000_000; | ||
| 1083 | if pll_min <= r && r <= pll_max { | ||
| 1084 | clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk()); | ||
| 1085 | self.src = MainClkSrc::PllMain; | ||
| 1086 | self.freq.store(r, Ordering::Relaxed); | ||
| 1087 | Ok(()) | ||
| 1088 | } else { | ||
| 1089 | Err(ClockError::InvalidFrequency) | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | MainClkSrc::RTC32k => { | ||
| 1093 | if rate == RtcFreq::SubSecond32kHz as u32 { | ||
| 1094 | self.src = MainClkSrc::RTC32k; | ||
| 1095 | self.freq.store(rate, Ordering::Relaxed); | ||
| 1096 | clkctl0.mainclkselb().write(|w| w.sel().rtc_32k_clk()); | ||
| 1097 | Ok(()) | ||
| 1098 | } else { | ||
| 1099 | Err(ClockError::InvalidFrequency) | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | } | ||
| 1103 | } else { | ||
| 1104 | Err(ClockError::ClockNotSupported) | ||
| 1105 | } | ||
| 1106 | } | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | impl ConfigurableClock for MainClkConfig { | ||
| 1110 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 1111 | MainClkConfig::init_main_clk(); | ||
| 1112 | Ok(()) | ||
| 1113 | } | ||
| 1114 | fn disable(&self) -> Result<(), ClockError> { | ||
| 1115 | error!("Attempting to reset the main clock, should NOT happen during runtime"); | ||
| 1116 | Err(ClockError::ClockNotSupported) | ||
| 1117 | } | ||
| 1118 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 1119 | let (_c, rate) = MainClkConfig::get_clock_source_and_rate(self, &Clocks::MainClk)?; | ||
| 1120 | Ok(rate) | ||
| 1121 | } | ||
| 1122 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { | ||
| 1123 | error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate"); | ||
| 1124 | Err(ClockError::ClockNotSupported) | ||
| 1125 | } | ||
| 1126 | fn is_enabled(&self) -> bool { | ||
| 1127 | self.state == State::Enabled | ||
| 1128 | } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | impl ConfigurableClock for ClkInConfig { | ||
| 1132 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 1133 | // External Input, no hw writes needed | ||
| 1134 | Ok(()) | ||
| 1135 | } | ||
| 1136 | fn disable(&self) -> Result<(), ClockError> { | ||
| 1137 | error!("Attempting to reset a clock input"); | ||
| 1138 | Err(ClockError::ClockNotSupported) | ||
| 1139 | } | ||
| 1140 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 1141 | if self.freq.is_some() { | ||
| 1142 | Ok(self.freq.as_ref().unwrap().load(Ordering::Relaxed)) | ||
| 1143 | } else { | ||
| 1144 | Err(ClockError::ClockNotEnabled) | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 1148 | trace!("Setting value of clk in config, this won't change the clock itself"); | ||
| 1149 | self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed); | ||
| 1150 | Ok(()) | ||
| 1151 | } | ||
| 1152 | fn is_enabled(&self) -> bool { | ||
| 1153 | self.state == State::Enabled | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | impl RtcClkConfig { | ||
| 1158 | /// Register writes to initialize the RTC Clock | ||
| 1159 | fn init_rtc_clk() { | ||
| 1160 | // SAFETY: unsafe needed to take pointer to Clkctl0, Clkctl1, and RTC | ||
| 1161 | // needed to enable the RTC HW | ||
| 1162 | let cc0 = unsafe { pac::Clkctl0::steal() }; | ||
| 1163 | let cc1 = unsafe { pac::Clkctl1::steal() }; | ||
| 1164 | let r = unsafe { pac::Rtc::steal() }; | ||
| 1165 | // Enable the RTC peripheral clock | ||
| 1166 | cc1.pscctl2_set().write(|w| w.rtc_lite_clk_set().set_clock()); | ||
| 1167 | // Make sure the reset bit is cleared amd RTC OSC is powered up | ||
| 1168 | r.ctrl().modify(|_, w| w.swreset().not_in_reset().rtc_osc_pd().enable()); | ||
| 1169 | |||
| 1170 | // set initial match value, note that with a 15 bit count-down timer this would | ||
| 1171 | // typically be 0x8000, but we are "doing some clever things" in time-driver.rs, | ||
| 1172 | // read more about it in the comments there | ||
| 1173 | // SAFETY: unsafe needed to write the bits | ||
| 1174 | r.wake().write(|w| unsafe { w.bits(0xA) }); | ||
| 1175 | |||
| 1176 | // Enable 32K OSC | ||
| 1177 | cc0.osc32khzctl0().write(|w| w.ena32khz().enabled()); | ||
| 1178 | |||
| 1179 | // enable rtc clk | ||
| 1180 | r.ctrl().modify(|_, w| w.rtc_en().enable()); | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | impl ConfigurableClock for RtcClkConfig { | ||
| 1185 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 1186 | // should only be called once if previously disabled | ||
| 1187 | RtcClkConfig::init_rtc_clk(); | ||
| 1188 | Ok(()) | ||
| 1189 | } | ||
| 1190 | fn disable(&self) -> Result<(), ClockError> { | ||
| 1191 | error!("Resetting the RTC clock, this should NOT happen during runtime"); | ||
| 1192 | Err(ClockError::ClockNotSupported) | ||
| 1193 | } | ||
| 1194 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { | ||
| 1195 | if let Ok(r) = <u32 as TryInto<RtcFreq>>::try_into(freq) { | ||
| 1196 | // SAFETY: unsafe needed to take pointer to RTC | ||
| 1197 | // needed to enable the HW for the different RTC frequencies, powered down by default | ||
| 1198 | let rtc = unsafe { crate::pac::Rtc::steal() }; | ||
| 1199 | match r { | ||
| 1200 | RtcFreq::Default1Hz => { | ||
| 1201 | if rtc.ctrl().read().rtc_en().is_enable() { | ||
| 1202 | trace!("Attempting to enable an already enabled clock, RTC 1Hz"); | ||
| 1203 | } else { | ||
| 1204 | rtc.ctrl().modify(|_r, w| w.rtc_en().enable()); | ||
| 1205 | } | ||
| 1206 | Ok(()) | ||
| 1207 | } | ||
| 1208 | RtcFreq::HighResolution1khz => { | ||
| 1209 | if rtc.ctrl().read().rtc1khz_en().is_enable() { | ||
| 1210 | trace!("Attempting to enable an already enabled clock, RTC 1Hz"); | ||
| 1211 | } else { | ||
| 1212 | rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable()); | ||
| 1213 | } | ||
| 1214 | Ok(()) | ||
| 1215 | } | ||
| 1216 | RtcFreq::SubSecond32kHz => { | ||
| 1217 | if rtc.ctrl().read().rtc_subsec_ena().is_enable() { | ||
| 1218 | trace!("Attempting to enable an already enabled clock, RTC 1Hz"); | ||
| 1219 | } else { | ||
| 1220 | rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable()); | ||
| 1221 | } | ||
| 1222 | Ok(()) | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | } else { | ||
| 1226 | Err(ClockError::InvalidFrequency) | ||
| 1227 | } | ||
| 1228 | } | ||
| 1229 | // unlike the others, since this provides multiple clocks, return the fastest one | ||
| 1230 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 1231 | if self.sub_second_state == State::Enabled { | ||
| 1232 | Ok(RtcFreq::SubSecond32kHz as u32) | ||
| 1233 | } else if self.wake_alarm_state == State::Enabled { | ||
| 1234 | Ok(RtcFreq::HighResolution1khz as u32) | ||
| 1235 | } else if self.state == State::Enabled { | ||
| 1236 | Ok(RtcFreq::Default1Hz as u32) | ||
| 1237 | } else { | ||
| 1238 | Err(ClockError::ClockNotEnabled) | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | fn is_enabled(&self) -> bool { | ||
| 1242 | self.state == State::Enabled | ||
| 1243 | } | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | impl SysClkConfig { | ||
| 1247 | /// Updates the system core clock frequency, SW concept used for systick | ||
| 1248 | fn update_sys_core_clock(&self) { | ||
| 1249 | trace!( | ||
| 1250 | "System core clock has been updated to {:?}, this involves no HW reg writes", | ||
| 1251 | self.sysclkfreq.load(Ordering::Relaxed) | ||
| 1252 | ); | ||
| 1253 | } | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | impl ConfigurableClock for SysOscConfig { | ||
| 1257 | fn enable_and_reset(&self) -> Result<(), ClockError> { | ||
| 1258 | if self.state == State::Enabled { | ||
| 1259 | trace!("SysOsc was already enabled"); | ||
| 1260 | return Ok(()); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW | ||
| 1264 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 1265 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 1266 | |||
| 1267 | // Let CPU run on ffro for safe switching | ||
| 1268 | clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); | ||
| 1269 | clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); | ||
| 1270 | |||
| 1271 | // Power on SYSXTAL | ||
| 1272 | sysctl0.pdruncfg0_clr().write(|w| w.sysxtal_pd().clr_pdruncfg0()); | ||
| 1273 | |||
| 1274 | // Enable system OSC | ||
| 1275 | clkctl0 | ||
| 1276 | .sysoscctl0() | ||
| 1277 | .write(|w| w.lp_enable().lp().bypass_enable().normal_mode()); | ||
| 1278 | |||
| 1279 | delay_loop_clocks(260, SYS_OSC_DEFAULT_FREQ.into()); | ||
| 1280 | Ok(()) | ||
| 1281 | } | ||
| 1282 | fn disable(&self) -> Result<(), ClockError> { | ||
| 1283 | // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW | ||
| 1284 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 1285 | let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; | ||
| 1286 | |||
| 1287 | // Let CPU run on ffro for safe switching | ||
| 1288 | clkctl0.mainclksela().write(|w| w.sel().ffro_clk()); | ||
| 1289 | clkctl0.mainclksela().write(|w| w.sel().ffro_div_4()); | ||
| 1290 | |||
| 1291 | // Power on SYSXTAL | ||
| 1292 | sysctl0.pdruncfg0_set().write(|w| w.sysxtal_pd().set_pdruncfg0()); | ||
| 1293 | Ok(()) | ||
| 1294 | } | ||
| 1295 | fn get_clock_rate(&self) -> Result<u32, ClockError> { | ||
| 1296 | if self.state == State::Enabled { | ||
| 1297 | Ok(SYS_OSC_DEFAULT_FREQ) | ||
| 1298 | } else { | ||
| 1299 | Err(ClockError::ClockNotEnabled) | ||
| 1300 | } | ||
| 1301 | } | ||
| 1302 | fn is_enabled(&self) -> bool { | ||
| 1303 | self.state == State::Enabled | ||
| 1304 | } | ||
| 1305 | fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { | ||
| 1306 | Err(ClockError::ClockNotSupported) | ||
| 1307 | } | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /// Method to delay for a certain number of microseconds given a clock rate | ||
| 1311 | pub fn delay_loop_clocks(usec: u64, freq_mhz: u64) { | ||
| 1312 | let mut ticks = usec * freq_mhz / 1_000_000 / 4; | ||
| 1313 | if ticks > u64::from(u32::MAX) { | ||
| 1314 | ticks = u64::from(u32::MAX); | ||
| 1315 | } | ||
| 1316 | // won't panic since we check value above | ||
| 1317 | cortex_m::asm::delay(ticks as u32); | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | /// Configure the pad voltage pmc registers for all 3 vddio ranges | ||
| 1321 | fn set_pad_voltage_range() { | ||
| 1322 | // SAFETY: unsafe needed to take pointer to PNC as well as to write specific bits | ||
| 1323 | unsafe { | ||
| 1324 | let pmc = crate::pac::Pmc::steal(); | ||
| 1325 | // Set up IO voltages | ||
| 1326 | // all 3 ranges need to be 1.71-1.98V which is 01 | ||
| 1327 | pmc.padvrange().write(|w| { | ||
| 1328 | w.vddio_0range() | ||
| 1329 | .bits(0b01) | ||
| 1330 | .vddio_1range() | ||
| 1331 | .bits(0b01) | ||
| 1332 | .vddio_2range() | ||
| 1333 | .bits(0b01) | ||
| 1334 | }); | ||
| 1335 | } | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | /// Initialize AHB clock | ||
| 1339 | fn init_syscpuahb_clk() { | ||
| 1340 | // SAFETY: unsafe needed to take pointer to Clkctl0 | ||
| 1341 | let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; | ||
| 1342 | // SAFETY: unsafe needed to write the bits | ||
| 1343 | // Set syscpuahbclkdiv to value 2, Subtract 1 since 0-> 1, 1-> 2, etc... | ||
| 1344 | clkctl0.syscpuahbclkdiv().write(|w| unsafe { w.div().bits(2 - 1) }); | ||
| 1345 | |||
| 1346 | while clkctl0.syscpuahbclkdiv().read().reqflag().bit_is_set() {} | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | /// `ClockOut` config | ||
| 1350 | pub struct ClockOutConfig { | ||
| 1351 | src: ClkOutSrc, | ||
| 1352 | div: u8, | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /// `ClockOut` sources | ||
| 1356 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 1357 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 1358 | /// `ClockOut` sources | ||
| 1359 | pub enum ClkOutSrc { | ||
| 1360 | /// No Source, reduce power consumption | ||
| 1361 | None, | ||
| 1362 | /// SFRO clock | ||
| 1363 | Sfro, | ||
| 1364 | /// External input clock | ||
| 1365 | ClkIn, | ||
| 1366 | /// Low-power oscillator | ||
| 1367 | Lposc, | ||
| 1368 | /// FFRO clock | ||
| 1369 | Ffro, | ||
| 1370 | /// Main clock | ||
| 1371 | MainClk, | ||
| 1372 | /// Main DSP clock | ||
| 1373 | DspMainClk, | ||
| 1374 | /// Main Pll clock | ||
| 1375 | MainPllClk, | ||
| 1376 | /// `SysPll` Aux0 clock | ||
| 1377 | Aux0PllClk, | ||
| 1378 | /// `SysPll` DSP clock | ||
| 1379 | DspPllClk, | ||
| 1380 | /// `SysPll` Aux1 clock | ||
| 1381 | Aux1PllClk, | ||
| 1382 | /// Audio Pll clock | ||
| 1383 | AudioPllClk, | ||
| 1384 | /// 32 `KHz` RTC | ||
| 1385 | RTC32k, | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | /// Initialize the `ClkOutConfig` | ||
| 1389 | impl ClockOutConfig { | ||
| 1390 | /// Default configuration for Clock out | ||
| 1391 | #[must_use] | ||
| 1392 | pub fn default_config() -> Self { | ||
| 1393 | Self { | ||
| 1394 | src: ClkOutSrc::None, | ||
| 1395 | div: 0, | ||
| 1396 | } | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | /// Enable the Clock Out output | ||
| 1400 | pub fn enable_and_reset(&mut self) -> Result<(), ClockError> { | ||
| 1401 | self.set_clkout_source_and_div(self.src, self.div)?; | ||
| 1402 | Ok(()) | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | /// Disable Clock Out output and select None as the source to conserve power | ||
| 1406 | pub fn disable(&mut self) -> Result<(), ClockError> { | ||
| 1407 | self.set_clkout_source_and_div(ClkOutSrc::None, 0)?; | ||
| 1408 | Ok(()) | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | /// Set the source of the Clock Out pin | ||
| 1412 | fn set_clkout_source(&mut self, src: ClkOutSrc) -> Result<(), ClockError> { | ||
| 1413 | // SAFETY: unsafe needed to take pointers to Clkctl1, needed to set source in HW | ||
| 1414 | let cc1 = unsafe { pac::Clkctl1::steal() }; | ||
| 1415 | match src { | ||
| 1416 | ClkOutSrc::None => { | ||
| 1417 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1418 | cc1.clkoutsel1().write(|w| w.sel().none()); | ||
| 1419 | } | ||
| 1420 | ClkOutSrc::Sfro => { | ||
| 1421 | cc1.clkoutsel0().write(|w| w.sel().sfro_clk()); | ||
| 1422 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1423 | } | ||
| 1424 | ClkOutSrc::ClkIn => { | ||
| 1425 | cc1.clkoutsel0().write(|w| w.sel().xtalin_clk()); | ||
| 1426 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1427 | } | ||
| 1428 | ClkOutSrc::Lposc => { | ||
| 1429 | cc1.clkoutsel0().write(|w| w.sel().lposc()); | ||
| 1430 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1431 | } | ||
| 1432 | ClkOutSrc::Ffro => { | ||
| 1433 | cc1.clkoutsel0().write(|w| w.sel().ffro_clk()); | ||
| 1434 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1435 | } | ||
| 1436 | ClkOutSrc::MainClk => { | ||
| 1437 | cc1.clkoutsel0().write(|w| w.sel().main_clk()); | ||
| 1438 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1439 | } | ||
| 1440 | ClkOutSrc::DspMainClk => { | ||
| 1441 | cc1.clkoutsel0().write(|w| w.sel().dsp_main_clk()); | ||
| 1442 | cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output()); | ||
| 1443 | } | ||
| 1444 | ClkOutSrc::MainPllClk => { | ||
| 1445 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1446 | cc1.clkoutsel1().write(|w| w.sel().main_pll_clk()); | ||
| 1447 | } | ||
| 1448 | ClkOutSrc::Aux0PllClk => { | ||
| 1449 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1450 | cc1.clkoutsel1().write(|w| w.sel().syspll0_aux0_pll_clk()); | ||
| 1451 | } | ||
| 1452 | ClkOutSrc::DspPllClk => { | ||
| 1453 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1454 | cc1.clkoutsel1().write(|w| w.sel().dsp_pll_clk()); | ||
| 1455 | } | ||
| 1456 | ClkOutSrc::AudioPllClk => { | ||
| 1457 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1458 | cc1.clkoutsel1().write(|w| w.sel().audio_pll_clk()); | ||
| 1459 | } | ||
| 1460 | ClkOutSrc::Aux1PllClk => { | ||
| 1461 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1462 | cc1.clkoutsel1().write(|w| w.sel().syspll0_aux1_pll_clk()); | ||
| 1463 | } | ||
| 1464 | ClkOutSrc::RTC32k => { | ||
| 1465 | cc1.clkoutsel0().write(|w| w.sel().none()); | ||
| 1466 | cc1.clkoutsel1().write(|w| w.sel().rtc_clk_32khz()); | ||
| 1467 | } | ||
| 1468 | } | ||
| 1469 | self.src = src; | ||
| 1470 | Ok(()) | ||
| 1471 | } | ||
| 1472 | /// set the clock out divider | ||
| 1473 | /// note that 1 will be added to div when mapping to the divider | ||
| 1474 | /// so bits(0) -> divide by 1 | ||
| 1475 | /// ... | ||
| 1476 | /// bits(255)-> divide by 256 | ||
| 1477 | pub fn set_clkout_divider(&self, div: u8) -> Result<(), ClockError> { | ||
| 1478 | // don't wait for clock to be ready if there's no source | ||
| 1479 | if self.src != ClkOutSrc::None { | ||
| 1480 | let cc1 = unsafe { pac::Clkctl1::steal() }; | ||
| 1481 | |||
| 1482 | cc1.clkoutdiv() | ||
| 1483 | .modify(|_, w| unsafe { w.div().bits(div).halt().clear_bit() }); | ||
| 1484 | while cc1.clkoutdiv().read().reqflag().bit_is_set() {} | ||
| 1485 | } | ||
| 1486 | Ok(()) | ||
| 1487 | } | ||
| 1488 | /// set the source and divider for the clockout pin | ||
| 1489 | pub fn set_clkout_source_and_div(&mut self, src: ClkOutSrc, div: u8) -> Result<(), ClockError> { | ||
| 1490 | self.set_clkout_source(src)?; | ||
| 1491 | |||
| 1492 | self.set_clkout_divider(div)?; | ||
| 1493 | |||
| 1494 | Ok(()) | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | /// Using the config, enables all desired clocks to desired clock rates | ||
| 1499 | fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { | ||
| 1500 | if let Err(e) = config.rtc.enable_and_reset() { | ||
| 1501 | error!("couldn't Power on OSC for RTC, result: {:?}", e); | ||
| 1502 | return Err(e); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | if let Err(e) = config.lposc.enable_and_reset() { | ||
| 1506 | error!("couldn't Power on LPOSC, result: {:?}", e); | ||
| 1507 | return Err(e); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | if let Err(e) = config.ffro.enable_and_reset() { | ||
| 1511 | error!("couldn't Power on FFRO, result: {:?}", e); | ||
| 1512 | return Err(e); | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | if let Err(e) = config.sfro.enable_and_reset() { | ||
| 1516 | error!("couldn't Power on SFRO, result: {:?}", e); | ||
| 1517 | return Err(e); | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | if let Err(e) = config.sys_osc.enable_and_reset() { | ||
| 1521 | error!("Couldn't enable sys oscillator {:?}", e); | ||
| 1522 | return Err(e); | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | if let Err(e) = config.main_pll_clk.enable_and_reset() { | ||
| 1526 | error!("Couldn't enable main pll clock {:?}", e); | ||
| 1527 | return Err(e); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | // Move FLEXSPI clock source from main clock to FFRO to avoid instruction/data fetch issue in XIP when | ||
| 1531 | // updating PLL and main clock. | ||
| 1532 | // SAFETY: unsafe needed to take pointers to Clkctl0 | ||
| 1533 | let cc0 = unsafe { pac::Clkctl0::steal() }; | ||
| 1534 | cc0.flexspifclksel().write(|w| w.sel().ffro_clk()); | ||
| 1535 | |||
| 1536 | // Move ESPI clock source to FFRO | ||
| 1537 | #[cfg(feature = "_espi")] | ||
| 1538 | { | ||
| 1539 | cc0.espiclksel().write(|w| w.sel().use_48_60m()); | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | init_syscpuahb_clk(); | ||
| 1543 | |||
| 1544 | if let Err(e) = config.main_clk.enable_and_reset() { | ||
| 1545 | error!("Couldn't enable main clock {:?}", e); | ||
| 1546 | return Err(e); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | config.sys_clk.update_sys_core_clock(); | ||
| 1550 | Ok(()) | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | /// SAFETY: must be called exactly once at bootup | ||
| 1554 | pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> { | ||
| 1555 | init_clock_hw(config)?; | ||
| 1556 | |||
| 1557 | // set VDDIO ranges 0-2 | ||
| 1558 | set_pad_voltage_range(); | ||
| 1559 | Ok(()) | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | ///Trait to expose perph clocks | ||
| 1563 | trait SealedSysconPeripheral { | ||
| 1564 | fn enable_and_reset_perph_clock(); | ||
| 1565 | fn disable_perph_clock(); | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | /// Clock and Reset control for peripherals | ||
| 1569 | #[allow(private_bounds)] | ||
| 1570 | pub trait SysconPeripheral: SealedSysconPeripheral + 'static {} | ||
| 1571 | /// Enables and resets peripheral `T`. | ||
| 1572 | /// | ||
| 1573 | /// # Safety | ||
| 1574 | /// | ||
| 1575 | /// Peripheral must not be in use. | ||
| 1576 | pub fn enable_and_reset<T: SysconPeripheral>() { | ||
| 1577 | T::enable_and_reset_perph_clock(); | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | /// Disables peripheral `T`. | ||
| 1581 | /// | ||
| 1582 | /// # Safety | ||
| 1583 | /// | ||
| 1584 | /// Peripheral must not be in use. | ||
| 1585 | pub fn disable<T: SysconPeripheral>() { | ||
| 1586 | T::disable_perph_clock(); | ||
| 1587 | } | ||
| 1588 | macro_rules! impl_perph_clk { | ||
| 1589 | ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => { | ||
| 1590 | impl SealedSysconPeripheral for crate::peripherals::$peripheral { | ||
| 1591 | fn enable_and_reset_perph_clock() { | ||
| 1592 | // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 | ||
| 1593 | let cc1 = unsafe { pac::$clkctl::steal() }; | ||
| 1594 | let rc1 = unsafe { pac::$rstctl::steal() }; | ||
| 1595 | |||
| 1596 | paste! { | ||
| 1597 | // SAFETY: unsafe due to the use of bits() | ||
| 1598 | cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); | ||
| 1599 | |||
| 1600 | // SAFETY: unsafe due to the use of bits() | ||
| 1601 | rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); | ||
| 1602 | } | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | fn disable_perph_clock() { | ||
| 1606 | // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 | ||
| 1607 | let cc1 = unsafe { pac::$clkctl::steal() }; | ||
| 1608 | let rc1 = unsafe { pac::$rstctl::steal() }; | ||
| 1609 | |||
| 1610 | paste! { | ||
| 1611 | // SAFETY: unsafe due to the use of bits() | ||
| 1612 | rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); | ||
| 1613 | |||
| 1614 | // SAFETY: unsafe due to the use of bits() | ||
| 1615 | cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); | ||
| 1616 | } | ||
| 1617 | } | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | impl SysconPeripheral for crate::peripherals::$peripheral {} | ||
| 1621 | }; | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | // These should enabled once the relevant peripherals are implemented. | ||
| 1625 | // impl_perph_clk!(GPIOINTCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 30); | ||
| 1626 | // impl_perph_clk!(OTP, Clkctl0, pscctl0, Rstctl0, prstctl0, 17); | ||
| 1627 | |||
| 1628 | // impl_perph_clk!(ROM_CTL_128KB, Clkctl0, pscctl0, Rstctl0, prstctl0, 2); | ||
| 1629 | // impl_perph_clk!(USBHS_SRAM, Clkctl0, pscctl0, Rstctl0, prstctl0, 23); | ||
| 1630 | |||
| 1631 | impl_perph_clk!(PIMCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 31); | ||
| 1632 | impl_perph_clk!(ACMP, Clkctl0, pscctl1, Rstctl0, prstctl1, 15); | ||
| 1633 | impl_perph_clk!(ADC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 16); | ||
| 1634 | impl_perph_clk!(CASPER, Clkctl0, pscctl0, Rstctl0, prstctl0, 9); | ||
| 1635 | impl_perph_clk!(CRC, Clkctl1, pscctl1, Rstctl1, prstctl1, 16); | ||
| 1636 | impl_perph_clk!(CTIMER0_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 0); | ||
| 1637 | impl_perph_clk!(CTIMER1_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 1); | ||
| 1638 | impl_perph_clk!(CTIMER2_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 2); | ||
| 1639 | impl_perph_clk!(CTIMER3_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 3); | ||
| 1640 | impl_perph_clk!(CTIMER4_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 4); | ||
| 1641 | impl_perph_clk!(DMA0, Clkctl1, pscctl1, Rstctl1, prstctl1, 23); | ||
| 1642 | impl_perph_clk!(DMA1, Clkctl1, pscctl1, Rstctl1, prstctl1, 24); | ||
| 1643 | impl_perph_clk!(DMIC0, Clkctl1, pscctl0, Rstctl1, prstctl0, 24); | ||
| 1644 | |||
| 1645 | #[cfg(feature = "_espi")] | ||
| 1646 | impl_perph_clk!(ESPI, Clkctl0, pscctl1, Rstctl0, prstctl1, 7); | ||
| 1647 | |||
| 1648 | impl_perph_clk!(FLEXCOMM0, Clkctl1, pscctl0, Rstctl1, prstctl0, 8); | ||
| 1649 | impl_perph_clk!(FLEXCOMM1, Clkctl1, pscctl0, Rstctl1, prstctl0, 9); | ||
| 1650 | impl_perph_clk!(FLEXCOMM14, Clkctl1, pscctl0, Rstctl1, prstctl0, 22); | ||
| 1651 | impl_perph_clk!(FLEXCOMM15, Clkctl1, pscctl0, Rstctl1, prstctl0, 23); | ||
| 1652 | impl_perph_clk!(FLEXCOMM2, Clkctl1, pscctl0, Rstctl1, prstctl0, 10); | ||
| 1653 | impl_perph_clk!(FLEXCOMM3, Clkctl1, pscctl0, Rstctl1, prstctl0, 11); | ||
| 1654 | impl_perph_clk!(FLEXCOMM4, Clkctl1, pscctl0, Rstctl1, prstctl0, 12); | ||
| 1655 | impl_perph_clk!(FLEXCOMM5, Clkctl1, pscctl0, Rstctl1, prstctl0, 13); | ||
| 1656 | impl_perph_clk!(FLEXCOMM6, Clkctl1, pscctl0, Rstctl1, prstctl0, 14); | ||
| 1657 | impl_perph_clk!(FLEXCOMM7, Clkctl1, pscctl0, Rstctl1, prstctl0, 15); | ||
| 1658 | impl_perph_clk!(FLEXSPI, Clkctl0, pscctl0, Rstctl0, prstctl0, 16); | ||
| 1659 | impl_perph_clk!(FREQME, Clkctl1, pscctl1, Rstctl1, prstctl1, 31); | ||
| 1660 | impl_perph_clk!(HASHCRYPT, Clkctl0, pscctl0, Rstctl0, prstctl0, 10); | ||
| 1661 | impl_perph_clk!(HSGPIO0, Clkctl1, pscctl1, Rstctl1, prstctl1, 0); | ||
| 1662 | impl_perph_clk!(HSGPIO1, Clkctl1, pscctl1, Rstctl1, prstctl1, 1); | ||
| 1663 | impl_perph_clk!(HSGPIO2, Clkctl1, pscctl1, Rstctl1, prstctl1, 2); | ||
| 1664 | impl_perph_clk!(HSGPIO3, Clkctl1, pscctl1, Rstctl1, prstctl1, 3); | ||
| 1665 | impl_perph_clk!(HSGPIO4, Clkctl1, pscctl1, Rstctl1, prstctl1, 4); | ||
| 1666 | impl_perph_clk!(HSGPIO5, Clkctl1, pscctl1, Rstctl1, prstctl1, 5); | ||
| 1667 | impl_perph_clk!(HSGPIO6, Clkctl1, pscctl1, Rstctl1, prstctl1, 6); | ||
| 1668 | impl_perph_clk!(HSGPIO7, Clkctl1, pscctl1, Rstctl1, prstctl1, 7); | ||
| 1669 | impl_perph_clk!(I3C0, Clkctl1, pscctl2, Rstctl1, prstctl2, 16); | ||
| 1670 | impl_perph_clk!(MRT0, Clkctl1, pscctl2, Rstctl1, prstctl2, 8); | ||
| 1671 | impl_perph_clk!(MU_A, Clkctl1, pscctl1, Rstctl1, prstctl1, 28); | ||
| 1672 | impl_perph_clk!(OS_EVENT, Clkctl1, pscctl0, Rstctl1, prstctl0, 27); | ||
| 1673 | impl_perph_clk!(POWERQUAD, Clkctl0, pscctl0, Rstctl0, prstctl0, 8); | ||
| 1674 | impl_perph_clk!(PUF, Clkctl0, pscctl0, Rstctl0, prstctl0, 11); | ||
| 1675 | impl_perph_clk!(RNG, Clkctl0, pscctl0, Rstctl0, prstctl0, 12); | ||
| 1676 | impl_perph_clk!(RTC, Clkctl1, pscctl2, Rstctl1, prstctl2, 7); | ||
| 1677 | impl_perph_clk!(SCT0, Clkctl0, pscctl0, Rstctl0, prstctl0, 24); | ||
| 1678 | impl_perph_clk!(SECGPIO, Clkctl0, pscctl1, Rstctl0, prstctl1, 24); | ||
| 1679 | impl_perph_clk!(SEMA42, Clkctl1, pscctl1, Rstctl1, prstctl1, 29); | ||
| 1680 | impl_perph_clk!(USBHSD, Clkctl0, pscctl0, Rstctl0, prstctl0, 21); | ||
| 1681 | impl_perph_clk!(USBHSH, Clkctl0, pscctl0, Rstctl0, prstctl0, 22); | ||
| 1682 | impl_perph_clk!(USBPHY, Clkctl0, pscctl0, Rstctl0, prstctl0, 20); | ||
| 1683 | impl_perph_clk!(USDHC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 2); | ||
| 1684 | impl_perph_clk!(USDHC1, Clkctl0, pscctl1, Rstctl0, prstctl1, 3); | ||
| 1685 | impl_perph_clk!(UTICK0, Clkctl0, pscctl2, Rstctl0, prstctl2, 0); | ||
| 1686 | impl_perph_clk!(WDT0, Clkctl0, pscctl2, Rstctl0, prstctl2, 1); | ||
| 1687 | impl_perph_clk!(WDT1, Clkctl1, pscctl2, Rstctl1, prstctl2, 10); | ||
