use super::{
super::{Mk20Dx128, Mk20Dx256, Mk64Fx512, Mk66Fx1M0, Mkl26Z64},
osc::OscToken,
};
use crate::{
register::{Register, Reserved},
sync::Flag,
};
use bit_field::BitField;
use core::{marker::PhantomData, sync::atomic::Ordering};
#[repr(C)]
struct McgRegs {
c1: Register<u8>,
c2: Register<u8>,
c3: Register<u8>,
c4: Register<u8>,
c5: Register<u8>,
c6: Register<u8>,
s: Register<u8>,
_reserved_0: Reserved<u8>,
sc: Register<u8>,
_reserved_1: Reserved<u8>,
atcvh: Register<u8>,
atcvl: Register<u8>,
c7: Register<u8>,
c8: Register<u8>,
}
pub struct Mcg<M> {
regs: &'static mut McgRegs,
_mcu: PhantomData<M>,
}
pub struct Fei<'a, M>(&'a mut Mcg<M>);
pub struct Fbe<'a, M>(&'a mut Mcg<M>);
pub struct Pbe<'a, M>(&'a mut Mcg<M>);
pub struct Pee<'a, M>(&'a mut Mcg<M>);
#[non_exhaustive]
pub enum Clock<'a, M> {
Fei(Fei<'a, M>),
Fbe(Fbe<'a, M>),
Pbe(Pbe<'a, M>),
Pee(Pee<'a, M>),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
InvalidDivider,
}
static LOCK: Flag = Flag::new(false);
impl<M> Mcg<M> {
pub fn get() -> Option<Self> {
unsafe {
if LOCK.swap(true, Ordering::Acquire) {
None
} else {
Some(Self {
regs: &mut *(0x4006_4000 as *mut _),
_mcu: PhantomData,
})
}
}
}
pub fn clock(&mut self) -> Clock<M> {
let source: OscSource = self.regs.c1.read().get_bits(6..8).into();
let fll_internal = self.regs.c1.read().get_bit(2);
let pll_enabled = self.regs.c6.read().get_bit(6);
match (fll_internal, pll_enabled, source) {
(true, false, OscSource::LockedLoop) => Clock::Fei(Fei(self)),
(false, false, OscSource::LockedLoop) => panic!("FEE mode not yet supported"),
(true, false, OscSource::Internal) => panic!("FBI mod not yet supported"),
(false, false, OscSource::External) => Clock::Fbe(Fbe(self)),
(_, true, OscSource::External) => Clock::Pbe(Pbe(self)),
(_, true, OscSource::LockedLoop) => Clock::Pee(Pee(self)),
_ => panic!("Unknown clock configuration"),
}
}
}
impl<M> Drop for Mcg<M> {
fn drop(&mut self) {
LOCK.store(false, Ordering::Release);
}
}
#[derive(PartialEq)]
enum OscSource {
LockedLoop,
Internal,
External,
}
impl Into<u8> for OscSource {
fn into(self) -> u8 {
match self {
Self::LockedLoop => 0,
Self::Internal => 1,
Self::External => 2,
}
}
}
impl From<u8> for OscSource {
fn from(value: u8) -> Self {
match value {
0 => Self::LockedLoop,
1 => Self::Internal,
2 => Self::External,
3 => Self::LockedLoop,
_ => panic!("Invalid OscSource value"),
}
}
}
#[derive(PartialEq, Copy, Clone)]
pub enum OscRange {
Low,
High,
VeryHigh,
}
impl Into<u8> for OscRange {
fn into(self) -> u8 {
match self {
Self::Low => 0,
Self::High => 1,
Self::VeryHigh => 2,
}
}
}
impl<'a, M> Fei<'a, M> {
pub fn use_external(
self,
divide: u32,
range: OscRange,
token: Option<OscToken>,
) -> Result<Fbe<'a, M>, Error> {
self.0.regs.c2.update(|c2| {
c2.set_bits(4..6, range.into());
c2.set_bit(2, token.is_some());
});
if token.is_some() {
while !self.0.regs.s.read().get_bit(1) {}
}
let frdiv = if range == OscRange::Low.into() {
match divide {
1 => 0,
2 => 1,
4 => 2,
8 => 3,
16 => 4,
32 => 5,
64 => 6,
128 => 7,
_ => return Err(Error::InvalidDivider),
}
} else {
match divide {
32 => 0,
64 => 1,
128 => 2,
256 => 3,
512 => 4,
1024 => 5,
1280 => 6,
1536 => 7,
_ => return Err(Error::InvalidDivider),
}
};
self.0.regs.c1.update(|c1| {
c1.set_bits(6..8, OscSource::External.into());
c1.set_bits(3..6, frdiv);
c1.set_bit(2, false);
});
while self.0.regs.s.read().get_bit(4) {}
while self.0.regs.s.read().get_bits(2..4) != OscSource::External.into() {}
Ok(Fbe(self.0))
}
}
macro_rules! fbe {
($m:ident) => {
impl<'a> Fbe<'a, $m> {
pub fn enable_pll(self, numerator: u8, denominator: u8) -> Result<Pbe<'a, $m>, Error> {
if numerator < 24 || numerator > 55 {
return Err(Error::InvalidDivider);
}
if denominator < 1 || denominator > 25 {
return Err(Error::InvalidDivider);
}
self.0.regs.c5.update(|c5| {
c5.set_bits(0..5, denominator - 1);
});
self.0.regs.c6.update(|c6| {
c6.set_bits(0..6, numerator - 24);
c6.set_bit(6, true);
});
while !self.0.regs.s.read().get_bit(5) {}
while !self.0.regs.s.read().get_bit(6) {}
Ok(Pbe(self.0))
}
}
};
}
fbe!(Mk20Dx128);
fbe!(Mk20Dx256);
fbe!(Mk64Fx512);
fbe!(Mkl26Z64);
impl<'a> Fbe<'a, Mk66Fx1M0> {
pub fn enable_pll(self, numerator: u8, denominator: u8) -> Result<Pbe<'a, Mk66Fx1M0>, Error> {
if numerator < 16 || numerator > 47 {
return Err(Error::InvalidDivider);
}
if denominator < 1 || denominator > 7 {
return Err(Error::InvalidDivider);
}
self.0.regs.c5.update(|c5| {
c5.set_bits(0..4, denominator - 1);
});
self.0.regs.c6.update(|c6| {
c6.set_bits(0..5, numerator - 16);
c6.set_bit(6, true);
});
while !self.0.regs.s.read().get_bit(5) {}
while !self.0.regs.s.read().get_bit(6) {}
Ok(Pbe(self.0))
}
}
impl<'a, M> Pbe<'a, M> {
pub fn use_pll(self) -> Pee<'a, M> {
self.0.regs.c1.update(|c1| {
c1.set_bits(6..8, OscSource::LockedLoop.into());
});
while self.0.regs.s.read().get_bits(2..4) != 3 {}
Pee(self.0)
}
pub fn disable_pll(self) -> Fbe<'a, M> {
self.0.regs.c6.update(|c6| {
c6.set_bit(6, false);
});
while self.0.regs.s.read().get_bit(5) {}
Fbe(self.0)
}
}
impl<'a, M> Pee<'a, M> {
pub fn bypass_pll(self) -> Pbe<'a, M> {
self.0.regs.c1.update(|c1| {
c1.set_bits(6..8, OscSource::External.into());
});
while self.0.regs.s.read().get_bits(2..4) != OscSource::External.into() {}
Pbe(self.0)
}
}