diff options
| author | James Munns <[email protected]> | 2025-12-04 18:46:39 +0100 |
|---|---|---|
| committer | James Munns <[email protected]> | 2025-12-04 18:47:31 +0100 |
| commit | c3bc8fe8c0db112e5f2f66580104fc49b02890d2 (patch) | |
| tree | eea7adc15021697ea980289edd6235daaa12d6ee /embassy-mcxa/src/clocks/periph_helpers.rs | |
| parent | 5182b2ed54f991b40b45fd2de75f597358ff9c3e (diff) | |
| parent | 277ab0d2e8714edf37a0ee84cda1059d9944ecef (diff) | |
Import embassy-mcxa repo
Merge remote-tracking branch 'james-e-mcxa/james/upstream' into james/upstream-mcxa
Diffstat (limited to 'embassy-mcxa/src/clocks/periph_helpers.rs')
| -rw-r--r-- | embassy-mcxa/src/clocks/periph_helpers.rs | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/embassy-mcxa/src/clocks/periph_helpers.rs b/embassy-mcxa/src/clocks/periph_helpers.rs new file mode 100644 index 000000000..1ea7a99ed --- /dev/null +++ b/embassy-mcxa/src/clocks/periph_helpers.rs | |||
| @@ -0,0 +1,502 @@ | |||
| 1 | //! Peripheral Helpers | ||
| 2 | //! | ||
| 3 | //! The purpose of this module is to define the per-peripheral special handling | ||
| 4 | //! required from a clocking perspective. Different peripherals have different | ||
| 5 | //! selectable source clocks, and some peripherals have additional pre-dividers | ||
| 6 | //! that can be used. | ||
| 7 | //! | ||
| 8 | //! See the docs of [`SPConfHelper`] for more details. | ||
| 9 | |||
| 10 | use super::{ClockError, Clocks, PoweredClock}; | ||
| 11 | use crate::pac; | ||
| 12 | |||
| 13 | /// Sealed Peripheral Configuration Helper | ||
| 14 | /// | ||
| 15 | /// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the | ||
| 16 | /// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should | ||
| 17 | /// fix the name, or actually do the sealing of peripherals. | ||
| 18 | /// | ||
| 19 | /// This trait serves to act as a per-peripheral customization for clocking behavior. | ||
| 20 | /// | ||
| 21 | /// This trait should be implemented on a configuration type for a given peripheral, and | ||
| 22 | /// provide the methods that will be called by the higher level operations like | ||
| 23 | /// `embassy_mcxa::clocks::enable_and_reset()`. | ||
| 24 | pub trait SPConfHelper { | ||
| 25 | /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated), | ||
| 26 | /// but BEFORE the peripheral reset line is reset. | ||
| 27 | /// | ||
| 28 | /// This function should check that any relevant upstream clocks are enabled, are in a | ||
| 29 | /// reasonable power state, and that the requested configuration can be made. If any of | ||
| 30 | /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`. | ||
| 31 | /// | ||
| 32 | /// This function SHOULD NOT make any changes to the system clock configuration, even | ||
| 33 | /// unsafely, as this should remain static for the duration of the program. | ||
| 34 | /// | ||
| 35 | /// This function WILL be called in a critical section, care should be taken not to delay | ||
| 36 | /// for an unreasonable amount of time. | ||
| 37 | /// | ||
| 38 | /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency | ||
| 39 | /// fed into the peripheral, taking into account the selected source clock, as well as | ||
| 40 | /// any pre-divisors. | ||
| 41 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Copy and paste macro that: | ||
| 45 | /// | ||
| 46 | /// * Sets the clocksel mux to `$selvar` | ||
| 47 | /// * Resets and halts the div, and applies the calculated div4 bits | ||
| 48 | /// * Releases reset + halt | ||
| 49 | /// * Waits for the div to stabilize | ||
| 50 | /// * Returns `Ok($freq / $conf.div.into_divisor())` | ||
| 51 | /// | ||
| 52 | /// Assumes: | ||
| 53 | /// | ||
| 54 | /// * self is a configuration struct that has a field called `div`, which | ||
| 55 | /// is a `Div4` | ||
| 56 | /// | ||
| 57 | /// usage: | ||
| 58 | /// | ||
| 59 | /// ```rust | ||
| 60 | /// apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 61 | /// ``` | ||
| 62 | /// | ||
| 63 | /// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom | ||
| 64 | /// registers, or if we put some kind of simple trait around those regs, we could | ||
| 65 | /// do this with something other than a macro, but for now, this is harm-reduction | ||
| 66 | /// to avoid incorrect copy + paste | ||
| 67 | macro_rules! apply_div4 { | ||
| 68 | ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{ | ||
| 69 | // set clksel | ||
| 70 | $selreg.modify(|_r, w| w.mux().variant($selvar)); | ||
| 71 | |||
| 72 | // Set up clkdiv | ||
| 73 | $divreg.modify(|_r, w| { | ||
| 74 | unsafe { w.div().bits($conf.div.into_bits()) } | ||
| 75 | .halt() | ||
| 76 | .asserted() | ||
| 77 | .reset() | ||
| 78 | .asserted() | ||
| 79 | }); | ||
| 80 | $divreg.modify(|_r, w| w.halt().deasserted().reset().deasserted()); | ||
| 81 | |||
| 82 | while $divreg.read().unstab().is_unstable() {} | ||
| 83 | |||
| 84 | Ok($freq / $conf.div.into_divisor()) | ||
| 85 | }}; | ||
| 86 | } | ||
| 87 | |||
| 88 | // config types | ||
| 89 | |||
| 90 | /// This type represents a divider in the range 1..=16. | ||
| 91 | /// | ||
| 92 | /// At a hardware level, this is an 8-bit register from 0..=15, | ||
| 93 | /// which adds one. | ||
| 94 | /// | ||
| 95 | /// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain | ||
| 96 | /// seems to use 4 bit dividers! | ||
| 97 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 98 | pub struct Div4(pub(super) u8); | ||
| 99 | |||
| 100 | impl Div4 { | ||
| 101 | /// Divide by one, or no division | ||
| 102 | pub const fn no_div() -> Self { | ||
| 103 | Self(0) | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Store a "raw" divisor value that will divide the source by | ||
| 107 | /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source | ||
| 108 | /// by 1, and `Div4::from_raw(15)` will divide the source by | ||
| 109 | /// 16. | ||
| 110 | pub const fn from_raw(n: u8) -> Option<Self> { | ||
| 111 | if n > 0b1111 { | ||
| 112 | None | ||
| 113 | } else { | ||
| 114 | Some(Self(n)) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Store a specific divisor value that will divide the source | ||
| 119 | /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source | ||
| 120 | /// by 1, and `Div4::from_divisor(16)` will divide the source | ||
| 121 | /// by 16. | ||
| 122 | /// | ||
| 123 | /// Will return `None` if `n` is not in the range `1..=16`. | ||
| 124 | /// Consider [`Self::from_raw`] for an infallible version. | ||
| 125 | pub const fn from_divisor(n: u8) -> Option<Self> { | ||
| 126 | let Some(n) = n.checked_sub(1) else { | ||
| 127 | return None; | ||
| 128 | }; | ||
| 129 | if n > 0b1111 { | ||
| 130 | return None; | ||
| 131 | } | ||
| 132 | Some(Self(n)) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Convert into "raw" bits form | ||
| 136 | #[inline(always)] | ||
| 137 | pub const fn into_bits(self) -> u8 { | ||
| 138 | self.0 | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Convert into "divisor" form, as a u32 for convenient frequency math | ||
| 142 | #[inline(always)] | ||
| 143 | pub const fn into_divisor(self) -> u32 { | ||
| 144 | self.0 as u32 + 1 | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /// A basic type that always returns an error when `post_enable_config` is called. | ||
| 149 | /// | ||
| 150 | /// Should only be used as a placeholder. | ||
| 151 | pub struct UnimplementedConfig; | ||
| 152 | |||
| 153 | impl SPConfHelper for UnimplementedConfig { | ||
| 154 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 155 | Err(ClockError::UnimplementedConfig) | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// A basic type that always returns `Ok(0)` when `post_enable_config` is called. | ||
| 160 | /// | ||
| 161 | /// This should only be used for peripherals that are "ambiently" clocked, like `PORTn` | ||
| 162 | /// peripherals, which have no selectable/configurable source clock. | ||
| 163 | pub struct NoConfig; | ||
| 164 | impl SPConfHelper for NoConfig { | ||
| 165 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 166 | Ok(0) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | // | ||
| 171 | // LPI2c | ||
| 172 | // | ||
| 173 | |||
| 174 | /// Selectable clocks for `Lpi2c` peripherals | ||
| 175 | #[derive(Debug, Clone, Copy)] | ||
| 176 | pub enum Lpi2cClockSel { | ||
| 177 | /// FRO12M/FRO_LF/SIRC clock source, passed through divider | ||
| 178 | /// "fro_lf_div" | ||
| 179 | FroLfDiv, | ||
| 180 | /// FRO180M/FRO_HF/FIRC clock source, passed through divider | ||
| 181 | /// "fro_hf_div" | ||
| 182 | FroHfDiv, | ||
| 183 | /// SOSC/XTAL/EXTAL clock source | ||
| 184 | ClkIn, | ||
| 185 | /// clk_1m/FRO_LF divided by 12 | ||
| 186 | Clk1M, | ||
| 187 | /// Output of PLL1, passed through clock divider, | ||
| 188 | /// "pll1_clk_div", maybe "pll1_lf_div"? | ||
| 189 | Pll1ClkDiv, | ||
| 190 | /// Disabled | ||
| 191 | None, | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Which instance of the `Lpi2c` is this? | ||
| 195 | /// | ||
| 196 | /// Should not be directly selectable by end-users. | ||
| 197 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 198 | pub enum Lpi2cInstance { | ||
| 199 | /// Instance 0 | ||
| 200 | Lpi2c0, | ||
| 201 | /// Instance 1 | ||
| 202 | Lpi2c1, | ||
| 203 | /// Instance 2 | ||
| 204 | Lpi2c2, | ||
| 205 | /// Instance 3 | ||
| 206 | Lpi2c3, | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Top level configuration for `Lpi2c` instances. | ||
| 210 | pub struct Lpi2cConfig { | ||
| 211 | /// Power state required for this peripheral | ||
| 212 | pub power: PoweredClock, | ||
| 213 | /// Clock source | ||
| 214 | pub source: Lpi2cClockSel, | ||
| 215 | /// Clock divisor | ||
| 216 | pub div: Div4, | ||
| 217 | /// Which instance is this? | ||
| 218 | // NOTE: should not be user settable | ||
| 219 | pub(crate) instance: Lpi2cInstance, | ||
| 220 | } | ||
| 221 | |||
| 222 | impl SPConfHelper for Lpi2cConfig { | ||
| 223 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 224 | // check that source is suitable | ||
| 225 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 226 | use mcxa_pac::mrcc0::mrcc_lpi2c0_clksel::Mux; | ||
| 227 | |||
| 228 | let (clkdiv, clksel) = match self.instance { | ||
| 229 | Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()), | ||
| 230 | Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()), | ||
| 231 | Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()), | ||
| 232 | Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()), | ||
| 233 | }; | ||
| 234 | |||
| 235 | let (freq, variant) = match self.source { | ||
| 236 | Lpi2cClockSel::FroLfDiv => { | ||
| 237 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 238 | (freq, Mux::ClkrootFunc0) | ||
| 239 | } | ||
| 240 | Lpi2cClockSel::FroHfDiv => { | ||
| 241 | let freq = clocks.ensure_fro_hf_div_active(&self.power)?; | ||
| 242 | (freq, Mux::ClkrootFunc2) | ||
| 243 | } | ||
| 244 | Lpi2cClockSel::ClkIn => { | ||
| 245 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 246 | (freq, Mux::ClkrootFunc3) | ||
| 247 | } | ||
| 248 | Lpi2cClockSel::Clk1M => { | ||
| 249 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 250 | (freq, Mux::ClkrootFunc5) | ||
| 251 | } | ||
| 252 | Lpi2cClockSel::Pll1ClkDiv => { | ||
| 253 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 254 | (freq, Mux::ClkrootFunc6) | ||
| 255 | } | ||
| 256 | Lpi2cClockSel::None => unsafe { | ||
| 257 | // no ClkrootFunc7, just write manually for now | ||
| 258 | clksel.write(|w| w.bits(0b111)); | ||
| 259 | clkdiv.modify(|_r, w| w.reset().asserted().halt().asserted()); | ||
| 260 | return Ok(0); | ||
| 261 | }, | ||
| 262 | }; | ||
| 263 | |||
| 264 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | // | ||
| 269 | // LPUart | ||
| 270 | // | ||
| 271 | |||
| 272 | /// Selectable clocks for Lpuart peripherals | ||
| 273 | #[derive(Debug, Clone, Copy)] | ||
| 274 | pub enum LpuartClockSel { | ||
| 275 | /// FRO12M/FRO_LF/SIRC clock source, passed through divider | ||
| 276 | /// "fro_lf_div" | ||
| 277 | FroLfDiv, | ||
| 278 | /// FRO180M/FRO_HF/FIRC clock source, passed through divider | ||
| 279 | /// "fro_hf_div" | ||
| 280 | FroHfDiv, | ||
| 281 | /// SOSC/XTAL/EXTAL clock source | ||
| 282 | ClkIn, | ||
| 283 | /// FRO16K/clk_16k source | ||
| 284 | Clk16K, | ||
| 285 | /// clk_1m/FRO_LF divided by 12 | ||
| 286 | Clk1M, | ||
| 287 | /// Output of PLL1, passed through clock divider, | ||
| 288 | /// "pll1_clk_div", maybe "pll1_lf_div"? | ||
| 289 | Pll1ClkDiv, | ||
| 290 | /// Disabled | ||
| 291 | None, | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Which instance of the Lpuart is this? | ||
| 295 | /// | ||
| 296 | /// Should not be directly selectable by end-users. | ||
| 297 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 298 | pub enum LpuartInstance { | ||
| 299 | /// Instance 0 | ||
| 300 | Lpuart0, | ||
| 301 | /// Instance 1 | ||
| 302 | Lpuart1, | ||
| 303 | /// Instance 2 | ||
| 304 | Lpuart2, | ||
| 305 | /// Instance 3 | ||
| 306 | Lpuart3, | ||
| 307 | /// Instance 4 | ||
| 308 | Lpuart4, | ||
| 309 | /// Instance 5 | ||
| 310 | Lpuart5, | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Top level configuration for `Lpuart` instances. | ||
| 314 | pub struct LpuartConfig { | ||
| 315 | /// Power state required for this peripheral | ||
| 316 | pub power: PoweredClock, | ||
| 317 | /// Clock source | ||
| 318 | pub source: LpuartClockSel, | ||
| 319 | /// Clock divisor | ||
| 320 | pub div: Div4, | ||
| 321 | /// Which instance is this? | ||
| 322 | // NOTE: should not be user settable | ||
| 323 | pub(crate) instance: LpuartInstance, | ||
| 324 | } | ||
| 325 | |||
| 326 | impl SPConfHelper for LpuartConfig { | ||
| 327 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 328 | // check that source is suitable | ||
| 329 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 330 | use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux; | ||
| 331 | |||
| 332 | let (clkdiv, clksel) = match self.instance { | ||
| 333 | LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()), | ||
| 334 | LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()), | ||
| 335 | LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()), | ||
| 336 | LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()), | ||
| 337 | LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()), | ||
| 338 | LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()), | ||
| 339 | }; | ||
| 340 | |||
| 341 | let (freq, variant) = match self.source { | ||
| 342 | LpuartClockSel::FroLfDiv => { | ||
| 343 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 344 | (freq, Mux::ClkrootFunc0) | ||
| 345 | } | ||
| 346 | LpuartClockSel::FroHfDiv => { | ||
| 347 | let freq = clocks.ensure_fro_hf_div_active(&self.power)?; | ||
| 348 | (freq, Mux::ClkrootFunc2) | ||
| 349 | } | ||
| 350 | LpuartClockSel::ClkIn => { | ||
| 351 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 352 | (freq, Mux::ClkrootFunc3) | ||
| 353 | } | ||
| 354 | LpuartClockSel::Clk16K => { | ||
| 355 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 356 | (freq, Mux::ClkrootFunc4) | ||
| 357 | } | ||
| 358 | LpuartClockSel::Clk1M => { | ||
| 359 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 360 | (freq, Mux::ClkrootFunc5) | ||
| 361 | } | ||
| 362 | LpuartClockSel::Pll1ClkDiv => { | ||
| 363 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 364 | (freq, Mux::ClkrootFunc6) | ||
| 365 | } | ||
| 366 | LpuartClockSel::None => unsafe { | ||
| 367 | // no ClkrootFunc7, just write manually for now | ||
| 368 | clksel.write(|w| w.bits(0b111)); | ||
| 369 | clkdiv.modify(|_r, w| { | ||
| 370 | w.reset().asserted(); | ||
| 371 | w.halt().asserted(); | ||
| 372 | w | ||
| 373 | }); | ||
| 374 | return Ok(0); | ||
| 375 | }, | ||
| 376 | }; | ||
| 377 | |||
| 378 | // set clksel | ||
| 379 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | // | ||
| 384 | // OSTimer | ||
| 385 | // | ||
| 386 | |||
| 387 | /// Selectable clocks for the OSTimer peripheral | ||
| 388 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 389 | pub enum OstimerClockSel { | ||
| 390 | /// 16k clock, sourced from FRO16K (Vdd Core) | ||
| 391 | Clk16kVddCore, | ||
| 392 | /// 1 MHz Clock sourced from FRO12M | ||
| 393 | Clk1M, | ||
| 394 | /// Disabled | ||
| 395 | None, | ||
| 396 | } | ||
| 397 | |||
| 398 | /// Top level configuration for the `OSTimer` peripheral | ||
| 399 | pub struct OsTimerConfig { | ||
| 400 | /// Power state required for this peripheral | ||
| 401 | pub power: PoweredClock, | ||
| 402 | /// Selected clock source for this peripheral | ||
| 403 | pub source: OstimerClockSel, | ||
| 404 | } | ||
| 405 | |||
| 406 | impl SPConfHelper for OsTimerConfig { | ||
| 407 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 408 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 409 | Ok(match self.source { | ||
| 410 | OstimerClockSel::Clk16kVddCore => { | ||
| 411 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 412 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k()); | ||
| 413 | freq | ||
| 414 | } | ||
| 415 | OstimerClockSel::Clk1M => { | ||
| 416 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 417 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); | ||
| 418 | freq | ||
| 419 | } | ||
| 420 | OstimerClockSel::None => { | ||
| 421 | mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) }); | ||
| 422 | 0 | ||
| 423 | } | ||
| 424 | }) | ||
| 425 | } | ||
| 426 | } | ||
| 427 | |||
| 428 | // | ||
| 429 | // Adc | ||
| 430 | // | ||
| 431 | |||
| 432 | /// Selectable clocks for the ADC peripheral | ||
| 433 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 434 | pub enum AdcClockSel { | ||
| 435 | /// Divided `fro_lf`/`clk_12m`/FRO12M source | ||
| 436 | FroLfDiv, | ||
| 437 | /// Gated `fro_hf`/`FRO180M` source | ||
| 438 | FroHf, | ||
| 439 | /// External Clock Source | ||
| 440 | ClkIn, | ||
| 441 | /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m` | ||
| 442 | Clk1M, | ||
| 443 | /// Internal PLL output, with configurable divisor | ||
| 444 | Pll1ClkDiv, | ||
| 445 | /// No clock/disabled | ||
| 446 | None, | ||
| 447 | } | ||
| 448 | |||
| 449 | /// Top level configuration for the ADC peripheral | ||
| 450 | pub struct AdcConfig { | ||
| 451 | /// Power state required for this peripheral | ||
| 452 | pub power: PoweredClock, | ||
| 453 | /// Selected clock-source for this peripheral | ||
| 454 | pub source: AdcClockSel, | ||
| 455 | /// Pre-divisor, applied to the upstream clock output | ||
| 456 | pub div: Div4, | ||
| 457 | } | ||
| 458 | |||
| 459 | impl SPConfHelper for AdcConfig { | ||
| 460 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 461 | use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux; | ||
| 462 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 463 | let (freq, variant) = match self.source { | ||
| 464 | AdcClockSel::FroLfDiv => { | ||
| 465 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 466 | (freq, Mux::ClkrootFunc0) | ||
| 467 | } | ||
| 468 | AdcClockSel::FroHf => { | ||
| 469 | let freq = clocks.ensure_fro_hf_active(&self.power)?; | ||
| 470 | (freq, Mux::ClkrootFunc1) | ||
| 471 | } | ||
| 472 | AdcClockSel::ClkIn => { | ||
| 473 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 474 | (freq, Mux::ClkrootFunc3) | ||
| 475 | } | ||
| 476 | AdcClockSel::Clk1M => { | ||
| 477 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 478 | (freq, Mux::ClkrootFunc5) | ||
| 479 | } | ||
| 480 | AdcClockSel::Pll1ClkDiv => { | ||
| 481 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 482 | (freq, Mux::ClkrootFunc6) | ||
| 483 | } | ||
| 484 | AdcClockSel::None => { | ||
| 485 | mrcc0.mrcc_adc_clksel().write(|w| unsafe { | ||
| 486 | // no ClkrootFunc7, just write manually for now | ||
| 487 | w.mux().bits(0b111) | ||
| 488 | }); | ||
| 489 | mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { | ||
| 490 | w.reset().asserted(); | ||
| 491 | w.halt().asserted(); | ||
| 492 | w | ||
| 493 | }); | ||
| 494 | return Ok(0); | ||
| 495 | } | ||
| 496 | }; | ||
| 497 | let clksel = mrcc0.mrcc_adc_clksel(); | ||
| 498 | let clkdiv = mrcc0.mrcc_adc_clkdiv(); | ||
| 499 | |||
| 500 | apply_div4!(self, clksel, clkdiv, variant, freq) | ||
| 501 | } | ||
| 502 | } | ||
