mk20d7_hal/
mcg.rs

1use core::convert::TryFrom;
2
3use mk20d7::{mcg::RegisterBlock, mcg::c1};
4
5use sim::MAXIMUM_CLOCK_FREQUENCY;
6use bitrate::{U32BitrateExt, KiloHertz, MegaHertz};
7
8pub const FLL_RANGE_MIN: f32 = 31.25;
9pub const FLL_RANGE_MAX: f32 = 39.0625;
10
11pub const PLL_DIVIDER_NUMERATOR_MIN: u8 = 24;
12pub const PLL_DIVIDER_NUMERATOR_MAX: u8 = 55;
13pub const PLL_DIVIDER_DENOMINATOR_MIN: u8 = 1;
14pub const PLL_DIVIDER_DENOMINATOR_MAX: u8 = 25;
15
16pub struct MultipurposeClockGenerator<'a> {
17    mcg: &'a RegisterBlock,
18    pub external_crystal_frequency: MegaHertz<u32>,
19}
20
21pub struct Fei<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
22#[allow(dead_code)] pub struct Fee<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
23#[allow(dead_code)] pub struct Fbi<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
24pub struct Fbe<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
25pub struct Pee<'a> { #[allow(dead_code)] mcg: &'a mut MultipurposeClockGenerator<'a> }
26pub struct Pbe<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
27#[allow(dead_code)] pub struct Blpi<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
28#[allow(dead_code)] pub struct Blpe<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
29#[allow(dead_code)] pub struct Stop<'a> { mcg: &'a mut MultipurposeClockGenerator<'a> }
30
31// Multipurpose Clock Generator (MCG) modes of operation
32pub enum ClockMode<'a> {
33    Fei(Fei<'a>), // FEI: Frequency Locked Loop (FLL) Engaged Internal
34    Fee(Fee<'a>), // FEE: Frequency Locked Loop (FLL) Engaged External
35    Fbi(Fbi<'a>), // FBI: Frequency Locked Loop (FLL) Bypassed Internal
36    Fbe(Fbe<'a>), // FBE: Frequency Locked Loop (FLL) Bypassed External
37    Pee(Pee<'a>), // PEE: Phase Locked Loop (PLL) Engaged External
38    Pbe(Pbe<'a>), // PBE: Phase Locked Loop (PLL) Bypassed External
39    Blpi(Blpi<'a>), // BLPI: Bypassed Low Power Internal
40    Blpe(Blpe<'a>), // BLPE: Bypassed Low Power External
41    Stop(Stop<'a>), // Stop
42}
43
44impl<'a> MultipurposeClockGenerator<'a> {
45    pub fn new(mcg: &'a RegisterBlock, external_crystal_frequency: MegaHertz<u32>) -> MultipurposeClockGenerator<'a> {
46        MultipurposeClockGenerator { mcg, external_crystal_frequency }
47    }
48
49    pub fn clock_mode(&'a mut self) -> ClockMode<'a> {
50        let clock_source = self.mcg.c1.read().clks();
51        let internal_clock_reference = self.mcg.c1.read().irefs().bit_is_set();
52        let pll_enabled = self.mcg.c6.read().plls().bit_is_set();
53        let low_power_enabled = self.mcg.c2.read().lp().bit_is_set();
54
55        let external_crystal_frequency_khz: KiloHertz<u32> = self.external_crystal_frequency.into();
56        let fll = external_crystal_frequency_khz.0 as f32 / f32::from(self.get_external_crystal_frequency_divider());
57        let fll_range_ok = fll >= FLL_RANGE_MIN && fll <= FLL_RANGE_MAX;
58
59        let mcg = self;
60        match (clock_source, internal_clock_reference, pll_enabled, low_power_enabled, fll_range_ok) {
61            (c1::CLKSR::_00, true, false, _, _) => ClockMode::Fei(Fei { mcg }),
62            (c1::CLKSR::_00, false, false, _, true) => ClockMode::Fee(Fee { mcg }),
63            (c1::CLKSR::_01, true, false, false, _) => ClockMode::Fbi(Fbi { mcg }),
64            (c1::CLKSR::_10, false, false, false, true) => ClockMode::Fbe(Fbe { mcg }),
65            (c1::CLKSR::_00, false, true, _, _) => ClockMode::Pee(Pee { mcg }),
66            (c1::CLKSR::_10, false, true, false, _) => ClockMode::Pbe(Pbe { mcg }),
67            (c1::CLKSR::_01, true, false, true, _) => ClockMode::Blpi(Blpi { mcg }),
68            (c1::CLKSR::_10, false, _, true, _) => ClockMode::Blpe(Blpe { mcg }),
69            _ => panic!("The current clock mode cannot be represented as a known struct"),
70        }
71    }
72
73    pub fn external_crystal_is_requested(&self) -> bool {
74        self.mcg.c2.read().erefs0().bit_is_set()
75    }
76
77    pub fn enable_external_crystal_request(&mut self) {
78        if self.external_crystal_is_requested() { return; }
79        self.mcg.c2.write(|w| w.erefs0().set_bit());
80        while self.mcg.s.read().oscinit0().bit_is_clear() {} // Wait to become enabled
81    }
82
83    pub fn disable_external_crystal_request(&mut self) {
84        if !self.external_crystal_is_requested() { return; }
85        self.mcg.c2.write(|w| w.erefs0().clear_bit());
86        while self.mcg.s.read().oscinit0().bit_is_set() {} // Wait to become disabled
87    }
88
89    pub fn set_external_crystal_frequency_range_low(&mut self) {
90        self.mcg.c2.write(|w| w.range0()._00());
91    }
92
93    pub fn set_external_crystal_frequency_range_high(&mut self) {
94        self.mcg.c2.write(|w| w.range0()._01());
95    }
96
97    pub fn set_external_crystal_frequency_divider(&self, divider: u16) {
98        let crystal_low_frequency = self.mcg.c2.read().range0().is_00();
99        let real_time_clock = self.mcg.c7.read().oscsel().bit_is_set();
100        let rtc_or_low_freq_crystal = crystal_low_frequency || real_time_clock;
101
102        self.mcg.c1.write(
103            |w| {
104                let frdiv_w = w.frdiv();
105                match divider {
106                    _ if rtc_or_low_freq_crystal && divider == 1 || divider == 32 => frdiv_w._000(),
107                    _ if rtc_or_low_freq_crystal && divider == 2 || divider == 64 => frdiv_w._001(),
108                    _ if rtc_or_low_freq_crystal && divider == 4 || divider == 128 => frdiv_w._010(),
109                    _ if rtc_or_low_freq_crystal && divider == 8 || divider == 256 => frdiv_w._011(),
110                    _ if rtc_or_low_freq_crystal && divider == 16 || divider == 512 => frdiv_w._100(),
111                    _ if rtc_or_low_freq_crystal && divider == 32 || divider == 1024 => frdiv_w._101(),
112                    _ if rtc_or_low_freq_crystal && divider == 64 || divider == 1280 => frdiv_w._110(),
113                    _ if rtc_or_low_freq_crystal && divider == 128 || divider == 1536 => frdiv_w._111(),
114                    _ => panic!("Invalid external clock divider: {}", divider),
115                }
116            }
117        );
118    }
119
120    pub fn get_external_crystal_frequency_divider(&self) -> u16 {
121        let crystal_low_frequency = self.mcg.c2.read().range0().is_00();
122        let real_time_clock = self.mcg.c7.read().oscsel().bit_is_set();
123        let rtc_or_low_freq_crystal = crystal_low_frequency || real_time_clock;
124
125        match self.mcg.c1.read().frdiv() {
126            c1::FRDIVR::_000 => if rtc_or_low_freq_crystal { 1 } else { 32 },
127            c1::FRDIVR::_001 => if rtc_or_low_freq_crystal { 2 } else { 64 },
128            c1::FRDIVR::_010 => if rtc_or_low_freq_crystal { 4 } else { 128 },
129            c1::FRDIVR::_011 => if rtc_or_low_freq_crystal { 8 } else { 256 },
130            c1::FRDIVR::_100 => if rtc_or_low_freq_crystal { 16 } else { 512 },
131            c1::FRDIVR::_101 => if rtc_or_low_freq_crystal { 32 } else { 1024 },
132            c1::FRDIVR::_110 => if rtc_or_low_freq_crystal { 64 } else { 1280 },
133            c1::FRDIVR::_111 => if rtc_or_low_freq_crystal { 128 } else { 1536 },
134        }
135    }
136
137    pub fn use_external_crystal(&mut self) {
138        self.mcg.c1.write(
139            |w| {
140                w.clks()._10();
141                w.irefs().clear_bit()
142            }
143        );
144
145        // Once we write to the control register, we need to wait for
146        // the new clock to stabilize before we move on.
147        while self.mcg.s.read().irefst().bit_is_set() {} // Wait for FLL to point to the crystal
148        while !self.mcg.s.read().clkst().is_10() {} // Wait for clock source to be the crystal osc
149    }
150
151    pub fn set_pll_frequency_divider(&mut self, numerator: u8, denominator: u8) {
152        if numerator < PLL_DIVIDER_NUMERATOR_MIN || numerator > PLL_DIVIDER_NUMERATOR_MAX {
153            panic!("Invalid PLL VCO divide factor: {}", numerator);
154        }
155
156        if denominator < PLL_DIVIDER_DENOMINATOR_MIN || denominator > PLL_DIVIDER_DENOMINATOR_MAX {
157            panic!("Invalid PLL reference divide factor: {}", denominator);
158        }
159
160        self.mcg.c5.write(|w| unsafe { w.prdiv0().bits(denominator - PLL_DIVIDER_DENOMINATOR_MIN) });
161        self.mcg.c6.write(|w| unsafe { w.vdiv0().bits(numerator - PLL_DIVIDER_NUMERATOR_MIN) });
162    }
163
164    pub fn get_pll_frequency_divider(&self) -> (u8, u8) {
165        let numerator = self.mcg.c6.read().vdiv0().bits() + PLL_DIVIDER_NUMERATOR_MIN;
166        let denominator = self.mcg.c5.read().prdiv0().bits() + PLL_DIVIDER_DENOMINATOR_MIN;
167        (numerator, denominator)
168    }
169
170    pub fn set_pll_frequency(&mut self, frequency: MegaHertz<u32>) {
171        let divider = pll_frequency_divider_gcd(
172            u8::try_from(frequency.0).unwrap(),
173            u8::try_from(self.external_crystal_frequency.0).unwrap()
174        );
175        self.set_pll_frequency_divider(divider.0, divider.1);
176    }
177
178    pub fn get_pll_frequency(&self) -> MegaHertz<u32> {
179        let (numerator, denominator) = self.get_pll_frequency_divider();
180        let num = u32::from(numerator);
181        let den = u32::from(denominator);
182        ((num * self.external_crystal_frequency.0) / den).mhz()
183    }
184
185    pub fn enable_pll(&mut self) {
186        self.mcg.c6.write(|w| w.plls().set_bit());
187        while self.mcg.s.read().pllst().bit_is_clear() {} // Wait for PLL to be enabled
188        while self.mcg.s.read().lock0().bit_is_clear() {} // Wait for PLL to be "locked" and stable
189    }
190
191    pub fn use_pll(&mut self) {
192        self.mcg.c1.write(|w| w.clks()._10());
193
194        // mcg.c1 and mcg.s have slightly different behaviors. In c1, we use one value to indicate
195        // "Use whichever LL is enabled". In s, it is differentiated between the FLL at 0, and the
196        // PLL at 3. Instead of adding a value to OscSource which would be invalid to set, we just
197        // check for the known value "3" here.
198        while !self.mcg.s.read().clkst().is_10() {}
199    }
200}
201
202impl<'a> Into<Fbe<'a>> for Fei<'a> {
203    fn into(self) -> Fbe<'a> {
204        self.mcg.set_external_crystal_frequency_range_high();
205        self.mcg.enable_external_crystal_request();
206        self.mcg.set_external_crystal_frequency_divider(512); // FIXME: Assumes a 16 Mhz crystal, don't hard code this
207        self.mcg.use_external_crystal();
208        match self.mcg.clock_mode() {
209            ClockMode::Fbe(fbe) => fbe,
210            _ => panic!("Somehow the clock wasn't in FBE mode"),
211        }
212    }
213}
214
215impl<'a> Into<Pbe<'a>> for Fbe<'a> {
216    fn into(self) -> Pbe<'a> {
217        self.mcg.set_pll_frequency(u32::from(MAXIMUM_CLOCK_FREQUENCY).mhz()); // FIXME: Assumes 72 Mhz, don't hard code this
218        self.mcg.enable_pll();
219        match self.mcg.clock_mode() {
220            ClockMode::Pbe(pbe) => pbe,
221            _ => panic!("Somehow the clock wasn't in PBE mode"),
222        }
223    }
224}
225
226impl<'a> Into<Pee<'a>> for Pbe<'a> {
227    fn into(self) -> Pee<'a> {
228        self.mcg.use_pll();
229        match self.mcg.clock_mode() {
230            ClockMode::Pee(pee) => pee,
231            _ => panic!("Somehow the clock wasn't in PEE mode"),
232        }
233    }
234}
235
236fn pll_frequency_divider_gcd(numerator: u8, denominator: u8) -> (u8, u8) {
237    // Euclid's GCD
238    let mut num = numerator;
239    let mut den = denominator;
240    while den != 0 {
241        let temp = den;
242        den = num % den;
243        num = temp;
244    }
245    let gcd = num;
246    num = numerator / gcd;
247    den = denominator / gcd;
248
249    // GCD too high or too low, not a valid PLL frequency
250    if num == 0 || den == 0 || num > PLL_DIVIDER_NUMERATOR_MAX || den > PLL_DIVIDER_DENOMINATOR_MAX {
251        panic!("Cannot find a GCD for PLL frequency divider {}/{}.", numerator, denominator);
252    }
253
254    // GCD too low, coerce into an acceptable range
255    let mut freq_num = num;
256    let mut freq_den = den;
257    let mut mul = 1;
258    while freq_num < PLL_DIVIDER_NUMERATOR_MIN || freq_den < PLL_DIVIDER_DENOMINATOR_MIN {
259        mul += 1;
260        match (num.checked_mul(mul), den.checked_mul(mul)) {
261            (Some(new_freq_num), Some(new_freq_den)) if
262            new_freq_num <= PLL_DIVIDER_NUMERATOR_MAX &&
263            new_freq_den <= PLL_DIVIDER_DENOMINATOR_MAX => {
264                freq_num = new_freq_num;
265                freq_den = new_freq_den;
266            },
267            _ => panic!("Cannot find a GCD for PLL frequency divider {}/{}.", numerator, denominator),
268        }
269    }
270
271    (freq_num, freq_den)
272}