#![allow(clippy::from_over_into)]
use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
use fugit::RateExtU32;
use crate::pac::gclk::clkctrl::Genselect::*;
use crate::pac::gclk::clkctrl::Idselect::*;
use crate::pac::gclk::genctrl::Srcselect::*;
use crate::pac::{self, Gclk, Nvmctrl, Pm, Sysctrl};
use crate::time::Hertz;
pub type ClockId = pac::gclk::clkctrl::Idselect;
pub type ClockGenId = pac::gclk::clkctrl::Genselect;
pub type ClockSource = pac::gclk::genctrl::Srcselect;
#[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,
) {
let mut divisor_invalid = false;
if gclk == Gclk1 {
if divider as u32 >= 2_u32.pow(16) {
divisor_invalid = true;
}
} else if gclk == Gclk2 {
if divider >= 2_u16.pow(5) {
divisor_invalid = true;
}
} else if divider >= 2_u16.pow(8) {
divisor_invalid = true;
}
if divisor_invalid {
panic!("invalid divisor {} for Gclk {}", divider, gclk as u8);
}
self.gclk.gendiv().write(|w| unsafe {
w.id().bits(u8::from(gclk));
w.div().bits(divider)
});
self.wait_for_sync();
self.gclk.genctrl().write(|w| unsafe {
w.id().bits(u8::from(gclk));
w.src().bits(u8::from(src));
w.divsel().clear_bit();
w.idc().bit(improve_duty_cycle);
w.genen().set_bit();
w.oe().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(u8::from(clock));
w.r#gen().bits(u8::from(generator));
w.clken().set_bit()
});
self.wait_for_sync();
}
fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) {
unsafe {
let genctrl_ptr_u8: *mut u8 = self.gclk.genctrl().as_ptr() as *mut u8;
*genctrl_ptr_u8 = u8::from(gclk);
}
self.wait_for_sync();
self.gclk.genctrl().modify(|_, w| w.runstdby().bit(enable));
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_48mhz_from_32khz(gclk, pm, sysctrl, nvmctrl, false)
}
pub fn with_external_32kosc(
gclk: Gclk,
pm: &mut Pm,
sysctrl: &mut Sysctrl,
nvmctrl: &mut Nvmctrl,
) -> Self {
Self::new_48mhz_from_32khz(gclk, pm, sysctrl, nvmctrl, true)
}
#[hal_macro_helper]
fn new_48mhz_from_32khz(
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);
#[hal_cfg("clock-d21")]
set_flash_manual_write(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,
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
],
used_clocks: 1u64 << u8::from(ClockId::Dfll48),
}
}
#[hal_macro_helper]
pub fn with_internal_8mhz(
gclk: Gclk,
pm: &mut Pm,
sysctrl: &mut Sysctrl,
nvmctrl: &mut Nvmctrl,
) -> Self {
let mut state = State { gclk };
#[hal_cfg("clock-d21")]
set_flash_manual_write(nvmctrl);
#[hal_cfg("clock-d11")]
let _ = nvmctrl;
enable_gclk_apb(pm);
state.reset_gclk();
state.set_gclk_divider_and_source(Gclk0, 1, Osc8m, false);
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: [
OSC8M_FREQ,
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
0.Hz(),
],
used_clocks: 0,
}
}
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 = u8::from(gclk) as usize;
if self.gclks[idx].to_Hz() == 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 = u8::from(gclk) as usize;
if self.gclks[idx].to_Hz() != 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 => OSC8M_FREQ,
Dfll48m => OSC48M_FREQ,
Dpll96m => 96.MHz(),
Gclkin | Xosc => unimplemented!(),
};
self.gclks[idx] = freq / divider as u32;
Some(GClock { gclk, freq })
}
pub fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) {
self.state.configure_standby(gclk, enable)
}
}
macro_rules! clock_generator {
($(($id:ident, $Type:ident, $clock:ident),)+) => {
$(
/// A typed token that indicates that the clock for the peripheral(s)
#[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<<u8::from(ClockId::$clock) as u64;
if (self.used_clocks & bits) != 0 {
return None;
}
self.used_clocks |= bits;
self.state.enable_clock_generator(ClockId::$clock, generator.gclk);
let freq = self.gclks[u8::from(generator.gclk) as usize];
Some($Type{freq})
}
)+
}
}
}
#[hal_cfg("clock-d11")]
clock_generator!(
(tcc0, Tcc0Clock, Tcc0),
(tc1_tc2, Tc1Tc2Clock, Tc1Tc2),
(sercom0_core, Sercom0CoreClock, Sercom0Core),
(sercom1_core, Sercom1CoreClock, Sercom1Core),
(sercom2_core, Sercom2CoreClock, Sercom2Core),
(rtc, RtcClock, Rtc),
(adc, AdcClock, Adc),
(wdt, WdtClock, Wdt),
(eic, EicClock, Eic),
(usb, UsbClock, Usb),
(evsys0, Evsys0Clock, Evsys0),
(evsys1, Evsys1Clock, Evsys1),
(evsys2, Evsys2Clock, Evsys2),
(evsys3, Evsys3Clock, Evsys3),
(evsys4, Evsys4Clock, Evsys4),
(evsys5, Evsys5Clock, Evsys5),
(ac_ana, AcAnaClock, AcAna),
(ac_dig, AcDigClock, AcDig),
(dac, DacClock, Dac),
);
#[hal_cfg("clock-d21")]
clock_generator!(
(tcc0_tcc1, Tcc0Tcc1Clock, Tcc0Tcc1),
(tcc2_tc3, Tcc2Tc3Clock, Tcc2Tc3),
(tc4_tc5, Tc4Tc5Clock, Tc4Tc5),
(tc6_tc7, Tc6Tc7Clock, Tc6Tc7),
(sercom0_core, Sercom0CoreClock, Sercom0Core),
(sercom1_core, Sercom1CoreClock, Sercom1Core),
(sercom2_core, Sercom2CoreClock, Sercom2Core),
(sercom3_core, Sercom3CoreClock, Sercom3Core),
(sercom4_core, Sercom4CoreClock, Sercom4Core),
(sercom5_core, Sercom5CoreClock, Sercom5Core),
(usb, UsbClock, Usb),
(rtc, RtcClock, Rtc),
(adc, AdcClock, Adc),
(wdt, WdtClock, Wdt),
(eic, EicClock, Eic),
(evsys0, Evsys0Clock, Evsys0),
(evsys1, Evsys1Clock, Evsys1),
(evsys2, Evsys2Clock, Evsys2),
(evsys3, Evsys3Clock, Evsys3),
(evsys4, Evsys4Clock, Evsys4),
(evsys5, Evsys5Clock, Evsys5),
(evsys6, Evsys6Clock, Evsys6),
(evsys7, Evsys7Clock, Evsys7),
(evsys8, Evsys8Clock, Evsys8),
(evsys9, Evsys9Clock, Evsys9),
(evsys10, Evsys10Clock, Evsys10),
(evsys11, Evsys11Clock, Evsys11),
(ac_ana, AcAnaClock, AcAna),
(ac_dig, AcDigClock, AcDig),
(dac, DacClock, Dac),
(i2s0, I2S0Clock, I2s0),
(i2s1, I2S1Clock, I2s1),
);
pub const OSC48M_FREQ: Hertz = Hertz::Hz(48_000_000);
pub const OSC8M_FREQ: Hertz = Hertz::Hz(8_000_000);
pub const OSC32K_FREQ: Hertz = Hertz::Hz(32_768);
fn set_flash_to_half_auto_wait_state(nvmctrl: &mut Nvmctrl) {
nvmctrl.ctrlb().modify(|_, w| w.rws().half());
}
#[hal_cfg("clock-d21")]
fn set_flash_manual_write(nvmctrl: &mut Nvmctrl) {
nvmctrl.ctrlb().modify(|_, w| w.manw().set_bit());
}
fn enable_gclk_apb(pm: &mut Pm) {
pm.apbamask().modify(|_, w| w.gclk_().set_bit());
}
pub 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();
w.runstdby().set_bit()
});
while sysctrl.pclksr().read().osc32krdy().bit_is_clear() {
}
}
pub 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();
w.runstdby().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() {}
}
#[hal_macro_helper]
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 / 1000) 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());
#[hal_cfg("clock-d21")]
if use_external_crystal {
while sysctrl.pclksr().read().dflllckc().bit_is_clear()
|| sysctrl.pclksr().read().dflllckf().bit_is_clear()
{}
}
wait_for_dfllrdy(sysctrl);
}