use crate::pac::{rcu, RCU};
use crate::unit::*;
use core::num::NonZeroU32;
pub trait RcuExt {
fn constrain(self) -> Rcu;
}
impl RcuExt for RCU {
fn constrain(self) -> Rcu {
Rcu {
apb1: APB1 { _ownership: () },
apb2: APB2 { _ownership: () },
ahb: AHB { _ownership: () },
cfg: CFG { _ownership: () },
bdctl: BDCTL { _ownership: () },
_todo: (),
}
}
}
pub struct Rcu {
pub apb1: APB1,
pub apb2: APB2,
pub ahb: AHB,
pub cfg: CFG,
pub bdctl: BDCTL,
_todo: (),
}
pub struct AHB {
_ownership: (),
}
impl AHB {
#[inline]
pub(crate) fn en(&mut self) -> &rcu::AHBEN {
unsafe { &(*RCU::ptr()).ahben }
}
}
pub struct APB1 {
_ownership: (),
}
impl APB1 {
#[inline]
pub(crate) fn en(&mut self) -> &rcu::APB1EN {
unsafe { &(*RCU::ptr()).apb1en }
}
#[inline]
pub(crate) fn rst(&mut self) -> &rcu::APB1RST {
unsafe { &(*RCU::ptr()).apb1rst }
}
}
pub struct APB2 {
_ownership: (),
}
impl APB2 {
#[inline]
pub(crate) fn en(&mut self) -> &rcu::APB2EN {
unsafe { &(*RCU::ptr()).apb2en }
}
#[inline]
pub(crate) fn rst(&mut self) -> &rcu::APB2RST {
unsafe { &(*RCU::ptr()).apb2rst }
}
}
pub struct CFG {
_ownership: (),
}
impl CFG {
#[inline]
pub(crate) fn cfg0(&mut self) -> &rcu::CFG0 {
unsafe { &(*RCU::ptr()).cfg0 }
}
#[inline]
pub(crate) fn cfg1(&mut self) -> &rcu::CFG1 {
unsafe { &(*RCU::ptr()).cfg1 }
}
#[inline]
pub(crate) fn ctl(&mut self) -> &rcu::CTL {
unsafe { &(*RCU::ptr()).ctl }
}
}
#[derive(Clone, Copy)]
pub struct Clocks {
ck_sys: Hertz,
ahb_shr: u8, apb1_shr: u8, apb2_shr: u8, adc_div: u8, usb_valid: bool,
}
impl Clocks {
pub const fn ck_sys(&self) -> Hertz {
self.ck_sys
}
pub const fn ck_ahb(&self) -> Hertz {
Hertz(self.ck_sys.0 >> self.ahb_shr)
}
pub const fn ck_apb1(&self) -> Hertz {
Hertz(self.ck_sys.0 >> (self.ahb_shr + self.apb1_shr))
}
pub const fn ck_apb2(&self) -> Hertz {
Hertz(self.ck_sys.0 >> (self.ahb_shr + self.apb2_shr))
}
pub const fn ck_timerx(&self) -> Hertz {
Hertz(
self.ck_sys.0
>> (self.ahb_shr + self.apb2_shr - [0, 1, 1, 1, 1][self.apb2_shr as usize]),
)
}
pub const fn ck_adc(&self) -> Hertz {
Hertz((self.ck_sys.0 >> (self.ahb_shr + self.apb2_shr)) / self.adc_div as u32)
}
pub const fn ck_usbfs_valid(&self) -> bool {
self.usb_valid
}
}
#[derive(Default)]
pub struct Strict {
hxtal: Option<NonZeroU32>,
target_ck_sys: Option<NonZeroU32>,
target_ck_i2s: Option<NonZeroU32>,
target_ck_ahb: Option<NonZeroU32>,
target_ck_apb1: Option<NonZeroU32>,
target_ck_apb2: Option<NonZeroU32>,
target_ck_adc: Option<NonZeroU32>,
}
impl Strict {
pub fn new() -> Self {
Strict {
hxtal: None,
target_ck_sys: None,
target_ck_i2s: None,
target_ck_ahb: None,
target_ck_apb1: None,
target_ck_apb2: None,
target_ck_adc: None,
}
}
pub fn use_hxtal(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz >= 4_000_000 && freq_hz <= 32_000_000); self.hxtal = NonZeroU32::new(freq_hz);
self
}
pub fn ck_sys(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz <= 108_000_000); self.target_ck_sys = NonZeroU32::new(freq_hz);
self
}
#[doc(hidden)] pub fn ck_i2s(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
self.target_ck_i2s = NonZeroU32::new(freq_hz);
self
}
pub fn ck_ahb(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz <= 108_000_000); self.target_ck_ahb = NonZeroU32::new(freq_hz);
self
}
pub fn ck_apb1(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz <= 54_000_000); self.target_ck_apb1 = NonZeroU32::new(freq_hz);
self
}
pub fn ck_apb2(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz <= 108_000_000); self.target_ck_apb2 = NonZeroU32::new(freq_hz);
self
}
pub fn ck_adc(mut self, freq: impl Into<Hertz>) -> Self {
let freq_hz = freq.into().0;
assert!(freq_hz <= 14_000_000); self.target_ck_adc = NonZeroU32::new(freq_hz);
self
}
pub fn freeze(self, cfg: &mut CFG) -> Clocks {
const IRC8M: u32 = 8_000_000;
let mut usb_valid = false;
let target_ck_sys = self.target_ck_sys.map(|f| f.get()).unwrap_or(IRC8M);
let target_ck_ahb = self.target_ck_ahb.map(|f| f.get()).unwrap_or(target_ck_sys);
let (scs, use_pll) = match (self.hxtal, target_ck_sys) {
(Some(hxtal), sys) if hxtal.get() == sys => (0b01, false),
(None, sys) if IRC8M == sys => (0b00, false),
_ => (0b10, true),
};
let pllmf = if use_pll {
if let Some(hxtal) = self.hxtal {
let hxtal = hxtal.get();
let calc_pllmf = || {
for div in 1..=16 {
if target_ck_sys == hxtal * 13 / 2 {
return 0b01101; }
let mul = target_ck_sys / (div * hxtal);
if mul < 2 || mul > 32 || mul == 15 {
continue;
}
let out_ck_sys = hxtal * mul / div;
if out_ck_sys == target_ck_sys {
return if mul <= 14 { mul - 2 } else { mul - 1 };
}
}
panic!("invalid frequency")
};
calc_pllmf() as u8
} else {
let pllsel0_src = IRC8M / 2;
let mul_pllmf = target_ck_sys / pllsel0_src;
let mul_pllmf = u32::max(2, u32::min(mul_pllmf, 32));
if target_ck_sys == mul_pllmf * pllsel0_src {
(if mul_pllmf <= 14 {
mul_pllmf - 2
} else {
mul_pllmf - 1
}) as u8
} else if target_ck_sys == pllsel0_src * 13 / 2 {
0b01101 as u8 } else {
panic!("invalid frequency")
}
}
} else {
0 };
let (ahbpsc, ahb_shr) = {
let mut ahb_shr = 0; let mut ans = 0b0111u8;
let mut target_freq = target_ck_ahb;
while ahb_shr <= 9 {
if ahb_shr != 5 && target_freq == target_ck_sys {
break;
}
target_freq *= 2;
ahb_shr += 1;
if ahb_shr != 5 {
ans += 1;
}
}
if ans > 0b1111 {
panic!("invalid frequency")
}
(ans, ahb_shr)
};
let calc_psc_apbx = |target_ck_apbx: u32| {
let mut ans = 0b011u8;
let mut target_freq = target_ck_apbx;
while ans <= 0b111 {
if target_freq == target_ck_ahb {
break;
}
target_freq *= 2;
ans += 1;
}
if ans > 0b111 {
panic!("invalid frequency")
};
ans
};
let target_ck_apb1 = self
.target_ck_apb1
.map(|f| f.get())
.unwrap_or(target_ck_ahb / 2);
let apb1psc = calc_psc_apbx(target_ck_apb1);
let target_ck_apb2 = self
.target_ck_apb2
.map(|f| f.get())
.unwrap_or(target_ck_ahb);
let apb2psc = calc_psc_apbx(target_ck_apb2);
let target_ck_adc = self
.target_ck_adc
.map(|f| f.get())
.unwrap_or(target_ck_apb2 / 8);
let adcpsc = if target_ck_adc * 2 == target_ck_apb2 {
0b000
} else if target_ck_adc * 4 == target_ck_apb2 {
0b001
} else if target_ck_adc * 6 == target_ck_apb2 {
0b010
} else if target_ck_adc * 8 == target_ck_apb2 {
0b011
} else if target_ck_adc * 12 == target_ck_apb2 {
0b101
} else if target_ck_adc * 16 == target_ck_apb2 {
0b111
} else {
panic!("invalid freqency")
};
if self.hxtal.is_none() {
cfg.ctl().modify(|_, w| w.irc8men().set_bit());
while cfg.ctl().read().irc8mstb().bit_is_clear() {}
}
if self.hxtal.is_some() {
cfg.ctl().modify(|_, w| w.hxtalen().set_bit());
while cfg.ctl().read().hxtalstb().bit_is_clear() {}
}
if use_pll {
cfg.cfg0().modify(|_, w| unsafe {
w.pllsel().bit(use_pll);
w.pllmf_4().bit(pllmf & 0x10 != 0);
w.pllmf_3_0().bits(pllmf & 0xf)
});
cfg.ctl().modify(|_, w| w.pllen().set_bit());
while cfg.ctl().read().pllstb().bit_is_clear() {}
} else {
cfg.ctl().modify(|_, w| w.pllen().clear_bit());
}
cfg.cfg0().modify(|_, w| unsafe { w.scs().bits(scs) });
if self.hxtal.is_some() {
let ck_pll = target_ck_sys;
let (usb_freq_okay, usbfspsc) = match ck_pll {
48_000_000 => (true, 0b01), 72_000_000 => (true, 0b00), 96_000_000 => (true, 0b11), _ => (false, 0),
};
usb_valid = usb_freq_okay;
cfg.cfg0()
.modify(|_, w| unsafe { w.usbfspsc().bits(usbfspsc) });
}
cfg.cfg0().modify(|_, w| unsafe {
w.ahbpsc().bits(ahbpsc);
w.apb1psc().bits(apb1psc);
w.apb2psc().bits(apb2psc);
w.adcpsc_2().bit(adcpsc & 0b100 != 0);
w.adcpsc_1_0().bits(adcpsc & 0b11)
});
Clocks {
ck_sys: Hertz(target_ck_sys),
ahb_shr,
apb1_shr: apb1psc - 0b011,
apb2_shr: apb2psc - 0b011,
adc_div: (target_ck_apb2 / target_ck_adc) as u8,
usb_valid,
}
}
}
pub struct Precise {
_todo: (),
}
pub struct BDCTL {
_ownership: (),
}
impl BDCTL {
#[inline]
pub(crate) fn bdctl(&mut self) -> &rcu::BDCTL {
unsafe { &(*RCU::ptr()).bdctl }
}
}