use core::{cmp::min, convert::TryFrom};
use embedded_time::rate::Extensions;
use crate::typestates::{
main_clock::MainClock,
ClocksSupport1MhzFroToken,
ClocksSupport32KhzFroToken,
ClocksSupportFlexcommToken,
ClocksSupportTouchToken,
ClocksSupportUsbfsToken,
ClocksSupportUsbhsToken,
ClocksSupportUtickToken,
};
use crate::{
peripherals::{anactrl::Anactrl, pmc::Pmc, syscon::Syscon},
time::{Hertz, Megahertz},
};
const MIN_USBFS_FREQ: Megahertz = Megahertz(24);
const MIN_USBHS_FREQ: Megahertz = Megahertz(96);
const DEFAULT_FREQ: Megahertz = Megahertz(12);
#[derive(Debug, Default)]
pub struct ClockRequirements {
pub system_frequency: Option<Megahertz>,
pub custom_pll: Option<Pll>,
}
#[derive(Debug, Copy, Clone)]
pub struct Clocks {
pub(crate) main_clock: MainClock,
pub(crate) system_frequency: Hertz,
}
impl Clocks {
pub fn support_flexcomm_token(&self) -> Option<ClocksSupportFlexcommToken> {
Some(ClocksSupportFlexcommToken { __: () })
}
pub fn support_usbfs_token(&self) -> Option<ClocksSupportUsbfsToken> {
let fast_enough = self.system_frequency >= Hertz::from(MIN_USBFS_FREQ);
let can_latch_sof = self.main_clock == MainClock::Fro96Mhz;
if fast_enough && can_latch_sof {
Some(ClocksSupportUsbfsToken { __: () })
} else {
None
}
}
pub fn support_usbhs_token(&self) -> Option<ClocksSupportUsbhsToken> {
let fast_enough = self.system_frequency >= Hertz::from(MIN_USBHS_FREQ);
if fast_enough {
Some(ClocksSupportUsbhsToken { __: () })
} else {
None
}
}
pub fn support_utick_token(&self) -> Option<ClocksSupportUtickToken> {
Some(ClocksSupportUtickToken { __: () })
}
pub fn support_1mhz_fro_token(&self) -> Option<ClocksSupport1MhzFroToken> {
Some(ClocksSupport1MhzFroToken { __: () })
}
pub fn support_touch_token(&self) -> Option<ClocksSupportTouchToken> {
if self.system_frequency.0 >= 96 {
Some(ClocksSupportTouchToken { __: () })
} else {
None
}
}
pub fn enable_32k_fro(&self, pmc: &mut Pmc) -> ClocksSupport32KhzFroToken {
let mut token = ClocksSupport32KhzFroToken { __: () };
pmc.power_on(&mut token);
token
}
}
#[derive(Debug)]
pub struct Pll {
n: u8,
m: u16,
p: u8,
selp: u8,
seli: u8,
}
impl Pll {
pub unsafe fn new(n: u8, m: u16, p: u8) -> Pll {
let selp = min((m >> 2) + 1, 31) as u8;
let seli = min(
63,
match m {
m if m >= 8000 => 1,
m if m >= 122 => 8000 / m,
_ => 2 * (m >> 2) + 3,
},
) as u8;
Pll {
n,
m,
p,
selp,
seli,
}
}
}
static mut CONFIGURED: bool = false;
#[derive(Debug)]
pub enum ClocksError {
AlreadyConfigured,
NotFeasible,
}
pub type Result<T> = core::result::Result<T, ClocksError>;
impl ClockRequirements {
pub fn system_frequency<Freq>(mut self, freq: Freq) -> Self
where
Freq: Into<Megahertz>,
{
self.system_frequency = Some(freq.into());
self
}
fn get_pll(freq: u32) -> Pll {
debug_assert!(freq >= 5);
debug_assert!(freq <= 150);
for n in 1..=6 {
for p in 1..=30 {
for m in 1..=255 {
if 2 * freq * n * p == 12 * m {
let selp = (m >> 2) + 1; let seli = 2 * (m >> 2) + 3; return Pll {
n: n as u8,
m: m as u16,
p: p as u8,
selp: selp as u8,
seli: seli as u8,
};
}
}
}
}
unreachable!();
}
fn configure_pll0(pll: Pll, pmc: &mut Pmc, syscon: &mut Syscon) {
pmc.raw
.pdruncfg0
.modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
syscon.raw.pll0ctrl.write(|w| unsafe {
w.clken()
.enable()
.seli()
.bits(pll.seli)
.selp()
.bits(pll.selp)
});
syscon
.raw
.pll0ndec
.write(|w| unsafe { w.ndiv().bits(pll.n) });
syscon.raw.pll0ndec.write(|w| unsafe {
w.ndiv().bits(pll.n).nreq().set_bit() });
syscon
.raw
.pll0pdec
.write(|w| unsafe { w.pdiv().bits(pll.p) });
syscon.raw.pll0pdec.write(|w| unsafe {
w.pdiv().bits(pll.p).preq().set_bit() });
syscon
.raw
.pll0sscg0
.write(|w| unsafe { w.md_lbs().bits(0) });
syscon
.raw
.pll0sscg1
.write(|w| unsafe { w.mdiv_ext().bits(pll.m).sel_ext().set_bit() });
syscon.raw.pll0sscg1.write(|w| unsafe {
w.mdiv_ext()
.bits(pll.m)
.sel_ext()
.set_bit()
.mreq()
.set_bit() .md_req()
.set_bit() });
pmc.raw
.pdruncfg0
.modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon());
crate::wait_at_least(6_000);
}
fn get_clock_source_and_div_for_freq(
freq: Megahertz,
pmc: &mut Pmc,
syscon: &mut Syscon,
) -> (MainClock, u8) {
let (main_clock, sys_divider) = match freq {
freq if freq <= 12_u32.MHz() && 12 % freq.0 == 0 => (MainClock::Fro12Mhz, 12 / freq.0),
freq if freq <= 96_u32.MHz() && 96 % freq.0 == 0 => (MainClock::Fro96Mhz, 96 / freq.0),
freq if freq == 150_u32.MHz() => {
syscon.raw.pll0clksel.write(|w| {
w.sel().enum_0x0()
});
Self::configure_pll0(
Pll {
n: 8,
m: 200,
p: 1,
selp: 31,
seli: 53,
},
pmc,
syscon,
);
(MainClock::Pll0, 1)
}
_ => {
let pll = Self::get_pll(freq.0);
syscon.raw.pll0clksel.write(|w| {
w.sel().enum_0x0()
});
Self::configure_pll0(pll, pmc, syscon);
(MainClock::Pll0, 1)
}
};
debug_assert!(sys_divider < 256);
(main_clock, sys_divider as u8)
}
fn set_new_clock_source(
freq: Megahertz,
main_clock: MainClock,
sys_divider: u8,
syscon: &mut Syscon,
) {
syscon
.raw
.fmccr
.modify(|_, w| unsafe { w.flashtim().bits(11) });
match main_clock {
MainClock::Fro12Mhz => {
syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x0());
syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x0());
unsafe {
syscon
.raw
.ahbclkdiv
.modify(|_, w| w.div().bits(sys_divider - 1))
};
}
MainClock::Fro96Mhz => {
syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x3());
syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x0());
unsafe {
syscon
.raw
.ahbclkdiv
.modify(|_, w| w.div().bits(sys_divider - 1))
};
}
MainClock::Pll0 => {
syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x0());
syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x1());
unsafe {
syscon
.raw
.ahbclkdiv
.modify(|_, w| w.div().bits(sys_divider - 1))
};
}
}
match freq.0 {
0..=99 => {
unsafe {
syscon
.raw
.fmccr
.modify(|_, w| w.flashtim().bits((freq.0 / 11) as u8 - 1))
};
}
100..=115 => {
unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(9)) };
}
116..=130 => {
unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(10)) };
}
_ => {
unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(11)) };
}
}
}
pub fn configure(
self,
anactrl: &mut Anactrl,
pmc: &mut Pmc,
syscon: &mut Syscon,
) -> Result<Clocks> {
if unsafe { CONFIGURED } {
return Err(ClocksError::AlreadyConfigured);
}
let freq: Megahertz = self.system_frequency.unwrap_or(DEFAULT_FREQ);
anactrl
.raw
.fro192m_ctrl
.modify(|_, w| w.ena_96mhzclk().enable());
anactrl
.raw
.fro192m_ctrl
.modify(|_, w| w.ena_12mhzclk().enable());
syscon
.raw
.clock_ctrl
.modify(|_, w| w.fro1mhz_clk_ena().enable().fro1mhz_utick_ena().enable());
let (main_clock, sys_divider) = Self::get_clock_source_and_div_for_freq(freq, pmc, syscon);
Self::set_new_clock_source(freq, main_clock, sys_divider, syscon);
unsafe { CONFIGURED = true };
Ok(Clocks {
main_clock,
system_frequency: Hertz::try_from(freq).unwrap(),
})
}
pub unsafe fn reconfigure(self, _clocks: Clocks, pmc: &mut Pmc, syscon: &mut Syscon) -> Clocks {
let freq: Megahertz = self.system_frequency.unwrap_or(DEFAULT_FREQ);
let (main_clock, sys_divider) = Self::get_clock_source_and_div_for_freq(freq, pmc, syscon);
Self::set_new_clock_source(freq, main_clock, sys_divider, syscon);
Clocks {
main_clock,
system_frequency: Hertz::try_from(freq).unwrap(),
}
}
}