use core::{
cell::{Cell, RefCell},
marker::PhantomData,
sync::atomic::{AtomicBool, AtomicU32, Ordering},
};
use critical_section::{CriticalSection, Mutex};
use embassy_hal_internal::interrupt::InterruptExt as _;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
use fugit::{HertzU32, KilohertzU32, MegahertzU32, RateExtU32 as _};
use crate::{
event_link::IcuInterrupt, interrupt, interrupt::typelevel::Interrupt, pac,
timer_gpt::TimerWidth,
};
#[cfg(agt)]
use pac::agt::{
regs::Agtcr,
vals::{Cks, Lpm, Tck, Tmod},
};
#[cfg(agtw)]
use pac::agtw::{
regs::Agtcr,
vals::{Cks, Lpm, Tck, Tmod},
};
static READY: AtomicBool = AtomicBool::new(false);
struct AlarmState {
timestamp: Cell<u64>,
}
unsafe impl Send for AlarmState {}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
trait TimerPause {
fn while_paused<F>(&mut self, func: F)
where
F: Fn(&Self);
}
impl TimerPause for TimerPeripheral {
#[inline]
fn while_paused<F>(&mut self, func: F)
where
F: FnOnce(&Self),
{
self.agtcr().modify(|r| r.set_tstop(true));
while self.agtcr().read().tcstf() {}
self.agtcr().modify(|r| {
r.set_tedgf(false);
r.set_tundf(false);
r.set_tcmaf(false);
r.set_tcmbf(false);
});
func(self);
self.agtcr().modify(|r| r.set_tstart(true));
while !self.agtcr().read().tcstf() {}
}
}
trait Instance: crate::timer_agt::Instance<Self::Width> + Send + Sync + 'static {
type AlarmInterrupt: interrupt::typelevel::Interrupt;
type UnderflowInterrupt: interrupt::typelevel::Interrupt;
type Width: TimerWidth;
const PERIPHERAL: &'static str;
}
macro_rules! driver_instance {
($peri:ident, $width:ident) => {
impl Instance for crate::peripherals::$peri {
type AlarmInterrupt = crate::interrupt::typelevel::IEL1;
type UnderflowInterrupt = crate::interrupt::typelevel::IEL0;
type Width = $width;
const PERIPHERAL: &'static str = stringify!($peri);
}
};
}
cfg_select! {
agt => {
use crate::peripherals::AGT1;
driver_instance!(AGT1, u16);
type TimerPeripheral = crate::pac::agt::Agt;
const PERIOD : u16 = u16::MAX;
const WIDTH : usize = 16;
},
agtw => {
use crate::peripherals::AGTW1;
driver_instance!(AGTW1, u32);
type TimerPeripheral = crate::pac::agtw::Agtw;
const PERIOD : u32 = u32::MAX;
const WIDTH : usize = 32;
}
}
struct AgtDriver<I: Instance> {
period: AtomicU32,
queue: Mutex<RefCell<Queue>>,
alarms: Mutex<AlarmState>,
phantom: PhantomData<I>,
}
impl<I: Instance> AgtDriver<I> {
const FUDGE_FACTOR: u64 = 10;
pub(crate) fn init(&'static self) {
I::start_module();
let system = pac::SYSTEM;
let timer = I::regs();
let clock_config = crate::clock::clock_status();
let agt_source = match clock_config.sosc {
true => {
if system.sosccr().read().sostp() {
panic!("Sub-clock Oscillator required but disabled.");
}
Tck::Agtsclk
}
false => {
warn!(
"{}: Sub-clock Oscillator not installed/configured. Using Low-speed On Chip Oscillator (±15%).",
I::PERIPHERAL
);
Tck::Agtlclk
}
};
unsafe {
I::AlarmInterrupt::IRQ.enable();
I::UnderflowInterrupt::IRQ.enable();
I::AlarmInterrupt::IRQ.icu_enable(I::COMP_A_EVENT);
I::UnderflowInterrupt::IRQ.icu_enable(I::UNDERFLOW_EVENT);
};
timer.agtcr().write_value(Agtcr(0));
while timer.agtcr().read().tcstf() {}
timer.agtmr2().modify(|r| r.set_cks(Cks::Div1));
for _ in 0..2 {
timer.agt().read();
}
timer.agtmr1().modify(|r| {
r.set_tck(agt_source);
r.set_tmod(Tmod::Timer);
});
timer.agtmr2().modify(|r| {
r.set_cks(Cks::Div1);
r.set_lpm(Lpm::Normal);
});
timer.agt().write_value(PERIOD);
timer.agtcr().modify(|r| {
r.set_tedgf(false);
r.set_tundf(false);
r.set_tcmaf(false);
r.set_tcmbf(false);
});
timer.agtcr().modify(|r| {
r.set_tstart(true);
});
READY.store(true, Ordering::SeqCst);
let mhz: HertzU32 = 1_u32.MHz();
let tick: HertzU32 = (embassy_time_driver::TICK_HZ as u32).Hz();
if tick >= mhz {
let tick: MegahertzU32 = tick.convert();
info!(
"{}: Time driver attached, tick={}, width={}",
I::PERIPHERAL,
tick,
WIDTH
);
} else {
let tick: KilohertzU32 = tick.convert();
info!(
"{}: Time driver attached, tick={}, width={}",
I::PERIPHERAL,
tick,
WIDTH
);
}
}
fn interrupted_alarm(&'static self) {
critical_section::with(|cs| {
let mut next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
while !self.set_alarm(&cs, next) {
next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
}
});
}
fn interrupted_underflow(&'static self) {
critical_section::with(|_cs| {
let mut timer = I::regs();
let _period = self
.period
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |p| Some(p + 1))
.unwrap_or_else(|p| {
error!("Unable to increment period. Time is now inaccurate");
p
});
timer.while_paused(|timer| {
timer.agt().write_value(PERIOD);
for _ in 0..500 {
timer.agtcr().read();
}
});
timer.agtcmsr().modify(|r| r.set_tcmea(true));
});
}
#[must_use]
fn set_alarm(&self, cs: &CriticalSection, timestamp: u64) -> bool {
let mut timer = I::regs();
let alarm = self.alarms.borrow(*cs);
alarm.timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
alarm.timestamp.set(u64::MAX);
return false;
}
let safe_timestamp = (timestamp.max(t + Self::FUDGE_FACTOR) & u64::from(PERIOD)) as u32;
let diff = timestamp - t;
let cur = timer.agt().read();
if diff < u64::from(PERIOD) {
let desired = (PERIOD as u32)
.checked_sub(safe_timestamp)
.expect("safe_timestamp > PERIOD");
timer.while_paused(|timer| {
for _ in 0..500 {
timer.agtcr().read();
}
timer.agt().write_value(cur);
timer.agtcma().write_value(desired as _);
});
timer.agtcmsr().modify(|r| r.set_tcmea(true));
}
true
}
}
impl<I: Instance> Driver for AgtDriver<I> {
fn now(&self) -> u64 {
let timer = I::regs();
if !READY.load(Ordering::SeqCst) {
return 0;
}
let period = self.period.load(Ordering::Acquire);
let count = timer.agt().read();
let count = PERIOD.checked_sub(count).unwrap();
((period as u64) << WIDTH) + (count as u64)
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(&cs, next) {
next = queue.next_expiration(self.now());
}
}
});
}
}
#[cfg(agt)]
embassy_time_driver::time_driver_impl!(static DRIVER: AgtDriver<AGT1> = AgtDriver {
period:AtomicU32::new(0),
queue:Mutex::new(RefCell::new(Queue::new())),
alarms:Mutex::new(AlarmState::new()),
phantom: PhantomData,
});
#[cfg(agtw)]
embassy_time_driver::time_driver_impl!(static DRIVER: AgtDriver<AGTW1> = AgtDriver {
period:AtomicU32::new(0),
queue:Mutex::new(RefCell::new(Queue::new())),
alarms:Mutex::new(AlarmState::new()),
phantom: PhantomData,
});
pub(crate) fn init() {
DRIVER.init()
}
#[interrupt]
fn IEL0() {
let icu = crate::pac::ICU;
icu.ielsr(0).modify(|r| r.set_ir(false));
DRIVER.interrupted_underflow();
}
#[interrupt]
fn IEL1() {
let icu = crate::pac::ICU;
icu.ielsr(1).modify(|r| r.set_ir(false));
DRIVER.interrupted_alarm();
}