use core::cell::{Cell, RefCell};
#[cfg(not(feature = "_grtc"))]
use core::sync::atomic::{AtomicU32, Ordering, compiler_fence};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
use crate::interrupt::InterruptExt;
#[cfg(feature = "_grtc")]
use crate::pac::grtc::vals::Busy;
use crate::{interrupt, pac};
#[cfg(feature = "_grtc")]
fn rtc() -> pac::grtc::Grtc {
pac::GRTC
}
#[cfg(not(feature = "_grtc"))]
fn rtc() -> pac::rtc::Rtc {
pac::RTC1
}
#[cfg(not(feature = "_grtc"))]
fn calc_now(period: u32, counter: u32) -> u64 {
((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64)
}
#[cfg(feature = "_grtc")]
fn syscounter() -> u64 {
let r = rtc();
if !r.mode().read().syscounteren() {
return 0;
}
r.syscounter(0).active().write(|w| w.set_active(true));
loop {
let countl: u32 = r.syscounter(0).syscounterl().read();
let counth = r.syscounter(0).syscounterh().read();
if counth.busy() == Busy::READY && !counth.overflow() {
let counth: u32 = counth.value();
let count = countl as u64 | ((counth as u64) << 32);
r.syscounter(0).active().write(|w| w.set_active(false));
return count;
}
}
}
#[cfg(not(feature = "_grtc"))]
fn compare_n(n: usize) -> u32 {
1 << (n + 16)
}
#[cfg(feature = "_grtc")]
fn compare_n(n: usize) -> u32 {
1 << n }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_calc_now() {
assert_eq!(calc_now(0, 0x000000), 0x0_000000);
assert_eq!(calc_now(0, 0x000001), 0x0_000001);
assert_eq!(calc_now(0, 0x7FFFFF), 0x0_7FFFFF);
assert_eq!(calc_now(1, 0x7FFFFF), 0x1_7FFFFF);
assert_eq!(calc_now(0, 0x800000), 0x0_800000);
assert_eq!(calc_now(1, 0x800000), 0x0_800000);
assert_eq!(calc_now(1, 0x800001), 0x0_800001);
assert_eq!(calc_now(1, 0xFFFFFF), 0x0_FFFFFF);
assert_eq!(calc_now(2, 0xFFFFFF), 0x1_FFFFFF);
assert_eq!(calc_now(1, 0x000000), 0x1_000000);
assert_eq!(calc_now(2, 0x000000), 0x1_000000);
}
}
struct AlarmState {
timestamp: Cell<u64>,
}
unsafe impl Send for AlarmState {}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
struct RtcDriver {
#[cfg(not(feature = "_grtc"))]
period: AtomicU32,
alarms: Mutex<AlarmState>,
queue: Mutex<RefCell<Queue>>,
}
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
#[cfg(not(feature = "_grtc"))]
period: AtomicU32::new(0),
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
queue: Mutex::new(RefCell::new(Queue::new())),
});
impl RtcDriver {
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
let r = rtc();
#[cfg(not(feature = "_grtc"))]
{
r.cc(3).write(|w| w.set_compare(0x800000));
r.intenset().write(|w| {
w.set_ovrflw(true);
w.set_compare(3, true);
});
}
#[cfg(feature = "_grtc")]
{
r.mode().write(|w| {
w.set_syscounteren(true);
});
}
r.tasks_clear().write_value(1);
r.tasks_start().write_value(1);
#[cfg(not(feature = "_grtc"))]
while r.counter().read().0 != 0 {}
#[cfg(feature = "_grtc")]
loop {
if r.status().lftimer().read().ready() {
break;
}
}
#[cfg(feature = "_grtc")]
{
interrupt::GRTC_1.set_priority(irq_prio);
unsafe { interrupt::GRTC_1.enable() };
}
#[cfg(not(feature = "_grtc"))]
{
interrupt::RTC1.set_priority(irq_prio);
unsafe { interrupt::RTC1.enable() };
}
}
fn on_interrupt(&self) {
let r = rtc();
#[cfg(not(feature = "_grtc"))]
if r.events_ovrflw().read() == 1 {
r.events_ovrflw().write_value(0);
self.next_period();
}
#[cfg(not(feature = "_grtc"))]
if r.events_compare(3).read() == 1 {
r.events_compare(3).write_value(0);
self.next_period();
}
let n = 0;
if r.events_compare(n).read() == 1 {
r.events_compare(n).write_value(0);
critical_section::with(|cs| {
self.trigger_alarm(cs);
});
}
}
#[cfg(not(feature = "_grtc"))]
fn next_period(&self) {
critical_section::with(|cs| {
let r = rtc();
let period = self.period.load(Ordering::Relaxed) + 1;
self.period.store(period, Ordering::Relaxed);
let t = (period as u64) << 23;
let n = 0;
let alarm = &self.alarms.borrow(cs);
let at = alarm.timestamp.get();
if at < t + 0xc00000 {
r.intenset().write(|w| w.0 = compare_n(n));
}
})
}
fn trigger_alarm(&self, cs: CriticalSection) {
let n = 0;
let r = rtc();
#[cfg(not(feature = "_grtc"))]
r.intenclr().write(|w| w.0 = compare_n(n));
#[cfg(feature = "_grtc")]
r.intenclr(1).write(|w| w.0 = compare_n(n));
let alarm = &self.alarms.borrow(cs);
alarm.timestamp.set(u64::MAX);
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 set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
let n = 0;
let alarm = &self.alarms.borrow(cs);
alarm.timestamp.set(timestamp);
let r = rtc();
loop {
let t = self.now();
if timestamp <= t {
#[cfg(not(feature = "_grtc"))]
r.intenclr().write(|w| w.0 = compare_n(n));
#[cfg(feature = "_grtc")]
r.intenclr(1).write(|w| w.0 = compare_n(n));
alarm.timestamp.set(u64::MAX);
return false;
}
#[cfg(not(feature = "_grtc"))]
{
let safe_timestamp = timestamp.max(t + 3);
r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
let diff = timestamp - t;
if diff < 0xc00000 {
r.intenset().write(|w| w.0 = compare_n(n));
if self.now() + 2 <= timestamp {
return true;
}
} else {
r.intenclr().write(|w| w.0 = compare_n(n));
return true;
}
}
#[cfg(feature = "_grtc")]
{
let ccl = timestamp as u32;
let cch = (timestamp >> 32) as u32 & 0xFFFFF;
r.cc(n).ccl().write_value(ccl);
r.cc(n).cch().write(|w| w.set_cch(cch));
r.intenset(1).write(|w| w.0 = compare_n(n));
return true;
}
}
}
}
impl Driver for RtcDriver {
#[cfg(not(feature = "_grtc"))]
fn now(&self) -> u64 {
let period = self.period.load(Ordering::Relaxed);
compiler_fence(Ordering::Acquire);
let counter = rtc().counter().read().0;
calc_now(period, counter)
}
#[cfg(feature = "_grtc")]
fn now(&self) -> u64 {
syscounter()
}
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(feature = "_grtc")]
#[cfg(feature = "rt")]
#[interrupt]
fn GRTC_1() {
DRIVER.on_interrupt()
}
#[cfg(not(feature = "_grtc"))]
#[cfg(feature = "rt")]
#[interrupt]
fn RTC1() {
DRIVER.on_interrupt()
}
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
DRIVER.init(irq_prio)
}