use target_device::gclk::clkctrl::GENR::*;
use target_device::gclk::clkctrl::IDR::*;
use target_device::gclk::genctrl::SRCR::*;
use target_device::{self, GCLK, NVMCTRL, PM, SYSCTRL};
use time::{Hertz, U32Ext};
pub type ClockId = target_device::gclk::clkctrl::IDR;
pub type ClockGenId = target_device::gclk::clkctrl::GENR;
pub type ClockSource = target_device::gclk::genctrl::SRCR;
#[derive(Clone, Copy)]
pub struct GClock {
gclk: ClockGenId,
freq: Hertz,
}
impl Into<Hertz> for GClock {
fn into(self) -> Hertz {
self.freq
}
}
struct State {
gclk: GCLK,
}
impl State {
fn reset_gclk(&mut self) {
self.gclk.ctrl.write(|w| w.swrst().set_bit());
while self.gclk.ctrl.read().swrst().bit_is_set()
|| self.gclk.status.read().syncbusy().bit_is_set()
{}
}
fn wait_for_sync(&mut self) {
while self.gclk.status.read().syncbusy().bit_is_set() {}
}
fn set_gclk_divider_and_source(
&mut self,
gclk: ClockGenId,
divider: u16,
src: ClockSource,
improve_duty_cycle: bool,
) {
self.gclk.gendiv.write(|w| unsafe {
w.id().bits(gclk.bits());
w.div().bits(divider)
});
self.wait_for_sync();
self.gclk.genctrl.write(|w| unsafe {
w.id().bits(gclk.bits());
w.src().bits(src.bits());
w.divsel().clear_bit();
w.idc().bit(improve_duty_cycle);
w.genen().set_bit()
});
self.wait_for_sync();
}
fn enable_clock_generator(&mut self, clock: ClockId, generator: ClockGenId) {
self.gclk.clkctrl.write(|w| unsafe {
w.id().bits(clock.bits());
w.gen().bits(generator.bits());
w.clken().set_bit()
});
self.wait_for_sync();
}
}
pub struct GenericClockController {
state: State,
gclks: [Hertz; 8],
used_clocks: u64,
}
impl GenericClockController {
pub fn with_internal_32kosc(
gclk: GCLK,
pm: &mut PM,
sysctrl: &mut SYSCTRL,
nvmctrl: &mut NVMCTRL,
) -> Self {
Self::new(gclk, pm, sysctrl, nvmctrl, false)
}
pub fn with_external_32kosc(
gclk: GCLK,
pm: &mut PM,
sysctrl: &mut SYSCTRL,
nvmctrl: &mut NVMCTRL,
) -> Self {
Self::new(gclk, pm, sysctrl, nvmctrl, true)
}
fn new(
gclk: GCLK,
pm: &mut PM,
sysctrl: &mut SYSCTRL,
nvmctrl: &mut NVMCTRL,
use_external_crystal: bool,
) -> Self {
let mut state = State { gclk };
set_flash_to_half_auto_wait_state(nvmctrl);
enable_gclk_apb(pm);
if use_external_crystal {
enable_external_32kosc(sysctrl);
} else {
enable_internal_32kosc(sysctrl);
}
state.reset_gclk();
if use_external_crystal {
state.set_gclk_divider_and_source(GCLK1, 1, XOSC32K, false);
} else {
state.set_gclk_divider_and_source(GCLK1, 1, OSC32K, false);
}
state.enable_clock_generator(DFLL48, GCLK1);
configure_and_enable_dfll48m(sysctrl, use_external_crystal);
state.set_gclk_divider_and_source(GCLK0, 1, DFLL48M, true);
sysctrl.osc8m.modify(|_, w| {
w.presc()._0();
w.ondemand().clear_bit()
});
pm.cpusel.write(|w| w.cpudiv().div1());
pm.apbasel.write(|w| w.apbadiv().div1());
pm.apbbsel.write(|w| w.apbbdiv().div1());
pm.apbcsel.write(|w| w.apbcdiv().div1());
Self {
state,
gclks: [
OSC48M_FREQ,
OSC32K_FREQ,
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
],
used_clocks: 1u64 << DFLL48M.bits(),
}
}
pub fn gclk0(&mut self) -> GClock {
GClock {
gclk: GCLK0,
freq: self.gclks[0],
}
}
pub fn gclk1(&mut self) -> GClock {
GClock {
gclk: GCLK1,
freq: self.gclks[1],
}
}
pub fn get_gclk(&mut self, gclk: ClockGenId) -> Option<GClock> {
let idx = gclk.bits() as usize;
if self.gclks[idx].0 == 0 {
None
} else {
Some(GClock {
gclk,
freq: self.gclks[idx],
})
}
}
pub fn configure_gclk_divider_and_source(
&mut self,
gclk: ClockGenId,
divider: u16,
src: ClockSource,
improve_duty_cycle: bool,
) -> Option<GClock> {
let idx = gclk.bits() as usize;
if self.gclks[idx].0 != 0 {
return None;
}
self.state
.set_gclk_divider_and_source(gclk, divider, src, improve_duty_cycle);
let freq: Hertz = match src {
XOSC32K | OSC32K | OSCULP32K => OSC32K_FREQ,
GCLKGEN1 => self.gclks[1],
OSC8M => 8.mhz().into(),
DFLL48M => OSC48M_FREQ,
DPLL96M => 96.mhz().into(),
GCLKIN | XOSC | _ => unimplemented!(),
};
self.gclks[idx] = Hertz(freq.0 / divider as u32);
Some(GClock { gclk, freq })
}
}
macro_rules! clock_generator {
($(($id:ident, $Type:ident, $clock:ident),)+) => {
$(
#[derive(Debug)]
pub struct $Type {
freq: Hertz,
}
impl $Type {
pub fn freq(&self) -> Hertz {
self.freq
}
}
impl Into<Hertz> for $Type {
fn into(self) -> Hertz {
self.freq
}
}
)+
impl GenericClockController {
$(
pub fn $id(&mut self, generator: &GClock) -> Option<$Type> {
let bits : u64 = 1<<$clock.bits() as u64;
if (self.used_clocks & bits) != 0 {
return None;
}
self.used_clocks |= bits;
self.state.enable_clock_generator($clock, generator.gclk);
let freq = self.gclks[generator.gclk.bits() as usize];
Some($Type{freq})
}
)+
}
}
}
clock_generator!(
(tcc2_tc3, Tcc2Tc3Clock, TCC2_TC3),
(tc4_tc5, Tc4Tc5Clock, TC4_TC5),
(tc6_tc7, Tc6Tc7Clock, TC6_TC7),
(sercom0_core, Sercom0CoreClock, SERCOM0_CORE),
(sercom1_core, Sercom1CoreClock, SERCOM1_CORE),
(sercom2_core, Sercom2CoreClock, SERCOM2_CORE),
(sercom3_core, Sercom3CoreClock, SERCOM3_CORE),
(sercom4_core, Sercom4CoreClock, SERCOM4_CORE),
(sercom5_core, Sercom5CoreClock, SERCOM5_CORE),
(usb, UsbClock, USB),
);
#[derive(Debug, Clone, Copy)]
pub struct ClockParams {
pub src_freq: Hertz,
pub divider: u16,
pub effective_freq: Hertz,
}
impl ClockParams {
pub fn new(src_freq: Hertz, desired_freq: Hertz) -> Self {
let divider = (src_freq.0 / desired_freq.0.saturating_sub(1).max(1)).next_power_of_two();
let divider = match divider {
1 | 2 | 4 | 8 | 16 | 64 | 256 | 1024 => divider,
32 => 64,
128 => 256,
512 => 1024,
_ => 1024,
};
let effective_freq = Hertz(src_freq.0 / divider);
Self {
src_freq,
divider: divider as u16,
effective_freq,
}
}
}
pub const OSC48M_FREQ: Hertz = Hertz(48_000_000);
pub const OSC32K_FREQ: Hertz = Hertz(32_000);
fn set_flash_to_half_auto_wait_state(nvmctrl: &mut NVMCTRL) {
nvmctrl.ctrlb.modify(|_, w| w.rws().half());
}
fn enable_gclk_apb(pm: &mut PM) {
pm.apbamask.modify(|_, w| w.gclk_().set_bit());
}
fn enable_internal_32kosc(sysctrl: &mut SYSCTRL) {
let calibration = super::calibration::osc32k_cal();
sysctrl.osc32k.write(|w| {
unsafe {
w.ondemand().clear_bit();
w.calib().bits(calibration);
w.startup().bits(6);
}
w.en32k().set_bit();
w.enable().set_bit()
});
while sysctrl.pclksr.read().osc32krdy().bit_is_clear() {
}
}
fn enable_external_32kosc(sysctrl: &mut SYSCTRL) {
sysctrl.xosc32k.modify(|_, w| {
unsafe {
w.startup().bits(6);
}
w.ondemand().clear_bit();
w.en32k().set_bit();
w.xtalen().set_bit()
});
sysctrl.xosc32k.modify(|_, w| w.enable().set_bit());
while sysctrl.pclksr.read().xosc32krdy().bit_is_clear() {
}
}
fn wait_for_dfllrdy(sysctrl: &mut SYSCTRL) {
while sysctrl.pclksr.read().dfllrdy().bit_is_clear() {}
}
fn configure_and_enable_dfll48m(sysctrl: &mut SYSCTRL, use_external_crystal: bool) {
sysctrl.dfllctrl.write(|w| w.ondemand().clear_bit());
wait_for_dfllrdy(sysctrl);
if use_external_crystal {
sysctrl.dfllmul.write(|w| unsafe {
w.cstep().bits(31);
w.fstep().bits(511);
w.mul().bits(((48_000_000u32 + 32768 / 2) / 32768) as u16)
});
sysctrl.dfllctrl.write(|w| {
w.ondemand().clear_bit();
w.mode().set_bit();
w.waitlock().set_bit();
w.qldis().set_bit()
});
} else {
let coarse = super::calibration::dfll48m_coarse_cal();
let fine = 0x1ff;
sysctrl.dfllval.write(|w| unsafe {
w.coarse().bits(coarse);
w.fine().bits(fine)
});
sysctrl.dfllmul.write(|w| unsafe {
w.cstep().bits(coarse / 4);
w.fstep().bits(10);
w.mul().bits((48_000_000u32 / 32768) as u16)
});
sysctrl.dfllctrl.write(|w| {
w.ondemand().clear_bit();
w.mode().set_bit();
w.ccdis().set_bit();
w.usbcrm().set_bit();
w.bplckc().set_bit()
});
}
wait_for_dfllrdy(sysctrl);
sysctrl.dfllctrl.modify(|_, w| w.enable().set_bit());
wait_for_dfllrdy(sysctrl);
}