use core::cell::RefCell;
use core::task::Waker;
use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
use crate::interrupt::{self, Interrupt};
use crate::peripherals::{Tcxo, Timer};
use crate::soc::ws63::TIMER_CLOCK_HZ;
const TCXO_HZ: u64 = crate::soc::ws63::TCXO_HZ as u64;
const TICK_HZ: u64 = 1_000_000;
const ALARM_CH: usize = 0;
fn now_ticks() -> u64 {
let r = unsafe { &*Tcxo::ptr() };
let st = r.tcxo_status().read().bits();
unsafe { r.tcxo_status().write(|w| w.bits(st | 0x01)) }; for _ in 0..100 {
if r.tcxo_status().read().bits() & 0x10 != 0 {
break; }
}
let c0 = r.tcxo_count0().read().bits() as u64;
let c1 = r.tcxo_count1().read().bits() as u64;
let c2 = r.tcxo_count2().read().bits() as u64;
let c3 = r.tcxo_count3().read().bits() as u64;
let count = (c3 << 48) | (c2 << 32) | (c1 << 16) | c0;
count * TICK_HZ / TCXO_HZ
}
struct Ws63Driver {
queue: Mutex<RefCell<Queue>>,
}
impl Ws63Driver {
fn set_alarm(&self, _cs: CriticalSection<'_>, at: u64) {
let r = unsafe { &*Timer::ptr() };
let prev = r.timer0_control(ALARM_CH).read().bits();
unsafe { r.timer0_control(ALARM_CH).write(|w| w.bits(prev & !1)) };
let _ = r.timer0_eoi(ALARM_CH).read().bits();
if at == u64::MAX {
return; }
let now = now_ticks();
let delta_ticks = at.saturating_sub(now).max(1);
let mut counts = delta_ticks * TIMER_CLOCK_HZ as u64 / TICK_HZ;
if counts == 0 {
counts = 1;
}
if counts > u32::MAX as u64 {
counts = u32::MAX as u64;
}
unsafe {
r.timer0_load_count(ALARM_CH).write(|w| w.bits(counts as u32));
r.timer0_control(ALARM_CH).write(|w| w.bits(1)); interrupt::enable(Interrupt::TIMER_INT0);
}
}
}
impl Driver for Ws63Driver {
fn now(&self) -> u64 {
now_ticks()
}
fn schedule_wake(&self, at: u64, waker: &Waker) {
critical_section::with(|cs| {
let mut q = self.queue.borrow(cs).borrow_mut();
if q.schedule_wake(at, waker) {
let next = q.next_expiration(now_ticks());
self.set_alarm(cs, next);
}
});
}
}
embassy_time_driver::time_driver_impl!(
static DRIVER: Ws63Driver = Ws63Driver {
queue: Mutex::new(RefCell::new(Queue::new()))
}
);
pub fn on_alarm_interrupt() {
critical_section::with(|cs| {
let mut q = DRIVER.queue.borrow(cs).borrow_mut();
let next = q.next_expiration(now_ticks());
DRIVER.set_alarm(cs, next);
});
}