aboutsummaryrefslogtreecommitdiff
path: root/src/clocks
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-11-14 14:39:23 +0100
committerJames Munns <[email protected]>2025-11-14 14:39:23 +0100
commit4979747286af4cd7bab43f8048b0ce6f00cc5ab6 (patch)
tree188116a3254e45e70acdecc6cd4e3f05a9efd888 /src/clocks
parent0f8e7650b937aa5d4accef3fdf01047afe099df3 (diff)
Plumb clocks changes to lpuart
Diffstat (limited to 'src/clocks')
-rw-r--r--src/clocks/mod.rs301
-rw-r--r--src/clocks/periph_helpers.rs3
2 files changed, 231 insertions, 73 deletions
diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs
index ad48a9cf2..63a8fb64d 100644
--- a/src/clocks/mod.rs
+++ b/src/clocks/mod.rs
@@ -1,66 +1,157 @@
1//! Clock control helpers (no magic numbers, PAC field access only). 1//! Clock control helpers (no magic numbers, PAC field access only).
2//! Provides reusable gate abstractions for peripherals used by the examples. 2//! Provides reusable gate abstractions for peripherals used by the examples.
3use core::cell::RefCell;
4
3use mcxa_pac::scg0::{ 5use mcxa_pac::scg0::{
4 firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}, 6 firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten},
5 sirccsr::{SircClkPeriphEn, Sircsten}, 7 sirccsr::{SircClkPeriphEn, Sircsten},
6}; 8};
9use periph_helpers::SPConfHelper;
7 10
8use crate::pac; 11use crate::pac;
9pub mod periph_helpers; 12pub mod periph_helpers;
10 13
11// /// Trait describing an AHB clock gate that can be toggled through MRCC. 14/// Trait describing an AHB clock gate that can be toggled through MRCC.
12// pub trait Gate { 15pub trait Gate {
13// /// Enable the clock gate. 16 type MrccPeriphConfig: SPConfHelper;
14// unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock);
15 17
16// /// Return whether the clock gate is currently enabled. 18 /// Enable the clock gate.
17// fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool; 19 unsafe fn enable_clock();
18// }
19 20
20// /// Enable a clock gate for the given peripheral set. 21 /// Disable the clock gate.
21// #[inline] 22 unsafe fn disable_clock();
22// pub unsafe fn enable<G: Gate>(peripherals: &pac::Peripherals) {
23// let mrcc = &peripherals.mrcc0;
24// G::enable(mrcc);
25// while !G::is_enabled(mrcc) {}
26// core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
27// }
28 23
29// /// Check whether a gate is currently enabled. 24 /// Drive the peripheral into reset.
30// #[inline] 25 unsafe fn assert_reset();
31// pub fn is_enabled<G: Gate>(peripherals: &pac::Peripherals) -> bool {
32// G::is_enabled(&peripherals.mrcc0)
33// }
34 26
35// macro_rules! impl_cc_gate { 27 /// Drive the peripheral out of reset.
36// ($name:ident, $reg:ident, $field:ident) => { 28 unsafe fn release_reset();
37// pub struct $name;
38
39// impl Gate for $name {
40// #[inline]
41// unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock) {
42// mrcc.$reg().modify(|_, w| w.$field().enabled());
43// }
44
45// #[inline]
46// fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
47// mrcc.$reg().read().$field().is_enabled()
48// }
49// }
50// };
51// }
52 29
53// pub mod gate { 30 /// Return whether the clock gate is currently enabled.
54// use super::*; 31 fn is_clock_enabled() -> bool;
55 32
56// impl_cc_gate!(Port2, mrcc_glb_cc1, port2); 33 /// .
57// impl_cc_gate!(Port3, mrcc_glb_cc1, port3); 34 fn is_reset_released() -> bool;
58// impl_cc_gate!(Ostimer0, mrcc_glb_cc1, ostimer0); 35}
59// impl_cc_gate!(Lpuart2, mrcc_glb_cc0, lpuart2); 36
60// impl_cc_gate!(Gpio3, mrcc_glb_cc2, gpio3); 37#[inline]
61// impl_cc_gate!(Port1, mrcc_glb_cc1, port1); 38pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
62// impl_cc_gate!(Adc1, mrcc_glb_cc1, adc1); 39 let freq = enable::<G>(cfg)?;
63// } 40 pulse_reset::<G>();
41 Ok(freq)
42}
43
44/// Enable a clock gate for the given peripheral set.
45#[inline]
46pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
47 G::enable_clock();
48 while !G::is_clock_enabled() {}
49 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
50
51 let freq = critical_section::with(|cs| {
52 let clocks = CLOCKS.borrow_ref(cs);
53 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
54 cfg.post_enable_config(clocks)
55 });
56
57 freq.inspect_err(|_e| {
58 G::disable_clock();
59 })
60}
61
62pub unsafe fn disable<G: Gate>() {
63 G::disable_clock();
64}
65
66/// Check whether a gate is currently enabled.
67#[inline]
68pub fn is_clock_enabled<G: Gate>() -> bool {
69 G::is_clock_enabled()
70}
71
72/// Release a reset line for the given peripheral set.
73#[inline]
74pub unsafe fn release_reset<G: Gate>() {
75 G::release_reset();
76}
77
78/// Assert a reset line for the given peripheral set.
79#[inline]
80pub unsafe fn assert_reset<G: Gate>() {
81 G::assert_reset();
82}
83
84/// Pulse a reset line (assert then release) with a short delay.
85#[inline]
86pub unsafe fn pulse_reset<G: Gate>() {
87 G::assert_reset();
88 cortex_m::asm::nop();
89 cortex_m::asm::nop();
90 G::release_reset();
91}
92
93macro_rules! impl_cc_gate {
94 ($name:ident, $reg:ident, $field:ident, $config:ty) => {
95 impl Gate for crate::peripherals::$name {
96 type MrccPeriphConfig = $config;
97
98 #[inline]
99 unsafe fn enable_clock() {
100 let mrcc = unsafe { pac::Mrcc0::steal() };
101 mrcc.$reg().modify(|_, w| w.$field().enabled());
102 }
103
104 #[inline]
105 unsafe fn disable_clock() {
106 let mrcc = unsafe { pac::Mrcc0::steal() };
107 mrcc.$reg().modify(|_r, w| w.$field().disabled());
108 }
109
110 #[inline]
111 fn is_clock_enabled() -> bool {
112 let mrcc = unsafe { pac::Mrcc0::steal() };
113 mrcc.$reg().read().$field().is_enabled()
114 }
115
116 #[inline]
117 unsafe fn release_reset() {
118 let mrcc = unsafe { pac::Mrcc0::steal() };
119 mrcc.$reg().modify(|_, w| w.$field().enabled());
120 }
121
122 #[inline]
123 unsafe fn assert_reset() {
124 let mrcc = unsafe { pac::Mrcc0::steal() };
125 mrcc.$reg().modify(|_, w| w.$field().disabled());
126 }
127
128 #[inline]
129 fn is_reset_released() -> bool {
130 let mrcc = unsafe { pac::Mrcc0::steal() };
131 mrcc.$reg().read().$field().is_enabled()
132 }
133 }
134 };
135}
136
137pub struct UnimplementedConfig;
138impl SPConfHelper for UnimplementedConfig {
139 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
140 Err(ClockError::UnimplementedConfig)
141 }
142}
143
144pub mod gate {
145 use super::{periph_helpers::LpuartConfig, *};
146
147 impl_cc_gate!(PORT1, mrcc_glb_cc1, port1, UnimplementedConfig);
148 impl_cc_gate!(PORT2, mrcc_glb_cc1, port2, UnimplementedConfig);
149 impl_cc_gate!(PORT3, mrcc_glb_cc1, port3, UnimplementedConfig);
150 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, ostimer0, UnimplementedConfig);
151 impl_cc_gate!(LPUART2, mrcc_glb_cc0, lpuart2, LpuartConfig);
152 impl_cc_gate!(GPIO3, mrcc_glb_cc2, gpio3, UnimplementedConfig);
153 impl_cc_gate!(ADC1, mrcc_glb_cc1, adc1, UnimplementedConfig);
154}
64 155
65// /// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART. 156// /// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART.
66// pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) { 157// pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) {
@@ -113,20 +204,6 @@ pub mod periph_helpers;
113// mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); 204// mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
114// } 205// }
115 206
116// pub unsafe fn init_fro16k(peripherals: &pac::Peripherals) {
117// let vbat = &peripherals.vbat0;
118// // Enable FRO16K oscillator
119// vbat.froctla().modify(|_, w| w.fro_en().set_bit());
120
121// // Lock the control register
122// vbat.frolcka().modify(|_, w| w.lock().set_bit());
123
124// // Enable clock outputs to both VSYS and VDD_CORE domains
125// // Bit 0: clk_16k0 to VSYS domain
126// // Bit 1: clk_16k1 to VDD_CORE domain
127// vbat.froclke().modify(|_, w| unsafe { w.clke().bits(0x3) });
128// }
129
130// pub unsafe fn enable_adc(peripherals: &pac::Peripherals) { 207// pub unsafe fn enable_adc(peripherals: &pac::Peripherals) {
131// enable::<gate::Port1>(peripherals); 208// enable::<gate::Port1>(peripherals);
132// enable::<gate::Adc1>(peripherals); 209// enable::<gate::Adc1>(peripherals);
@@ -271,6 +348,7 @@ pub struct ClocksConfig {
271 pub firc: Option<FircConfig>, 348 pub firc: Option<FircConfig>,
272 // NOTE: I don't think we *can* disable the SIRC? 349 // NOTE: I don't think we *can* disable the SIRC?
273 pub sirc: SircConfig, 350 pub sirc: SircConfig,
351 pub fro16k: Option<Fro16KConfig>,
274} 352}
275 353
276// FIRC/FRO180M 354// FIRC/FRO180M
@@ -340,17 +418,30 @@ pub struct Clocks {
340 pub fro_lf_div: Option<Clock>, 418 pub fro_lf_div: Option<Clock>,
341 // 419 //
342 // End FRO12M stuff 420 // End FRO12M stuff
421
422 pub clk_16k_vsys: Option<Clock>,
423 pub clk_16k_vdd_core: Option<Clock>,
343 pub main_clk: Option<Clock>, 424 pub main_clk: Option<Clock>,
344 pub pll1_clk: Option<Clock>, 425 pub pll1_clk: Option<Clock>,
345} 426}
346 427
347static CLOCKS: critical_section::Mutex<Option<Clocks>> = critical_section::Mutex::new(None); 428#[non_exhaustive]
429pub struct Fro16KConfig {
430 pub vsys_domain_active: bool,
431 pub vdd_core_domain_active: bool,
432}
433
434static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
348 435
436#[derive(Debug, Copy, Clone, Eq, PartialEq)]
437#[cfg_attr(feature = "defmt", derive(defmt::Format))]
349#[non_exhaustive] 438#[non_exhaustive]
350pub enum ClockError { 439pub enum ClockError {
440 NeverInitialized,
351 AlreadyInitialized, 441 AlreadyInitialized,
352 BadConfig { clock: &'static str, reason: &'static str }, 442 BadConfig { clock: &'static str, reason: &'static str },
353 NotImplemented { clock: &'static str }, 443 NotImplemented { clock: &'static str },
444 UnimplementedConfig,
354} 445}
355 446
356struct ClockOperator<'a> { 447struct ClockOperator<'a> {
@@ -360,6 +451,7 @@ struct ClockOperator<'a> {
360 _mrcc0: pac::Mrcc0, 451 _mrcc0: pac::Mrcc0,
361 scg0: pac::Scg0, 452 scg0: pac::Scg0,
362 syscon: pac::Syscon, 453 syscon: pac::Syscon,
454 vbat0: pac::Vbat0,
363} 455}
364 456
365impl ClockOperator<'_> { 457impl ClockOperator<'_> {
@@ -586,13 +678,52 @@ impl ClockOperator<'_> {
586 }); 678 });
587 } 679 }
588 680
589 todo!() 681 Ok(())
682 }
683
684 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
685 let Some(fro16k) = self.config.fro16k.as_ref() else {
686 return Ok(());
687 };
688 // Enable FRO16K oscillator
689 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
690
691 // Lock the control register
692 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
693
694 let Fro16KConfig { vsys_domain_active, vdd_core_domain_active } = fro16k;
695
696 // Enable clock outputs to both VSYS and VDD_CORE domains
697 // Bit 0: clk_16k0 to VSYS domain
698 // Bit 1: clk_16k1 to VDD_CORE domain
699 //
700 // TODO: Define sub-fields for this register with a PAC patch?
701 let mut bits = 0;
702 if *vsys_domain_active {
703 bits |= 0b01;
704 self.clocks.clk_16k_vsys = Some(Clock {
705 frequency: 16_384,
706 power: PoweredClock::AlwaysEnabled,
707 });
708 }
709 if *vdd_core_domain_active {
710 bits |= 0b10;
711 self.clocks.clk_16k_vdd_core = Some(Clock {
712 frequency: 16_384,
713 power: PoweredClock::AlwaysEnabled,
714 });
715 }
716 self.vbat0.froclke().modify(|_r, w| {
717 unsafe { w.clke().bits(bits) }
718 });
719
720 Ok(())
590 } 721 }
591} 722}
592 723
593pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { 724pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
594 critical_section::with(|cs| { 725 critical_section::with(|cs| {
595 if CLOCKS.borrow(cs).is_some() { 726 if CLOCKS.borrow_ref(cs).is_some() {
596 Err(ClockError::AlreadyInitialized) 727 Err(ClockError::AlreadyInitialized)
597 } else { 728 } else {
598 Ok(()) 729 Ok(())
@@ -607,12 +738,20 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
607 _mrcc0: unsafe { pac::Mrcc0::steal() }, 738 _mrcc0: unsafe { pac::Mrcc0::steal() },
608 scg0: unsafe { pac::Scg0::steal() }, 739 scg0: unsafe { pac::Scg0::steal() },
609 syscon: unsafe { pac::Syscon::steal() }, 740 syscon: unsafe { pac::Syscon::steal() },
741 vbat0: unsafe { pac::Vbat0::steal() },
610 }; 742 };
611 743
612 operator.configure_firc_clocks()?; 744 operator.configure_firc_clocks()?;
613 operator.configure_sirc_clocks()?; 745 operator.configure_sirc_clocks()?;
746 operator.configure_fro16k_clocks()?;
614 // TODO, everything downstream 747 // TODO, everything downstream
615 748
749 critical_section::with(|cs| {
750 let mut clks = CLOCKS.borrow_ref_mut(cs);
751 assert!(clks.is_none(), "Clock setup race!");
752 *clks = Some(clocks);
753 });
754
616 Ok(()) 755 Ok(())
617} 756}
618 757
@@ -621,7 +760,8 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
621/// NOTE: Clocks implements `Clone`, 760/// NOTE: Clocks implements `Clone`,
622pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> { 761pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
623 critical_section::with(|cs| { 762 critical_section::with(|cs| {
624 let c = CLOCKS.borrow(cs).as_ref()?; 763 let c = CLOCKS.borrow_ref(cs);
764 let c = c.as_ref()?;
625 Some(f(c)) 765 Some(f(c))
626 }) 766 })
627} 767}
@@ -629,20 +769,32 @@ pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
629impl Clocks { 769impl Clocks {
630 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 770 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
631 let Some(clk) = self.fro_lf_div.as_ref() else { 771 let Some(clk) = self.fro_lf_div.as_ref() else {
632 return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "required but not active" }); 772 return Err(ClockError::BadConfig {
773 clock: "fro_lf_div",
774 reason: "required but not active",
775 });
633 }; 776 };
634 if !clk.power.meets_requirement_of(at_level) { 777 if !clk.power.meets_requirement_of(at_level) {
635 return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "not low power active" }); 778 return Err(ClockError::BadConfig {
779 clock: "fro_lf_div",
780 reason: "not low power active",
781 });
636 } 782 }
637 Ok(clk.frequency) 783 Ok(clk.frequency)
638 } 784 }
639 785
640 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 786 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
641 let Some(clk) = self.fro_hf_div.as_ref() else { 787 let Some(clk) = self.fro_hf_div.as_ref() else {
642 return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "required but not active" }); 788 return Err(ClockError::BadConfig {
789 clock: "fro_hf_div",
790 reason: "required but not active",
791 });
643 }; 792 };
644 if !clk.power.meets_requirement_of(at_level) { 793 if !clk.power.meets_requirement_of(at_level) {
645 return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "not low power active" }); 794 return Err(ClockError::BadConfig {
795 clock: "fro_hf_div",
796 reason: "not low power active",
797 });
646 } 798 }
647 Ok(clk.frequency) 799 Ok(clk.frequency)
648 } 800 }
@@ -657,10 +809,16 @@ impl Clocks {
657 809
658 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 810 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
659 let Some(clk) = self.clk_1m.as_ref() else { 811 let Some(clk) = self.clk_1m.as_ref() else {
660 return Err(ClockError::BadConfig { clock: "clk_1m", reason: "required but not active" }); 812 return Err(ClockError::BadConfig {
813 clock: "clk_1m",
814 reason: "required but not active",
815 });
661 }; 816 };
662 if !clk.power.meets_requirement_of(at_level) { 817 if !clk.power.meets_requirement_of(at_level) {
663 return Err(ClockError::BadConfig { clock: "clk_1m", reason: "not low power active" }); 818 return Err(ClockError::BadConfig {
819 clock: "clk_1m",
820 reason: "not low power active",
821 });
664 } 822 }
665 Ok(clk.frequency) 823 Ok(clk.frequency)
666 } 824 }
@@ -668,5 +826,4 @@ impl Clocks {
668 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 826 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
669 Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) 827 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
670 } 828 }
671
672} 829}
diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs
index c4d4e9e3b..4d186acf8 100644
--- a/src/clocks/periph_helpers.rs
+++ b/src/clocks/periph_helpers.rs
@@ -7,6 +7,7 @@ pub trait SPConfHelper {
7 7
8// config types 8// config types
9 9
10#[derive(Debug, Clone, Copy)]
10pub enum LpuartClockSel { 11pub enum LpuartClockSel {
11 /// FRO12M/FRO_LF/SIRC clock source, passed through divider 12 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
12 /// "fro_lf_div" 13 /// "fro_lf_div"
@@ -45,7 +46,7 @@ pub struct LpuartConfig {
45 pub div: Div8, 46 pub div: Div8,
46 /// Which instance is this? 47 /// Which instance is this?
47 // NOTE: should not be user settable 48 // NOTE: should not be user settable
48 instance: LpuartInstance, 49 pub(crate) instance: LpuartInstance,
49} 50}
50 51
51// impls 52// impls