use core::marker::PhantomData;
use fugit::RateExtU32;
use typenum::U0;
use crate::pac::oscctrl;
use crate::pac::oscctrl::dpll::{Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus, dpllsyncbusy};
use crate::pac::oscctrl::dpll::dpllctrlb::Refclkselect;
use crate::time::Hertz;
use crate::typelevel::{Decrement, Increment, Sealed};
use super::gclk::GclkId;
use super::pclk::{Pclk, PclkId};
use super::xosc::{Xosc0Id, Xosc1Id, XoscId};
use super::xosc32k::Xosc32kId;
use super::{Enabled, Source};
pub struct DpllToken<D: DpllId> {
dpll: PhantomData<D>,
}
impl<D: DpllId> DpllToken<D> {
#[inline]
pub(super) unsafe fn new() -> Self {
Self { dpll: PhantomData }
}
#[inline]
fn dpll(&self) -> &oscctrl::Dpll {
unsafe { (*crate::pac::Oscctrl::PTR).dpll(D::NUM) }
}
#[inline]
fn ctrla(&self) -> &Dpllctrla {
self.dpll().dpllctrla()
}
#[inline]
fn ctrlb(&self) -> &Dpllctrlb {
self.dpll().dpllctrlb()
}
#[inline]
fn ratio(&self) -> &Dpllratio {
self.dpll().dpllratio()
}
#[inline]
fn syncbusy(&self) -> dpllsyncbusy::R {
self.dpll().dpllsyncbusy().read()
}
#[inline]
fn status(&self) -> dpllstatus::R {
self.dpll().dpllstatus().read()
}
#[inline]
fn configure(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) {
let div = match id {
DynDpllSourceId::Xosc0 | DynDpllSourceId::Xosc1 => prediv / 2 - 1,
_ => 0,
};
self.ctrlb().modify(|_, w| {
unsafe { w.div().bits(div) };
w.refclk().variant(id.into());
w.lbypass().bit(settings.lock_bypass);
w.wuf().bit(settings.wake_up_fast);
if let Some(cap) = settings.dco_filter {
w.dcoen().bit(true);
unsafe {
w.dcofilter().bits(cap as u8);
}
} else {
w.dcoen().bit(false);
}
unsafe { w.filter().bits(settings.filter as u8) }
});
self.ratio().write(|w| unsafe {
w.ldr().bits(settings.mult - 1);
w.ldrfrac().bits(settings.frac)
});
while self.syncbusy().dpllratio().bit_is_set() {}
self.ctrla().modify(|_, w| {
w.ondemand().bit(settings.on_demand);
w.runstdby().bit(settings.run_standby)
});
}
#[inline]
fn enable(&mut self) {
self.ctrla().modify(|_, w| w.enable().set_bit());
while self.syncbusy().enable().bit_is_set() {}
}
#[inline]
fn disable(&mut self) {
self.ctrla().modify(|_, w| w.enable().clear_bit());
while self.syncbusy().enable().bit_is_set() {}
}
#[inline]
fn is_locked(&self) -> bool {
self.status().lock().bit()
}
#[inline]
fn is_ready(&self) -> bool {
self.status().clkrdy().bit()
}
}
pub enum DynDpllId {
Dpll0,
Dpll1,
}
pub trait DpllId: Sealed + PclkId {
const DYN: DynDpllId;
const NUM: usize;
}
pub enum Dpll0Id {}
impl Sealed for Dpll0Id {}
impl DpllId for Dpll0Id {
const DYN: DynDpllId = DynDpllId::Dpll0;
const NUM: usize = 0;
}
pub enum Dpll1Id {}
impl Sealed for Dpll1Id {}
impl DpllId for Dpll1Id {
const DYN: DynDpllId = DynDpllId::Dpll1;
const NUM: usize = 1;
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DynDpllSourceId {
Pclk,
Xosc0,
Xosc1,
Xosc32k,
}
impl From<DynDpllSourceId> for Refclkselect {
fn from(source: DynDpllSourceId) -> Self {
match source {
DynDpllSourceId::Pclk => Refclkselect::Gclk,
DynDpllSourceId::Xosc0 => Refclkselect::Xosc0,
DynDpllSourceId::Xosc1 => Refclkselect::Xosc1,
DynDpllSourceId::Xosc32k => Refclkselect::Xosc32,
}
}
}
pub trait DpllSourceId {
const DYN: DynDpllSourceId;
#[doc(hidden)]
type Reference<D: DpllId>: settings::Reference;
}
impl<G: GclkId> DpllSourceId for G {
const DYN: DynDpllSourceId = DynDpllSourceId::Pclk;
type Reference<D: DpllId> = settings::Pclk<D, G>;
}
impl DpllSourceId for Xosc0Id {
const DYN: DynDpllSourceId = DynDpllSourceId::Xosc0;
type Reference<D: DpllId> = settings::Xosc;
}
impl DpllSourceId for Xosc1Id {
const DYN: DynDpllSourceId = DynDpllSourceId::Xosc1;
type Reference<D: DpllId> = settings::Xosc;
}
impl DpllSourceId for Xosc32kId {
const DYN: DynDpllSourceId = DynDpllSourceId::Xosc32k;
type Reference<D: DpllId> = settings::Xosc32k;
}
#[derive(Copy, Clone)]
pub enum PiFilter {
Bw23p2kHzDf0p75 = 0xA,
Bw32p8kHzDf0p53 = 0xE,
Bw32p8kHzDf1p06 = 0xB,
Bw46p4kHzDf0p38 = 0x2,
Bw46p4kHzDf0p75 = 0xF,
Bw46p4kHzDf1p49 = 0x8,
Bw65p6kHzDf0p28 = 0x6,
Bw65p6kHzDf0p54 = 0x3,
Bw65p6kHzDf1p07 = 0xC,
Bw65p6kHzDf2p11 = 0x9,
Bw92p7kHzDf0p39 = 0x7,
Bw92p7kHzDf0p76 = 0x0,
Bw92p7kHzDf1p51 = 0xD,
Bw131kHzDf0p56 = 0x4,
Bw131kHzDf1p08 = 0x1,
Bw185kHzDf0p79 = 0x5,
}
#[derive(Copy, Clone)]
pub enum DcoFilter {
C0p5pF = 0,
C1pF = 1,
C1p5pF = 2,
C2pF = 3,
C2p5pF = 4,
C3pF = 5,
C3p5pF = 6,
C4pF = 7,
}
#[derive(Copy, Clone)]
struct Settings {
mult: u16,
frac: u8,
lock_bypass: bool,
wake_up_fast: bool,
on_demand: bool,
run_standby: bool,
filter: PiFilter,
dco_filter: Option<DcoFilter>,
}
mod settings {
use super::super::pclk;
use super::RateExtU32;
use super::{DpllId, GclkId, Hertz};
pub struct Pclk<D: DpllId, G: GclkId> {
pub pclk: pclk::Pclk<D, G>,
}
pub struct Xosc {
pub freq: Hertz,
pub prediv: u16,
}
pub struct Xosc32k;
pub trait Reference {
fn freq(&self) -> Hertz;
fn prediv(&self) -> u16;
}
impl<D: DpllId, G: GclkId> Reference for Pclk<D, G> {
#[inline]
fn freq(&self) -> Hertz {
self.pclk.freq()
}
#[inline]
fn prediv(&self) -> u16 {
1
}
}
impl Reference for Xosc {
#[inline]
fn freq(&self) -> Hertz {
self.freq
}
#[inline]
fn prediv(&self) -> u16 {
self.prediv
}
}
impl Reference for Xosc32k {
#[inline]
fn freq(&self) -> Hertz {
32_768.Hz()
}
#[inline]
fn prediv(&self) -> u16 {
1
}
}
}
pub struct Dpll<D, I>
where
D: DpllId,
I: DpllSourceId,
{
token: DpllToken<D>,
reference: I::Reference<D>,
settings: Settings,
}
pub type Dpll0<M> = Dpll<Dpll0Id, M>;
pub type Dpll1<M> = Dpll<Dpll1Id, M>;
impl<D, I> Dpll<D, I>
where
D: DpllId,
I: DpllSourceId,
{
fn new(token: DpllToken<D>, reference: I::Reference<D>) -> Self {
let settings = Settings {
mult: 1,
frac: 0,
lock_bypass: false,
wake_up_fast: false,
on_demand: true,
run_standby: false,
filter: PiFilter::Bw92p7kHzDf0p76,
dco_filter: None,
};
Self {
token,
reference,
settings,
}
}
}
impl<D, G> Dpll<D, G>
where
D: DpllId,
G: GclkId,
{
#[inline]
pub fn from_pclk(token: DpllToken<D>, pclk: Pclk<D, G>) -> Self {
let reference = settings::Pclk { pclk };
Dpll::new(token, reference)
}
#[inline]
pub fn free_pclk(self) -> (DpllToken<D>, Pclk<D, G>) {
(self.token, self.reference.pclk)
}
}
impl<D, X> Dpll<D, X>
where
D: DpllId,
X: XoscId + DpllSourceId<Reference<D> = settings::Xosc>,
{
#[inline]
pub fn from_xosc<S>(token: DpllToken<D>, source: S) -> (Self, S::Inc)
where
S: Source<Id = X> + Increment,
{
let reference = settings::Xosc {
freq: source.freq(),
prediv: 2,
};
let dpll = Dpll::new(token, reference);
(dpll, source.inc())
}
#[inline]
pub fn free_xosc<S>(self, source: S) -> (DpllToken<D>, S::Dec)
where
S: Source<Id = X> + Decrement,
{
(self.token, source.dec())
}
#[inline]
pub fn prediv(mut self, prediv: u16) -> Self {
if prediv % 2 != 0 || prediv < 2 || prediv > 4096 {
panic!("DPLL prediv must be an even integer in the range [2, 4096]")
}
self.reference.prediv = prediv;
self
}
}
impl<D: DpllId> Dpll<D, Xosc32kId> {
#[inline]
pub fn from_xosc32k<S>(token: DpllToken<D>, source: S) -> (Self, S::Inc)
where
S: Source<Id = Xosc32kId> + Increment,
{
let dpll = Dpll::new(token, settings::Xosc32k);
(dpll, source.inc())
}
pub fn free_xosc32k<S>(self, source: S) -> (DpllToken<D>, S::Dec)
where
S: Source<Id = Xosc32kId> + Decrement,
{
(self.token, source.dec())
}
}
impl<D, I> Dpll<D, I>
where
D: DpllId,
I: DpllSourceId,
{
#[inline]
pub fn loop_div(mut self, int: u16, frac: u8) -> Self {
if int < 1 || int > 0x2000 {
panic!("Invalid integer part of the DPLL loop divider")
}
if frac > 31 {
panic!("Invalid fractional part of the DPLL loop divider")
}
self.settings.mult = int;
self.settings.frac = frac;
self
}
#[inline]
pub fn lock_bypass(mut self, bypass: bool) -> Self {
self.settings.lock_bypass = bypass;
self
}
#[inline]
pub fn wake_up_fast(mut self, wuf: bool) -> Self {
self.settings.wake_up_fast = wuf;
self
}
#[inline]
pub fn filter(mut self, filter: PiFilter) -> Self {
self.settings.filter = filter;
self
}
#[inline]
pub fn dco_filter(mut self, capacitor: DcoFilter) -> Self {
self.settings.dco_filter = Some(capacitor);
self
}
#[inline]
pub fn on_demand(mut self, on_demand: bool) -> Self {
self.settings.on_demand = on_demand;
self
}
#[inline]
pub fn run_standby(mut self, run_standby: bool) -> Self {
self.settings.run_standby = run_standby;
self
}
#[inline]
fn input_freq(&self) -> Hertz {
use settings::Reference;
self.reference.freq() / self.reference.prediv() as u32
}
#[inline]
fn output_freq(&self) -> Hertz {
let input = self.input_freq().to_Hz() as u64;
let multiplier_times_32 =
(32 * self.settings.mult as u32 + self.settings.frac as u32) as u64;
let output = (input * multiplier_times_32 / 32) as u32;
output.Hz()
}
#[inline]
pub fn freq(&self) -> Hertz {
self.output_freq()
}
#[inline]
pub fn enable(self) -> EnabledDpll<D, I> {
let input_freq = self.input_freq().to_Hz();
let output_freq = self.output_freq().to_Hz();
if input_freq < 32_000 || input_freq > 3_200_000 {
panic!("Invalid DPLL input frequency");
}
if output_freq < 96_000_000 || output_freq > 200_000_000 {
panic!("Invalid DPLL output frequency");
}
self.enable_unchecked()
}
#[inline]
pub fn enable_unchecked(mut self) -> EnabledDpll<D, I> {
use settings::Reference;
let prediv = self.reference.prediv();
self.token.configure(I::DYN, self.settings, prediv);
self.token.enable();
Enabled::new(self)
}
}
pub type EnabledDpll<D, I, N = U0> = Enabled<Dpll<D, I>, N>;
pub type EnabledDpll0<I, N = U0> = EnabledDpll<Dpll0Id, I, N>;
pub type EnabledDpll1<I, N = U0> = EnabledDpll<Dpll1Id, I, N>;
impl<D, I> EnabledDpll<D, I>
where
D: DpllId,
I: DpllSourceId,
{
#[inline]
pub fn disable(mut self) -> Dpll<D, I> {
self.0.token.disable();
self.0
}
}
impl<D, I, N> EnabledDpll<D, I, N>
where
D: DpllId,
I: DpllSourceId,
{
#[inline]
pub fn is_locked(&self) -> bool {
self.0.token.is_locked()
}
#[inline]
pub fn is_ready(&self) -> bool {
self.0.token.is_ready()
}
}
impl<D, I, N> Source for EnabledDpll<D, I, N>
where
D: DpllId,
I: DpllSourceId,
{
type Id = D;
#[inline]
fn freq(&self) -> Hertz {
self.0.freq()
}
}