diff options
| -rw-r--r-- | embassy-mcxa/src/clocks/config.rs | 15 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 83 | ||||
| -rw-r--r-- | examples/mcxa/src/bin/clkout.rs | 25 |
3 files changed, 103 insertions, 20 deletions
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs index 0f362ad0a..3f1729d00 100644 --- a/embassy-mcxa/src/clocks/config.rs +++ b/embassy-mcxa/src/clocks/config.rs | |||
| @@ -157,6 +157,8 @@ pub struct SpllConfig { | |||
| 157 | pub source: SpllSource, | 157 | pub source: SpllSource, |
| 158 | pub mode: SpllMode, | 158 | pub mode: SpllMode, |
| 159 | pub power: PoweredClock, | 159 | pub power: PoweredClock, |
| 160 | /// Is the "pll1_clk_div" clock enabled? | ||
| 161 | pub pll1_clk_div: Option<Div8>, | ||
| 160 | } | 162 | } |
| 161 | 163 | ||
| 162 | pub enum SpllSource { | 164 | pub enum SpllSource { |
| @@ -168,7 +170,6 @@ pub enum SpllSource { | |||
| 168 | Firc, | 170 | Firc, |
| 169 | /// S Internal Oscillator (12M) | 171 | /// S Internal Oscillator (12M) |
| 170 | Sirc, | 172 | Sirc, |
| 171 | |||
| 172 | // TODO: the reference manual hints that ROSC is possible, | 173 | // TODO: the reference manual hints that ROSC is possible, |
| 173 | // however the minimum input frequency is 32K, but ROSC is 16K. | 174 | // however the minimum input frequency is 32K, but ROSC is 16K. |
| 174 | // Some diagrams show this option, and some diagrams omit it. | 175 | // Some diagrams show this option, and some diagrams omit it. |
| @@ -182,9 +183,7 @@ pub enum SpllSource { | |||
| 182 | /// P: 1..=31 | 183 | /// P: 1..=31 |
| 183 | pub enum SpllMode { | 184 | pub enum SpllMode { |
| 184 | /// Fout = M x Fin | 185 | /// Fout = M x Fin |
| 185 | Mode1a { | 186 | Mode1a { m_mult: u16 }, |
| 186 | m_mult: u16, | ||
| 187 | }, | ||
| 188 | /// if !bypass_p2_div: Fout = (M / (2 x P)) x Fin | 187 | /// if !bypass_p2_div: Fout = (M / (2 x P)) x Fin |
| 189 | /// if bypass_p2_div: Fout = (M / P ) x Fin | 188 | /// if bypass_p2_div: Fout = (M / P ) x Fin |
| 190 | Mode1b { | 189 | Mode1b { |
| @@ -193,10 +192,7 @@ pub enum SpllMode { | |||
| 193 | bypass_p2_div: bool, | 192 | bypass_p2_div: bool, |
| 194 | }, | 193 | }, |
| 195 | /// Fout = (M / N) x Fin | 194 | /// Fout = (M / N) x Fin |
| 196 | Mode1c { | 195 | Mode1c { m_mult: u16, n_div: u8 }, |
| 197 | m_mult: u16, | ||
| 198 | n_div: u8, | ||
| 199 | }, | ||
| 200 | /// if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin | 196 | /// if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin |
| 201 | /// if bypass_p2_div: Fout = (M / ( N x P )) x Fin | 197 | /// if bypass_p2_div: Fout = (M / ( N x P )) x Fin |
| 202 | Mode1d { | 198 | Mode1d { |
| @@ -204,10 +200,9 @@ pub enum SpllMode { | |||
| 204 | n_div: u8, | 200 | n_div: u8, |
| 205 | p_div: u8, | 201 | p_div: u8, |
| 206 | bypass_p2_div: bool, | 202 | bypass_p2_div: bool, |
| 207 | } | 203 | }, |
| 208 | } | 204 | } |
| 209 | 205 | ||
| 210 | |||
| 211 | // FIRC/FRO180M | 206 | // FIRC/FRO180M |
| 212 | 207 | ||
| 213 | /// ```text | 208 | /// ```text |
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 98f7075eb..866e78bf2 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs | |||
| @@ -200,6 +200,9 @@ pub struct Clocks { | |||
| 200 | 200 | ||
| 201 | /// `pll1_clk` is the output of the main system PLL, `pll1`. | 201 | /// `pll1_clk` is the output of the main system PLL, `pll1`. |
| 202 | pub pll1_clk: Option<Clock>, | 202 | pub pll1_clk: Option<Clock>, |
| 203 | |||
| 204 | /// `pll1_clk_div` is a configurable frequency clock, sourced from `pll1_clk` | ||
| 205 | pub pll1_clk_div: Option<Clock>, | ||
| 203 | } | 206 | } |
| 204 | 207 | ||
| 205 | /// `ClockError` is the main error returned when configuring or checking clock state | 208 | /// `ClockError` is the main error returned when configuring or checking clock state |
| @@ -548,13 +551,37 @@ impl Clocks { | |||
| 548 | } | 551 | } |
| 549 | 552 | ||
| 550 | /// Ensure the `pll1_clk` clock is active and valid at the given power state. | 553 | /// Ensure the `pll1_clk` clock is active and valid at the given power state. |
| 551 | pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | 554 | pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { |
| 552 | Err(ClockError::NotImplemented { clock: "pll1_clk" }) | 555 | let Some(clk) = self.pll1_clk.as_ref() else { |
| 556 | return Err(ClockError::BadConfig { | ||
| 557 | clock: "spll", | ||
| 558 | reason: "required but not active", | ||
| 559 | }); | ||
| 560 | }; | ||
| 561 | if !clk.power.meets_requirement_of(at_level) { | ||
| 562 | return Err(ClockError::BadConfig { | ||
| 563 | clock: "spll", | ||
| 564 | reason: "not low power active", | ||
| 565 | }); | ||
| 566 | } | ||
| 567 | Ok(clk.frequency) | ||
| 553 | } | 568 | } |
| 554 | 569 | ||
| 555 | /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. | 570 | /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. |
| 556 | pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | 571 | pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { |
| 557 | Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) | 572 | let Some(clk) = self.pll1_clk_div.as_ref() else { |
| 573 | return Err(ClockError::BadConfig { | ||
| 574 | clock: "spll", | ||
| 575 | reason: "required but not active", | ||
| 576 | }); | ||
| 577 | }; | ||
| 578 | if !clk.power.meets_requirement_of(at_level) { | ||
| 579 | return Err(ClockError::BadConfig { | ||
| 580 | clock: "spll", | ||
| 581 | reason: "not low power active", | ||
| 582 | }); | ||
| 583 | } | ||
| 584 | Ok(clk.frequency) | ||
| 558 | } | 585 | } |
| 559 | 586 | ||
| 560 | /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active | 587 | /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active |
| @@ -1031,7 +1058,10 @@ impl ClockOperator<'_> { | |||
| 1031 | reason: s, | 1058 | reason: s, |
| 1032 | })?; | 1059 | })?; |
| 1033 | if !clk.power.meets_requirement_of(&cfg.power) { | 1060 | if !clk.power.meets_requirement_of(&cfg.power) { |
| 1034 | return Err(ClockError::BadConfig { clock: "spll", reason: "needs low power source" }); | 1061 | return Err(ClockError::BadConfig { |
| 1062 | clock: "spll", | ||
| 1063 | reason: "needs low power source", | ||
| 1064 | }); | ||
| 1035 | } | 1065 | } |
| 1036 | 1066 | ||
| 1037 | // Bandwidth calc | 1067 | // Bandwidth calc |
| @@ -1118,9 +1148,20 @@ impl ClockOperator<'_> { | |||
| 1118 | fout = (f_in / div).checked_mul(m_mult as u32); | 1148 | fout = (f_in / div).checked_mul(m_mult as u32); |
| 1119 | } | 1149 | } |
| 1120 | }; | 1150 | }; |
| 1151 | |||
| 1152 | defmt::debug!("f_in: {:?}", f_in); | ||
| 1153 | defmt::debug!("bp_pre: {:?}", bp_pre); | ||
| 1154 | defmt::debug!("bp_post: {:?}", bp_post); | ||
| 1155 | defmt::debug!("bp_post2: {:?}", bp_post2); | ||
| 1156 | defmt::debug!("m: {:?}", m); | ||
| 1157 | defmt::debug!("p: {:?}", p); | ||
| 1158 | defmt::debug!("n: {:?}", n); | ||
| 1159 | defmt::debug!("fout: {:?}", fout); | ||
| 1160 | defmt::debug!("fcco: {:?}", fcco); | ||
| 1161 | |||
| 1121 | let fcco = fcco.ok_or(ClockError::BadConfig { | 1162 | let fcco = fcco.ok_or(ClockError::BadConfig { |
| 1122 | clock: "spll", | 1163 | clock: "spll", |
| 1123 | reason: "fcco invalid", | 1164 | reason: "fcco invalid1", |
| 1124 | })?; | 1165 | })?; |
| 1125 | let fout = fout.ok_or(ClockError::BadConfig { | 1166 | let fout = fout.ok_or(ClockError::BadConfig { |
| 1126 | clock: "spll", | 1167 | clock: "spll", |
| @@ -1130,7 +1171,7 @@ impl ClockOperator<'_> { | |||
| 1130 | if !(275_000_000..=550_000_000).contains(&fcco) { | 1171 | if !(275_000_000..=550_000_000).contains(&fcco) { |
| 1131 | return Err(ClockError::BadConfig { | 1172 | return Err(ClockError::BadConfig { |
| 1132 | clock: "spll", | 1173 | clock: "spll", |
| 1133 | reason: "fcco invalid", | 1174 | reason: "fcco invalid2", |
| 1134 | }); | 1175 | }); |
| 1135 | } | 1176 | } |
| 1136 | // Fout: 4.3MHz to 2x Max CPU Frequency | 1177 | // Fout: 4.3MHz to 2x Max CPU Frequency |
| @@ -1149,6 +1190,7 @@ impl ClockOperator<'_> { | |||
| 1149 | assert!(m >= 1); | 1190 | assert!(m >= 1); |
| 1150 | if let Some(p) = p.as_ref() { | 1191 | if let Some(p) = p.as_ref() { |
| 1151 | assert!(*p >= 1); | 1192 | assert!(*p >= 1); |
| 1193 | assert!(*p <= 31); | ||
| 1152 | } | 1194 | } |
| 1153 | if let Some(n) = n.as_ref() { | 1195 | if let Some(n) = n.as_ref() { |
| 1154 | assert!(*n >= 1); | 1196 | assert!(*n >= 1); |
| @@ -1254,11 +1296,38 @@ impl ClockOperator<'_> { | |||
| 1254 | // Re-lock SPLL CSR | 1296 | // Re-lock SPLL CSR |
| 1255 | self.scg0.spllcsr().modify(|_r, w| w.lk().write_disabled()); | 1297 | self.scg0.spllcsr().modify(|_r, w| w.lk().write_disabled()); |
| 1256 | 1298 | ||
| 1299 | // Store clock state | ||
| 1257 | self.clocks.pll1_clk = Some(Clock { | 1300 | self.clocks.pll1_clk = Some(Clock { |
| 1258 | frequency: fout, | 1301 | frequency: fout, |
| 1259 | power: cfg.power, | 1302 | power: cfg.power, |
| 1260 | }); | 1303 | }); |
| 1261 | 1304 | ||
| 1305 | // Do we enable the `pll1_clk_div` output? | ||
| 1306 | if let Some(d) = cfg.pll1_clk_div.as_ref() { | ||
| 1307 | // Halt and reset the div; then set our desired div. | ||
| 1308 | self.syscon.pll1clkdiv().write(|w| { | ||
| 1309 | w.halt().halt(); | ||
| 1310 | w.reset().asserted(); | ||
| 1311 | unsafe { w.div().bits(d.into_bits()) }; | ||
| 1312 | w | ||
| 1313 | }); | ||
| 1314 | // Then unhalt it, and reset it | ||
| 1315 | self.syscon.pll1clkdiv().write(|w| { | ||
| 1316 | w.halt().run(); | ||
| 1317 | w.reset().released(); | ||
| 1318 | w | ||
| 1319 | }); | ||
| 1320 | |||
| 1321 | // Wait for clock to stabilize | ||
| 1322 | while self.syscon.pll1clkdiv().read().unstab().is_ongoing() {} | ||
| 1323 | |||
| 1324 | // Store off the clock info | ||
| 1325 | self.clocks.pll1_clk_div = Some(Clock { | ||
| 1326 | frequency: fout / d.into_divisor(), | ||
| 1327 | power: cfg.power, | ||
| 1328 | }); | ||
| 1329 | } | ||
| 1330 | |||
| 1262 | Ok(()) | 1331 | Ok(()) |
| 1263 | } | 1332 | } |
| 1264 | } | 1333 | } |
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index e6e6a2d3d..94f7d7bbf 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; | 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; |
| 6 | use embassy_mcxa::clocks::PoweredClock; | 6 | use embassy_mcxa::clocks::PoweredClock; |
| 7 | use embassy_mcxa::clocks::config::{SoscConfig, SoscMode}; | 7 | use embassy_mcxa::clocks::config::{SoscConfig, SoscMode, SpllConfig, SpllMode, SpllSource}; |
| 8 | use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; | 8 | use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; |
| 9 | use embassy_time::Timer; | 9 | use embassy_time::Timer; |
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -18,6 +18,18 @@ async fn main(_spawner: Spawner) { | |||
| 18 | frequency: 8_000_000, | 18 | frequency: 8_000_000, |
| 19 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | 19 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 20 | }); | 20 | }); |
| 21 | cfg.clock_cfg.spll = Some(SpllConfig { | ||
| 22 | source: SpllSource::Sirc, // 12MHz | ||
| 23 | // 12 x 32 => 384MHz | ||
| 24 | // 384 / (16 x 2) => 12.0MHz | ||
| 25 | mode: SpllMode::Mode1b { | ||
| 26 | m_mult: 32, | ||
| 27 | p_div: 16, | ||
| 28 | bypass_p2_div: false, | ||
| 29 | }, | ||
| 30 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 31 | pll1_clk_div: None, | ||
| 32 | }); | ||
| 21 | 33 | ||
| 22 | let p = hal::init(cfg); | 34 | let p = hal::init(cfg); |
| 23 | 35 | ||
| @@ -39,11 +51,18 @@ async fn main(_spawner: Spawner) { | |||
| 39 | div: const { Div4::from_divisor(16).unwrap() }, | 51 | div: const { Div4::from_divisor(16).unwrap() }, |
| 40 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | 52 | level: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 41 | }; | 53 | }; |
| 54 | const M1_CONFIG: Config = Config { | ||
| 55 | sel: ClockOutSel::Pll1Clk, | ||
| 56 | div: const { Div4::from_divisor(12).unwrap() }, | ||
| 57 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 58 | }; | ||
| 42 | 59 | ||
| 60 | #[rustfmt::skip] | ||
| 43 | let configs = [ | 61 | let configs = [ |
| 44 | ("16K -> /1 = 16K", K16_CONFIG), | 62 | ("16K -> /1 = 16K", K16_CONFIG), |
| 45 | ("12M -> /3 = 4M", M4_CONFIG), | 63 | ("12M -> /3 = 4M", M4_CONFIG), |
| 46 | ("8M -> /16 = 512K", K512_CONFIG), | 64 | ("8M -> /16 = 512K", K512_CONFIG), |
| 65 | ("12M-> /12 = 1M", M1_CONFIG), | ||
| 47 | ]; | 66 | ]; |
| 48 | 67 | ||
| 49 | loop { | 68 | loop { |
