#![allow(clippy::from_over_into)]
use crate::pac::gclk::clkctrl::GEN_A::*;
use crate::pac::gclk::clkctrl::ID_A::*;
use crate::pac::gclk::genctrl::SRC_A::*;
use crate::pac::{self, GCLK, NVMCTRL, PM, SYSCTRL};
use crate::time::{Hertz, U32Ext};
pub type ClockId = pac::gclk::clkctrl::ID_A;
pub type ClockGenId = pac::gclk::clkctrl::GEN_A;
pub type ClockSource = pac::gclk::genctrl::SRC_A;
#[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.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)
}
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);
#[cfg(feature = "samd21")]
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,
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
],
used_clocks: 1u64 << u8::from(ClockId::DFLL48),
}
}
pub fn with_internal_8mhz(
gclk: GCLK,
pm: &mut PM,
sysctrl: &mut SYSCTRL,
nvmctrl: &mut NVMCTRL,
) -> Self {
let mut state = State { gclk };
#[cfg(feature = "samd21")]
set_flash_manual_write(nvmctrl);
#[cfg(not(feature = "samd21"))]
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,
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
Hertz(0),
],
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].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 = u8::from(gclk) 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 => OSC8M_FREQ,
DFLL48M => OSC48M_FREQ,
DPLL96M => 96.mhz().into(),
GCLKIN | XOSC => unimplemented!(),
};
self.gclks[idx] = Hertz(freq.0 / 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})
}
)+
}
}
}
#[cfg(feature = "samd11")]
clock_generator!(
(tcc0, Tcc0Clock, TCC0),
(tc1_tc2, Tc1Tc2Clock, TC1_TC2),
(sercom0_core, Sercom0CoreClock, SERCOM0_CORE),
(sercom1_core, Sercom1CoreClock, SERCOM1_CORE),
(sercom2_core, Sercom2CoreClock, SERCOM2_CORE),
(rtc, RtcClock, RTC),
(adc, AdcClock, ADC),
(wdt, WdtClock, WDT),
(eic, EicClock, EIC),
(evsys0, Evsys0Clock, EVSYS_0),
(evsys1, Evsys1Clock, EVSYS_1),
(evsys2, Evsys2Clock, EVSYS_2),
(evsys3, Evsys3Clock, EVSYS_3),
(evsys4, Evsys4Clock, EVSYS_4),
(evsys5, Evsys5Clock, EVSYS_5),
(ac_ana, AcAnaClock, AC_ANA),
(ac_dig, AcDigClock, AC_DIG),
(dac, DacClock, DAC),
);
#[cfg(feature = "samd21")]
clock_generator!(
(tcc0_tcc1, Tcc0Tcc1Clock, TCC0_TCC1),
(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),
(rtc, RtcClock, RTC),
(adc, AdcClock, ADC),
(wdt, WdtClock, WDT),
(eic, EicClock, EIC),
(evsys0, Evsys0Clock, EVSYS_0),
(evsys1, Evsys1Clock, EVSYS_1),
(evsys2, Evsys2Clock, EVSYS_2),
(evsys3, Evsys3Clock, EVSYS_3),
(evsys4, Evsys4Clock, EVSYS_4),
(evsys5, Evsys5Clock, EVSYS_5),
(evsys6, Evsys6Clock, EVSYS_6),
(evsys7, Evsys7Clock, EVSYS_7),
(evsys8, Evsys8Clock, EVSYS_8),
(evsys9, Evsys9Clock, EVSYS_9),
(evsys10, Evsys10Clock, EVSYS_10),
(evsys11, Evsys11Clock, EVSYS_11),
(ac_ana, AcAnaClock, AC_ANA),
(ac_dig, AcDigClock, AC_DIG),
(dac, DacClock, DAC),
(i2s0, I2S0Clock, I2S_0),
(i2s1, I2S1Clock, I2S_1),
);
pub const OSC48M_FREQ: Hertz = Hertz(48_000_000);
pub const OSC8M_FREQ: Hertz = Hertz(8_000_000);
pub const OSC32K_FREQ: Hertz = Hertz(32_768);
fn set_flash_to_half_auto_wait_state(nvmctrl: &mut NVMCTRL) {
nvmctrl.ctrlb.modify(|_, w| w.rws().half());
}
#[cfg(feature = "samd21")]
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() {}
}
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();
#[cfg(feature = "samd11")]
w.usbcrm().set_bit();
w.bplckc().set_bit()
});
}
wait_for_dfllrdy(sysctrl);
sysctrl.dfllctrl.modify(|_, w| w.enable().set_bit());
#[cfg(feature = "samd21")]
if use_external_crystal {
while sysctrl.pclksr.read().dflllckc().bit_is_clear()
|| sysctrl.pclksr.read().dflllckf().bit_is_clear()
{}
}
wait_for_dfllrdy(sysctrl);
}