diff options
Diffstat (limited to 'embassy-mcxa/src/clocks/mod.rs')
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 943 |
1 files changed, 943 insertions, 0 deletions
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs new file mode 100644 index 000000000..9c9e6ef3d --- /dev/null +++ b/embassy-mcxa/src/clocks/mod.rs | |||
| @@ -0,0 +1,943 @@ | |||
| 1 | //! # Clock Module | ||
| 2 | //! | ||
| 3 | //! For the MCX-A, we separate clock and peripheral control into two main stages: | ||
| 4 | //! | ||
| 5 | //! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the | ||
| 6 | //! core system clocks, including external and internal oscillators. This | ||
| 7 | //! configuration is then largely static for the duration of the program. | ||
| 8 | //! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver | ||
| 9 | //! is responsible for two main things: | ||
| 10 | //! * Ensuring that any required "upstream" core system clocks necessary for | ||
| 11 | //! clocking the peripheral is active and configured to a reasonable value | ||
| 12 | //! * Enabling the clock gates for that peripheral, and resetting the peripheral | ||
| 13 | //! | ||
| 14 | //! From a user perspective, only step 1 is visible. Step 2 is automatically handled | ||
| 15 | //! by HAL drivers, using interfaces defined in this module. | ||
| 16 | //! | ||
| 17 | //! It is also possible to *view* the state of the clock configuration after [`init()`] | ||
| 18 | //! has been called, using the [`with_clocks()`] function, which provides a view of the | ||
| 19 | //! [`Clocks`] structure. | ||
| 20 | //! | ||
| 21 | //! ## For HAL driver implementors | ||
| 22 | //! | ||
| 23 | //! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or | ||
| 24 | //! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This | ||
| 25 | //! selection, as well as often any pre-scaler division from that source clock, is made | ||
| 26 | //! through MRCC registers. | ||
| 27 | //! | ||
| 28 | //! Any peripheral that is controlled through the MRCC register can automatically implement | ||
| 29 | //! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need | ||
| 30 | //! to define the configuration surface and steps necessary to fully configure that peripheral | ||
| 31 | //! from a clocks perspective by: | ||
| 32 | //! | ||
| 33 | //! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects | ||
| 34 | //! or divisions available to the HAL driver | ||
| 35 | //! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the | ||
| 36 | //! necessary input clocks are reasonable | ||
| 37 | |||
| 38 | use core::cell::RefCell; | ||
| 39 | |||
| 40 | use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig}; | ||
| 41 | use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}; | ||
| 42 | use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten}; | ||
| 43 | use periph_helpers::SPConfHelper; | ||
| 44 | |||
| 45 | use crate::pac; | ||
| 46 | pub mod config; | ||
| 47 | pub mod periph_helpers; | ||
| 48 | |||
| 49 | // | ||
| 50 | // Statics/Consts | ||
| 51 | // | ||
| 52 | |||
| 53 | /// The state of system core clocks. | ||
| 54 | /// | ||
| 55 | /// Initialized by [`init()`], and then unchanged for the remainder of the program. | ||
| 56 | static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None)); | ||
| 57 | |||
| 58 | // | ||
| 59 | // Free functions | ||
| 60 | // | ||
| 61 | |||
| 62 | /// Initialize the core system clocks with the given [`ClocksConfig`]. | ||
| 63 | /// | ||
| 64 | /// This function should be called EXACTLY once at start-up, usually via a | ||
| 65 | /// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will | ||
| 66 | /// return an error. | ||
| 67 | pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { | ||
| 68 | critical_section::with(|cs| { | ||
| 69 | if CLOCKS.borrow_ref(cs).is_some() { | ||
| 70 | Err(ClockError::AlreadyInitialized) | ||
| 71 | } else { | ||
| 72 | Ok(()) | ||
| 73 | } | ||
| 74 | })?; | ||
| 75 | |||
| 76 | let mut clocks = Clocks::default(); | ||
| 77 | let mut operator = ClockOperator { | ||
| 78 | clocks: &mut clocks, | ||
| 79 | config: &settings, | ||
| 80 | |||
| 81 | _mrcc0: unsafe { pac::Mrcc0::steal() }, | ||
| 82 | scg0: unsafe { pac::Scg0::steal() }, | ||
| 83 | syscon: unsafe { pac::Syscon::steal() }, | ||
| 84 | vbat0: unsafe { pac::Vbat0::steal() }, | ||
| 85 | }; | ||
| 86 | |||
| 87 | operator.configure_firc_clocks()?; | ||
| 88 | operator.configure_sirc_clocks()?; | ||
| 89 | operator.configure_fro16k_clocks()?; | ||
| 90 | |||
| 91 | // For now, just use FIRC as the main/cpu clock, which should already be | ||
| 92 | // the case on reset | ||
| 93 | assert!(operator.scg0.rccr().read().scs().is_firc()); | ||
| 94 | let input = operator.clocks.fro_hf_root.clone().unwrap(); | ||
| 95 | operator.clocks.main_clk = Some(input.clone()); | ||
| 96 | // We can also assume cpu/system clk == fro_hf because div is /1. | ||
| 97 | assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0); | ||
| 98 | operator.clocks.cpu_system_clk = Some(input); | ||
| 99 | |||
| 100 | critical_section::with(|cs| { | ||
| 101 | let mut clks = CLOCKS.borrow_ref_mut(cs); | ||
| 102 | assert!(clks.is_none(), "Clock setup race!"); | ||
| 103 | *clks = Some(clocks); | ||
| 104 | }); | ||
| 105 | |||
| 106 | Ok(()) | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Obtain the full clocks structure, calling the given closure in a critical section. | ||
| 110 | /// | ||
| 111 | /// The given closure will be called with read-only access to the state of the system | ||
| 112 | /// clocks. This can be used to query and return the state of a given clock. | ||
| 113 | /// | ||
| 114 | /// As the caller's closure will be called in a critical section, care must be taken | ||
| 115 | /// not to block or cause any other undue delays while accessing. | ||
| 116 | /// | ||
| 117 | /// Calls to this function will not succeed until after a successful call to `init()`, | ||
| 118 | /// and will always return None. | ||
| 119 | pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> { | ||
| 120 | critical_section::with(|cs| { | ||
| 121 | let c = CLOCKS.borrow_ref(cs); | ||
| 122 | let c = c.as_ref()?; | ||
| 123 | Some(f(c)) | ||
| 124 | }) | ||
| 125 | } | ||
| 126 | |||
| 127 | // | ||
| 128 | // Structs/Enums | ||
| 129 | // | ||
| 130 | |||
| 131 | /// The `Clocks` structure contains the initialized state of the core system clocks | ||
| 132 | /// | ||
| 133 | /// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function | ||
| 134 | /// at boot time. | ||
| 135 | #[derive(Default, Debug, Clone)] | ||
| 136 | #[non_exhaustive] | ||
| 137 | pub struct Clocks { | ||
| 138 | /// The `clk_in` is a clock provided by an external oscillator | ||
| 139 | pub clk_in: Option<Clock>, | ||
| 140 | |||
| 141 | // FRO180M stuff | ||
| 142 | // | ||
| 143 | /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator | ||
| 144 | /// | ||
| 145 | /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`, | ||
| 146 | /// and `fro_hf_div`. | ||
| 147 | pub fro_hf_root: Option<Clock>, | ||
| 148 | |||
| 149 | /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate. | ||
| 150 | pub fro_hf: Option<Clock>, | ||
| 151 | |||
| 152 | /// `clk_45` is a 45MHz clock, sourced from `fro_hf`. | ||
| 153 | pub clk_45m: Option<Clock>, | ||
| 154 | |||
| 155 | /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`. | ||
| 156 | pub fro_hf_div: Option<Clock>, | ||
| 157 | |||
| 158 | // | ||
| 159 | // End FRO180M | ||
| 160 | |||
| 161 | // FRO12M stuff | ||
| 162 | // | ||
| 163 | /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator | ||
| 164 | /// | ||
| 165 | /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`, | ||
| 166 | /// `and `fro_lf_div`. | ||
| 167 | pub fro_12m_root: Option<Clock>, | ||
| 168 | |||
| 169 | /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate. | ||
| 170 | pub fro_12m: Option<Clock>, | ||
| 171 | |||
| 172 | /// `clk_1m` is a 1MHz clock, sourced from `fro_12m` | ||
| 173 | pub clk_1m: Option<Clock>, | ||
| 174 | |||
| 175 | /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m` | ||
| 176 | pub fro_lf_div: Option<Clock>, | ||
| 177 | // | ||
| 178 | // End FRO12M stuff | ||
| 179 | /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 180 | /// | ||
| 181 | /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in | ||
| 182 | /// the system domain, such as the CMP and RTC. | ||
| 183 | pub clk_16k_vsys: Option<Clock>, | ||
| 184 | |||
| 185 | /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 186 | /// | ||
| 187 | /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in | ||
| 188 | /// the VDD Core domain, such as the OSTimer or LPUarts. | ||
| 189 | pub clk_16k_vdd_core: Option<Clock>, | ||
| 190 | |||
| 191 | /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some | ||
| 192 | /// peripherals. | ||
| 193 | pub main_clk: Option<Clock>, | ||
| 194 | |||
| 195 | /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV` | ||
| 196 | pub cpu_system_clk: Option<Clock>, | ||
| 197 | |||
| 198 | /// `pll1_clk` is the output of the main system PLL, `pll1`. | ||
| 199 | pub pll1_clk: Option<Clock>, | ||
| 200 | } | ||
| 201 | |||
| 202 | /// `ClockError` is the main error returned when configuring or checking clock state | ||
| 203 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 204 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 205 | #[non_exhaustive] | ||
| 206 | pub enum ClockError { | ||
| 207 | /// The system clocks were never initialized by calling [`init()`] | ||
| 208 | NeverInitialized, | ||
| 209 | /// The [`init()`] function was called more than once | ||
| 210 | AlreadyInitialized, | ||
| 211 | /// The requested configuration was not possible to fulfill, as the system clocks | ||
| 212 | /// were not configured in a compatible way | ||
| 213 | BadConfig { clock: &'static str, reason: &'static str }, | ||
| 214 | /// The requested configuration was not possible to fulfill, as the required system | ||
| 215 | /// clocks have not yet been implemented. | ||
| 216 | NotImplemented { clock: &'static str }, | ||
| 217 | /// The requested peripheral could not be configured, as the steps necessary to | ||
| 218 | /// enable it have not yet been implemented. | ||
| 219 | UnimplementedConfig, | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Information regarding a system clock | ||
| 223 | #[derive(Debug, Clone)] | ||
| 224 | pub struct Clock { | ||
| 225 | /// The frequency, in Hz, of the given clock | ||
| 226 | pub frequency: u32, | ||
| 227 | /// The power state of the clock, e.g. whether it is active in deep sleep mode | ||
| 228 | /// or not. | ||
| 229 | pub power: PoweredClock, | ||
| 230 | } | ||
| 231 | |||
| 232 | /// The power state of a given clock. | ||
| 233 | /// | ||
| 234 | /// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep | ||
| 235 | /// mode will be stopped. This means that any downstream usage, e.g. by peripherals, | ||
| 236 | /// will also stop. | ||
| 237 | /// | ||
| 238 | /// In the future, we will provide an API for entering Deep Sleep, and if there are | ||
| 239 | /// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into | ||
| 240 | /// Deep Sleep will be prevented, in order to avoid misbehaving peripherals. | ||
| 241 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 242 | pub enum PoweredClock { | ||
| 243 | /// The given clock will NOT continue running in Deep Sleep mode | ||
| 244 | NormalEnabledDeepSleepDisabled, | ||
| 245 | /// The given clock WILL continue running in Deep Sleep mode | ||
| 246 | AlwaysEnabled, | ||
| 247 | } | ||
| 248 | |||
| 249 | /// The ClockOperator is a private helper type that contains the methods used | ||
| 250 | /// during system clock initialization. | ||
| 251 | /// | ||
| 252 | /// # SAFETY | ||
| 253 | /// | ||
| 254 | /// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`, | ||
| 255 | /// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function. | ||
| 256 | struct ClockOperator<'a> { | ||
| 257 | /// A mutable reference to the current state of system clocks | ||
| 258 | clocks: &'a mut Clocks, | ||
| 259 | /// A reference to the requested configuration provided by the caller of [`init()`] | ||
| 260 | config: &'a ClocksConfig, | ||
| 261 | |||
| 262 | // We hold on to stolen peripherals | ||
| 263 | _mrcc0: pac::Mrcc0, | ||
| 264 | scg0: pac::Scg0, | ||
| 265 | syscon: pac::Syscon, | ||
| 266 | vbat0: pac::Vbat0, | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Trait describing an AHB clock gate that can be toggled through MRCC. | ||
| 270 | pub trait Gate { | ||
| 271 | type MrccPeriphConfig: SPConfHelper; | ||
| 272 | |||
| 273 | /// Enable the clock gate. | ||
| 274 | /// | ||
| 275 | /// # SAFETY | ||
| 276 | /// | ||
| 277 | /// The current peripheral must be disabled prior to calling this method | ||
| 278 | unsafe fn enable_clock(); | ||
| 279 | |||
| 280 | /// Disable the clock gate. | ||
| 281 | /// | ||
| 282 | /// # SAFETY | ||
| 283 | /// | ||
| 284 | /// There must be no active user of this peripheral when calling this method | ||
| 285 | unsafe fn disable_clock(); | ||
| 286 | |||
| 287 | /// Drive the peripheral into reset. | ||
| 288 | /// | ||
| 289 | /// # SAFETY | ||
| 290 | /// | ||
| 291 | /// There must be no active user of this peripheral when calling this method | ||
| 292 | unsafe fn assert_reset(); | ||
| 293 | |||
| 294 | /// Drive the peripheral out of reset. | ||
| 295 | /// | ||
| 296 | /// # SAFETY | ||
| 297 | /// | ||
| 298 | /// There must be no active user of this peripheral when calling this method | ||
| 299 | unsafe fn release_reset(); | ||
| 300 | |||
| 301 | /// Return whether the clock gate for this peripheral is currently enabled. | ||
| 302 | fn is_clock_enabled() -> bool; | ||
| 303 | |||
| 304 | /// Return whether the peripheral is currently held in reset. | ||
| 305 | fn is_reset_released() -> bool; | ||
| 306 | } | ||
| 307 | |||
| 308 | /// This is the primary helper method HAL drivers are expected to call when creating | ||
| 309 | /// an instance of the peripheral. | ||
| 310 | /// | ||
| 311 | /// This method: | ||
| 312 | /// | ||
| 313 | /// 1. Enables the MRCC clock gate for this peripheral | ||
| 314 | /// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error | ||
| 315 | /// and re-disabling the peripheral if this fails. | ||
| 316 | /// 3. Pulses the MRCC reset line, to reset the peripheral to the default state | ||
| 317 | /// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account | ||
| 318 | /// the selected upstream clock, as well as any division specified by `cfg`. | ||
| 319 | /// | ||
| 320 | /// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method | ||
| 321 | /// may return `Ok(0)`. In the future, this might be updated to return the correct | ||
| 322 | /// "ambient" clock, e.g. the AHB/APB frequency. | ||
| 323 | /// | ||
| 324 | /// # SAFETY | ||
| 325 | /// | ||
| 326 | /// This peripheral must not yet be in use prior to calling `enable_and_reset`. | ||
| 327 | #[inline] | ||
| 328 | pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 329 | let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?; | ||
| 330 | pulse_reset::<G>(); | ||
| 331 | Ok(freq) | ||
| 332 | } | ||
| 333 | |||
| 334 | /// Enable the clock gate for the given peripheral. | ||
| 335 | /// | ||
| 336 | /// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need | ||
| 337 | /// to control the duration of the pulse more directly. | ||
| 338 | /// | ||
| 339 | /// # SAFETY | ||
| 340 | /// | ||
| 341 | /// This peripheral must not yet be in use prior to calling `enable`. | ||
| 342 | #[inline] | ||
| 343 | pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 344 | G::enable_clock(); | ||
| 345 | while !G::is_clock_enabled() {} | ||
| 346 | core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags)); | ||
| 347 | |||
| 348 | let freq = critical_section::with(|cs| { | ||
| 349 | let clocks = CLOCKS.borrow_ref(cs); | ||
| 350 | let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?; | ||
| 351 | cfg.post_enable_config(clocks) | ||
| 352 | }); | ||
| 353 | |||
| 354 | freq.inspect_err(|_e| { | ||
| 355 | G::disable_clock(); | ||
| 356 | }) | ||
| 357 | } | ||
| 358 | |||
| 359 | /// Disable the clock gate for the given peripheral. | ||
| 360 | /// | ||
| 361 | /// # SAFETY | ||
| 362 | /// | ||
| 363 | /// This peripheral must no longer be in use prior to calling `enable`. | ||
| 364 | #[allow(dead_code)] | ||
| 365 | #[inline] | ||
| 366 | pub unsafe fn disable<G: Gate>() { | ||
| 367 | G::disable_clock(); | ||
| 368 | } | ||
| 369 | |||
| 370 | /// Check whether a gate is currently enabled. | ||
| 371 | #[allow(dead_code)] | ||
| 372 | #[inline] | ||
| 373 | pub fn is_clock_enabled<G: Gate>() -> bool { | ||
| 374 | G::is_clock_enabled() | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Release a reset line for the given peripheral set. | ||
| 378 | /// | ||
| 379 | /// Prefer [`enable_and_reset`]. | ||
| 380 | /// | ||
| 381 | /// # SAFETY | ||
| 382 | /// | ||
| 383 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 384 | #[inline] | ||
| 385 | pub unsafe fn release_reset<G: Gate>() { | ||
| 386 | G::release_reset(); | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Assert a reset line for the given peripheral set. | ||
| 390 | /// | ||
| 391 | /// Prefer [`enable_and_reset`]. | ||
| 392 | /// | ||
| 393 | /// # SAFETY | ||
| 394 | /// | ||
| 395 | /// This peripheral must not yet be in use prior to calling `assert_reset`. | ||
| 396 | #[inline] | ||
| 397 | pub unsafe fn assert_reset<G: Gate>() { | ||
| 398 | G::assert_reset(); | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Check whether the peripheral is held in reset. | ||
| 402 | #[inline] | ||
| 403 | pub unsafe fn is_reset_released<G: Gate>() -> bool { | ||
| 404 | G::is_reset_released() | ||
| 405 | } | ||
| 406 | |||
| 407 | /// Pulse a reset line (assert then release) with a short delay. | ||
| 408 | /// | ||
| 409 | /// Prefer [`enable_and_reset`]. | ||
| 410 | /// | ||
| 411 | /// # SAFETY | ||
| 412 | /// | ||
| 413 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 414 | #[inline] | ||
| 415 | pub unsafe fn pulse_reset<G: Gate>() { | ||
| 416 | G::assert_reset(); | ||
| 417 | cortex_m::asm::nop(); | ||
| 418 | cortex_m::asm::nop(); | ||
| 419 | G::release_reset(); | ||
| 420 | } | ||
| 421 | |||
| 422 | // | ||
| 423 | // `impl`s for structs/enums | ||
| 424 | // | ||
| 425 | |||
| 426 | /// The [`Clocks`] type's methods generally take the form of "ensure X clock is active". | ||
| 427 | /// | ||
| 428 | /// These methods are intended to be used by HAL peripheral implementors to ensure that their | ||
| 429 | /// selected clocks are active at a suitable level at time of construction. These methods | ||
| 430 | /// return the frequency of the requested clock, in Hertz, or a [`ClockError`]. | ||
| 431 | impl Clocks { | ||
| 432 | /// Ensure the `fro_lf_div` clock is active and valid at the given power state. | ||
| 433 | pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 434 | let Some(clk) = self.fro_lf_div.as_ref() else { | ||
| 435 | return Err(ClockError::BadConfig { | ||
| 436 | clock: "fro_lf_div", | ||
| 437 | reason: "required but not active", | ||
| 438 | }); | ||
| 439 | }; | ||
| 440 | if !clk.power.meets_requirement_of(at_level) { | ||
| 441 | return Err(ClockError::BadConfig { | ||
| 442 | clock: "fro_lf_div", | ||
| 443 | reason: "not low power active", | ||
| 444 | }); | ||
| 445 | } | ||
| 446 | Ok(clk.frequency) | ||
| 447 | } | ||
| 448 | |||
| 449 | /// Ensure the `fro_hf` clock is active and valid at the given power state. | ||
| 450 | pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 451 | let Some(clk) = self.fro_hf.as_ref() else { | ||
| 452 | return Err(ClockError::BadConfig { | ||
| 453 | clock: "fro_hf", | ||
| 454 | reason: "required but not active", | ||
| 455 | }); | ||
| 456 | }; | ||
| 457 | if !clk.power.meets_requirement_of(at_level) { | ||
| 458 | return Err(ClockError::BadConfig { | ||
| 459 | clock: "fro_hf", | ||
| 460 | reason: "not low power active", | ||
| 461 | }); | ||
| 462 | } | ||
| 463 | Ok(clk.frequency) | ||
| 464 | } | ||
| 465 | |||
| 466 | /// Ensure the `fro_hf_div` clock is active and valid at the given power state. | ||
| 467 | pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 468 | let Some(clk) = self.fro_hf_div.as_ref() else { | ||
| 469 | return Err(ClockError::BadConfig { | ||
| 470 | clock: "fro_hf_div", | ||
| 471 | reason: "required but not active", | ||
| 472 | }); | ||
| 473 | }; | ||
| 474 | if !clk.power.meets_requirement_of(at_level) { | ||
| 475 | return Err(ClockError::BadConfig { | ||
| 476 | clock: "fro_hf_div", | ||
| 477 | reason: "not low power active", | ||
| 478 | }); | ||
| 479 | } | ||
| 480 | Ok(clk.frequency) | ||
| 481 | } | ||
| 482 | |||
| 483 | /// Ensure the `clk_in` clock is active and valid at the given power state. | ||
| 484 | pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 485 | Err(ClockError::NotImplemented { clock: "clk_in" }) | ||
| 486 | } | ||
| 487 | |||
| 488 | /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. | ||
| 489 | pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 490 | // NOTE: clk_16k is always active in low power mode | ||
| 491 | Ok(self | ||
| 492 | .clk_16k_vsys | ||
| 493 | .as_ref() | ||
| 494 | .ok_or(ClockError::BadConfig { | ||
| 495 | clock: "clk_16k_vsys", | ||
| 496 | reason: "required but not active", | ||
| 497 | })? | ||
| 498 | .frequency) | ||
| 499 | } | ||
| 500 | |||
| 501 | /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state. | ||
| 502 | pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 503 | // NOTE: clk_16k is always active in low power mode | ||
| 504 | Ok(self | ||
| 505 | .clk_16k_vdd_core | ||
| 506 | .as_ref() | ||
| 507 | .ok_or(ClockError::BadConfig { | ||
| 508 | clock: "clk_16k_vdd_core", | ||
| 509 | reason: "required but not active", | ||
| 510 | })? | ||
| 511 | .frequency) | ||
| 512 | } | ||
| 513 | |||
| 514 | /// Ensure the `clk_1m` clock is active and valid at the given power state. | ||
| 515 | pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 516 | let Some(clk) = self.clk_1m.as_ref() else { | ||
| 517 | return Err(ClockError::BadConfig { | ||
| 518 | clock: "clk_1m", | ||
| 519 | reason: "required but not active", | ||
| 520 | }); | ||
| 521 | }; | ||
| 522 | if !clk.power.meets_requirement_of(at_level) { | ||
| 523 | return Err(ClockError::BadConfig { | ||
| 524 | clock: "clk_1m", | ||
| 525 | reason: "not low power active", | ||
| 526 | }); | ||
| 527 | } | ||
| 528 | Ok(clk.frequency) | ||
| 529 | } | ||
| 530 | |||
| 531 | /// Ensure the `pll1_clk` clock is active and valid at the given power state. | ||
| 532 | pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 533 | Err(ClockError::NotImplemented { clock: "pll1_clk" }) | ||
| 534 | } | ||
| 535 | |||
| 536 | /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. | ||
| 537 | pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 538 | Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active | ||
| 542 | pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 543 | let Some(clk) = self.cpu_system_clk.as_ref() else { | ||
| 544 | return Err(ClockError::BadConfig { | ||
| 545 | clock: "cpu_system_clk", | ||
| 546 | reason: "required but not active", | ||
| 547 | }); | ||
| 548 | }; | ||
| 549 | // Can the main_clk ever be active in deep sleep? I think it is gated? | ||
| 550 | match at_level { | ||
| 551 | PoweredClock::NormalEnabledDeepSleepDisabled => {} | ||
| 552 | PoweredClock::AlwaysEnabled => { | ||
| 553 | return Err(ClockError::BadConfig { | ||
| 554 | clock: "main_clk", | ||
| 555 | reason: "not low power active", | ||
| 556 | }); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | |||
| 560 | Ok(clk.frequency) | ||
| 561 | } | ||
| 562 | |||
| 563 | pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 564 | let freq = self.ensure_cpu_system_clk_active(at_level)?; | ||
| 565 | |||
| 566 | Ok(freq / 6) | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | impl PoweredClock { | ||
| 571 | /// Does THIS clock meet the power requirements of the OTHER clock? | ||
| 572 | pub fn meets_requirement_of(&self, other: &Self) -> bool { | ||
| 573 | match (self, other) { | ||
| 574 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false, | ||
| 575 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 576 | (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 577 | (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true, | ||
| 578 | } | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | impl ClockOperator<'_> { | ||
| 583 | /// Configure the FIRC/FRO180M clock family | ||
| 584 | /// | ||
| 585 | /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used | ||
| 586 | /// as the main clock used for the CPU, AHB, APB, etc. | ||
| 587 | fn configure_firc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 588 | const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig { | ||
| 589 | clock: "firc", | ||
| 590 | reason: "For now, FIRC must be enabled and in default state!", | ||
| 591 | }); | ||
| 592 | |||
| 593 | // Did the user give us a FIRC config? | ||
| 594 | let Some(firc) = self.config.firc.as_ref() else { | ||
| 595 | return HARDCODED_ERR; | ||
| 596 | }; | ||
| 597 | // Is the FIRC set to 45MHz (should be reset default) | ||
| 598 | if !matches!(firc.frequency, FircFreqSel::Mhz45) { | ||
| 599 | return HARDCODED_ERR; | ||
| 600 | } | ||
| 601 | let base_freq = 45_000_000; | ||
| 602 | |||
| 603 | // Now, check if the FIRC as expected for our hardcoded value | ||
| 604 | let mut firc_ok = true; | ||
| 605 | |||
| 606 | // Is the hardware currently set to the default 45MHz? | ||
| 607 | // | ||
| 608 | // NOTE: the SVD currently has the wrong(?) values for these: | ||
| 609 | // 45 -> 48 | ||
| 610 | // 60 -> 64 | ||
| 611 | // 90 -> 96 | ||
| 612 | // 180 -> 192 | ||
| 613 | // Probably correct-ish, but for a different trim value? | ||
| 614 | firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s(); | ||
| 615 | |||
| 616 | // Check some values in the CSR | ||
| 617 | let csr = self.scg0.firccsr().read(); | ||
| 618 | // Is it enabled? | ||
| 619 | firc_ok &= csr.fircen().is_enabled(); | ||
| 620 | // Is it accurate? | ||
| 621 | firc_ok &= csr.fircacc().is_enabled_and_valid(); | ||
| 622 | // Is there no error? | ||
| 623 | firc_ok &= csr.fircerr().is_error_not_detected(); | ||
| 624 | // Is the FIRC the system clock? | ||
| 625 | firc_ok &= csr.fircsel().is_firc(); | ||
| 626 | // Is it valid? | ||
| 627 | firc_ok &= csr.fircvld().is_enabled_and_valid(); | ||
| 628 | |||
| 629 | // Are we happy with the current (hardcoded) state? | ||
| 630 | if !firc_ok { | ||
| 631 | return HARDCODED_ERR; | ||
| 632 | } | ||
| 633 | |||
| 634 | // Note that the fro_hf_root is active | ||
| 635 | self.clocks.fro_hf_root = Some(Clock { | ||
| 636 | frequency: base_freq, | ||
| 637 | power: firc.power, | ||
| 638 | }); | ||
| 639 | |||
| 640 | // Okay! Now we're past that, let's enable all the downstream clocks. | ||
| 641 | let FircConfig { | ||
| 642 | frequency: _, | ||
| 643 | power, | ||
| 644 | fro_hf_enabled, | ||
| 645 | clk_45m_enabled, | ||
| 646 | fro_hf_div, | ||
| 647 | } = firc; | ||
| 648 | |||
| 649 | // When is the FRO enabled? | ||
| 650 | let pow_set = match power { | ||
| 651 | PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes, | ||
| 652 | PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes, | ||
| 653 | }; | ||
| 654 | |||
| 655 | // Do we enable the `fro_hf` output? | ||
| 656 | let fro_hf_set = if *fro_hf_enabled { | ||
| 657 | self.clocks.fro_hf = Some(Clock { | ||
| 658 | frequency: base_freq, | ||
| 659 | power: *power, | ||
| 660 | }); | ||
| 661 | FircFclkPeriphEn::Enabled | ||
| 662 | } else { | ||
| 663 | FircFclkPeriphEn::Disabled | ||
| 664 | }; | ||
| 665 | |||
| 666 | // Do we enable the `clk_45m` output? | ||
| 667 | let clk_45m_set = if *clk_45m_enabled { | ||
| 668 | self.clocks.clk_45m = Some(Clock { | ||
| 669 | frequency: 45_000_000, | ||
| 670 | power: *power, | ||
| 671 | }); | ||
| 672 | FircSclkPeriphEn::Enabled | ||
| 673 | } else { | ||
| 674 | FircSclkPeriphEn::Disabled | ||
| 675 | }; | ||
| 676 | |||
| 677 | self.scg0.firccsr().modify(|_r, w| { | ||
| 678 | w.fircsten().variant(pow_set); | ||
| 679 | w.firc_fclk_periph_en().variant(fro_hf_set); | ||
| 680 | w.firc_sclk_periph_en().variant(clk_45m_set); | ||
| 681 | w | ||
| 682 | }); | ||
| 683 | |||
| 684 | // Do we enable the `fro_hf_div` output? | ||
| 685 | if let Some(d) = fro_hf_div.as_ref() { | ||
| 686 | // We need `fro_hf` to be enabled | ||
| 687 | if !*fro_hf_enabled { | ||
| 688 | return Err(ClockError::BadConfig { | ||
| 689 | clock: "fro_hf_div", | ||
| 690 | reason: "fro_hf not enabled", | ||
| 691 | }); | ||
| 692 | } | ||
| 693 | |||
| 694 | // Halt and reset the div; then set our desired div. | ||
| 695 | self.syscon.frohfdiv().write(|w| { | ||
| 696 | w.halt().halt(); | ||
| 697 | w.reset().asserted(); | ||
| 698 | unsafe { w.div().bits(d.into_bits()) }; | ||
| 699 | w | ||
| 700 | }); | ||
| 701 | // Then unhalt it, and reset it | ||
| 702 | self.syscon.frohfdiv().write(|w| { | ||
| 703 | w.halt().run(); | ||
| 704 | w.reset().released(); | ||
| 705 | w | ||
| 706 | }); | ||
| 707 | |||
| 708 | // Wait for clock to stabilize | ||
| 709 | while self.syscon.frohfdiv().read().unstab().is_ongoing() {} | ||
| 710 | |||
| 711 | // Store off the clock info | ||
| 712 | self.clocks.fro_hf_div = Some(Clock { | ||
| 713 | frequency: base_freq / d.into_divisor(), | ||
| 714 | power: *power, | ||
| 715 | }); | ||
| 716 | } | ||
| 717 | |||
| 718 | Ok(()) | ||
| 719 | } | ||
| 720 | |||
| 721 | /// Configure the SIRC/FRO12M clock family | ||
| 722 | fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 723 | let SircConfig { | ||
| 724 | power, | ||
| 725 | fro_12m_enabled, | ||
| 726 | fro_lf_div, | ||
| 727 | } = &self.config.sirc; | ||
| 728 | let base_freq = 12_000_000; | ||
| 729 | |||
| 730 | // Allow writes | ||
| 731 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled()); | ||
| 732 | self.clocks.fro_12m_root = Some(Clock { | ||
| 733 | frequency: base_freq, | ||
| 734 | power: *power, | ||
| 735 | }); | ||
| 736 | |||
| 737 | let deep = match power { | ||
| 738 | PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled, | ||
| 739 | PoweredClock::AlwaysEnabled => Sircsten::Enabled, | ||
| 740 | }; | ||
| 741 | let pclk = if *fro_12m_enabled { | ||
| 742 | self.clocks.fro_12m = Some(Clock { | ||
| 743 | frequency: base_freq, | ||
| 744 | power: *power, | ||
| 745 | }); | ||
| 746 | self.clocks.clk_1m = Some(Clock { | ||
| 747 | frequency: base_freq / 12, | ||
| 748 | power: *power, | ||
| 749 | }); | ||
| 750 | SircClkPeriphEn::Enabled | ||
| 751 | } else { | ||
| 752 | SircClkPeriphEn::Disabled | ||
| 753 | }; | ||
| 754 | |||
| 755 | // Set sleep/peripheral usage | ||
| 756 | self.scg0.sirccsr().modify(|_r, w| { | ||
| 757 | w.sircsten().variant(deep); | ||
| 758 | w.sirc_clk_periph_en().variant(pclk); | ||
| 759 | w | ||
| 760 | }); | ||
| 761 | |||
| 762 | while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {} | ||
| 763 | if self.scg0.sirccsr().read().sircerr().is_error_detected() { | ||
| 764 | return Err(ClockError::BadConfig { | ||
| 765 | clock: "sirc", | ||
| 766 | reason: "error set", | ||
| 767 | }); | ||
| 768 | } | ||
| 769 | |||
| 770 | // reset lock | ||
| 771 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled()); | ||
| 772 | |||
| 773 | // Do we enable the `fro_lf_div` output? | ||
| 774 | if let Some(d) = fro_lf_div.as_ref() { | ||
| 775 | // We need `fro_lf` to be enabled | ||
| 776 | if !*fro_12m_enabled { | ||
| 777 | return Err(ClockError::BadConfig { | ||
| 778 | clock: "fro_lf_div", | ||
| 779 | reason: "fro_12m not enabled", | ||
| 780 | }); | ||
| 781 | } | ||
| 782 | |||
| 783 | // Halt and reset the div; then set our desired div. | ||
| 784 | self.syscon.frolfdiv().write(|w| { | ||
| 785 | w.halt().halt(); | ||
| 786 | w.reset().asserted(); | ||
| 787 | unsafe { w.div().bits(d.into_bits()) }; | ||
| 788 | w | ||
| 789 | }); | ||
| 790 | // Then unhalt it, and reset it | ||
| 791 | self.syscon.frolfdiv().modify(|_r, w| { | ||
| 792 | w.halt().run(); | ||
| 793 | w.reset().released(); | ||
| 794 | w | ||
| 795 | }); | ||
| 796 | |||
| 797 | // Wait for clock to stabilize | ||
| 798 | while self.syscon.frolfdiv().read().unstab().is_ongoing() {} | ||
| 799 | |||
| 800 | // Store off the clock info | ||
| 801 | self.clocks.fro_lf_div = Some(Clock { | ||
| 802 | frequency: base_freq / d.into_divisor(), | ||
| 803 | power: *power, | ||
| 804 | }); | ||
| 805 | } | ||
| 806 | |||
| 807 | Ok(()) | ||
| 808 | } | ||
| 809 | |||
| 810 | /// Configure the FRO16K/clk_16k clock family | ||
| 811 | fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { | ||
| 812 | let Some(fro16k) = self.config.fro16k.as_ref() else { | ||
| 813 | return Ok(()); | ||
| 814 | }; | ||
| 815 | // Enable FRO16K oscillator | ||
| 816 | self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit()); | ||
| 817 | |||
| 818 | // Lock the control register | ||
| 819 | self.vbat0.frolcka().modify(|_, w| w.lock().set_bit()); | ||
| 820 | |||
| 821 | let Fro16KConfig { | ||
| 822 | vsys_domain_active, | ||
| 823 | vdd_core_domain_active, | ||
| 824 | } = fro16k; | ||
| 825 | |||
| 826 | // Enable clock outputs to both VSYS and VDD_CORE domains | ||
| 827 | // Bit 0: clk_16k0 to VSYS domain | ||
| 828 | // Bit 1: clk_16k1 to VDD_CORE domain | ||
| 829 | // | ||
| 830 | // TODO: Define sub-fields for this register with a PAC patch? | ||
| 831 | let mut bits = 0; | ||
| 832 | if *vsys_domain_active { | ||
| 833 | bits |= 0b01; | ||
| 834 | self.clocks.clk_16k_vsys = Some(Clock { | ||
| 835 | frequency: 16_384, | ||
| 836 | power: PoweredClock::AlwaysEnabled, | ||
| 837 | }); | ||
| 838 | } | ||
| 839 | if *vdd_core_domain_active { | ||
| 840 | bits |= 0b10; | ||
| 841 | self.clocks.clk_16k_vdd_core = Some(Clock { | ||
| 842 | frequency: 16_384, | ||
| 843 | power: PoweredClock::AlwaysEnabled, | ||
| 844 | }); | ||
| 845 | } | ||
| 846 | self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) }); | ||
| 847 | |||
| 848 | Ok(()) | ||
| 849 | } | ||
| 850 | } | ||
| 851 | |||
| 852 | // | ||
| 853 | // Macros/macro impls | ||
| 854 | // | ||
| 855 | |||
| 856 | /// This macro is used to implement the [`Gate`] trait for a given peripheral | ||
| 857 | /// that is controlled by the MRCC peripheral. | ||
| 858 | macro_rules! impl_cc_gate { | ||
| 859 | ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => { | ||
| 860 | impl Gate for crate::peripherals::$name { | ||
| 861 | type MrccPeriphConfig = $config; | ||
| 862 | |||
| 863 | #[inline] | ||
| 864 | unsafe fn enable_clock() { | ||
| 865 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 866 | mrcc.$clk_reg().modify(|_, w| w.$field().enabled()); | ||
| 867 | } | ||
| 868 | |||
| 869 | #[inline] | ||
| 870 | unsafe fn disable_clock() { | ||
| 871 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 872 | mrcc.$clk_reg().modify(|_r, w| w.$field().disabled()); | ||
| 873 | } | ||
| 874 | |||
| 875 | #[inline] | ||
| 876 | fn is_clock_enabled() -> bool { | ||
| 877 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 878 | mrcc.$clk_reg().read().$field().is_enabled() | ||
| 879 | } | ||
| 880 | |||
| 881 | #[inline] | ||
| 882 | unsafe fn release_reset() { | ||
| 883 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 884 | mrcc.$rst_reg().modify(|_, w| w.$field().enabled()); | ||
| 885 | } | ||
| 886 | |||
| 887 | #[inline] | ||
| 888 | unsafe fn assert_reset() { | ||
| 889 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 890 | mrcc.$rst_reg().modify(|_, w| w.$field().disabled()); | ||
| 891 | } | ||
| 892 | |||
| 893 | #[inline] | ||
| 894 | fn is_reset_released() -> bool { | ||
| 895 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 896 | mrcc.$rst_reg().read().$field().is_enabled() | ||
| 897 | } | ||
| 898 | } | ||
| 899 | }; | ||
| 900 | } | ||
| 901 | |||
| 902 | /// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait, | ||
| 903 | /// for various low level peripherals. | ||
| 904 | pub(crate) mod gate { | ||
| 905 | #[cfg(not(feature = "time"))] | ||
| 906 | use super::periph_helpers::OsTimerConfig; | ||
| 907 | use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig}; | ||
| 908 | use super::*; | ||
| 909 | |||
| 910 | // These peripherals have no additional upstream clocks or configuration required | ||
| 911 | // other than enabling through the MRCC gate. Currently, these peripherals will | ||
| 912 | // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or | ||
| 913 | // [`SPConfHelper::post_enable_config()`]. | ||
| 914 | impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig); | ||
| 915 | impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig); | ||
| 916 | impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig); | ||
| 917 | impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig); | ||
| 918 | impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig); | ||
| 919 | |||
| 920 | impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig); | ||
| 921 | impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig); | ||
| 922 | impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig); | ||
| 923 | impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig); | ||
| 924 | impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig); | ||
| 925 | |||
| 926 | // These peripherals DO have meaningful configuration, and could fail if the system | ||
| 927 | // clocks do not match their needs. | ||
| 928 | #[cfg(not(feature = "time"))] | ||
| 929 | impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig); | ||
| 930 | |||
| 931 | impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig); | ||
| 932 | impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig); | ||
| 933 | impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig); | ||
| 934 | impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig); | ||
| 935 | |||
| 936 | impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig); | ||
| 937 | impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig); | ||
| 938 | impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig); | ||
| 939 | impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig); | ||
| 940 | impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig); | ||
| 941 | impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig); | ||
| 942 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); | ||
| 943 | } | ||
