#![allow(non_snake_case)]
use core::cell::{Cell, RefCell};
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_time_driver::{Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use py32_metapac::timer::{regs, TimGp16};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::timer::vals;
use crate::rcc::{self, SealedRccPeripheral};
use crate::timer::{CoreInstance, GeneralInstance1Channel};
use crate::{interrupt, peripherals};
#[cfg(time_driver_tim1)]
type T = peripherals::TIM1;
#[cfg(time_driver_tim2)]
type T = peripherals::TIM2;
#[cfg(time_driver_tim3)]
type T = peripherals::TIM3;
#[cfg(time_driver_tim4)]
type T = peripherals::TIM4;
#[cfg(time_driver_tim5)]
type T = peripherals::TIM5;
#[cfg(time_driver_tim8)]
type T = peripherals::TIM8;
#[cfg(time_driver_tim9)]
type T = peripherals::TIM9;
#[cfg(time_driver_tim12)]
type T = peripherals::TIM12;
#[cfg(time_driver_tim15)]
type T = peripherals::TIM15;
#[cfg(time_driver_tim20)]
type T = peripherals::TIM20;
#[cfg(time_driver_tim21)]
type T = peripherals::TIM21;
#[cfg(time_driver_tim22)]
type T = peripherals::TIM22;
#[cfg(time_driver_tim23)]
type T = peripherals::TIM23;
#[cfg(time_driver_tim24)]
type T = peripherals::TIM24;
foreach_interrupt! {
(TIM1, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim1)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM2, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim2)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM3, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim3)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM4, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim4)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM5, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim5)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM8, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim8)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM9, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim9)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM12, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim12)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM15, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim15)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM20, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim20)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM21, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim21)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM22, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim22)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM23, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim23)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM24, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim24)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
}
fn regs_gp16() -> TimGp16 {
unsafe { TimGp16::from_ptr(T::regs()) }
}
fn calc_now(period: u32, counter: u16) -> u64 {
((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
}
struct AlarmState {
timestamp: Cell<u64>,
}
unsafe impl Send for AlarmState {}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
pub(crate) struct RtcDriver {
period: AtomicU32,
alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
period: AtomicU32::new(0),
alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
queue: Mutex::new(RefCell::new(Queue::new()))
});
impl RtcDriver {
fn init(&'static self, cs: critical_section::CriticalSection) {
let r = regs_gp16();
rcc::enable_and_reset_with_cs::<T>(cs);
let timer_freq = T::frequency();
r.cr1().modify(|w| w.set_cen(false));
r.cnt().write(|w| w.set_cnt(0));
let psc = timer_freq.0 / TICK_HZ as u32 - 1;
let psc: u16 = match psc.try_into() {
Err(_) => panic!("psc division overflow: {}", psc),
Ok(n) => n,
};
r.psc().write_value(psc);
r.arr().write(|w| w.set_arr(u16::MAX));
r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTERONLY));
r.egr().write(|w| w.set_ug(true));
r.cr1().modify(|w| w.set_urs(vals::Urs::ANYEVENT));
r.ccr(0).write(|w| w.set_ccr(0x8000));
r.dier().write(|w| {
w.set_uie(true);
w.set_ccie(0, true);
});
<T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() };
r.cr1().modify(|w| w.set_cen(true));
}
fn on_interrupt(&self) {
let r = regs_gp16();
critical_section::with(|cs| {
let sr = r.sr().read();
let dier = r.dier().read();
r.sr().write_value(regs::SrGp16(!sr.0));
if sr.uif() {
self.next_period();
}
if sr.ccif(0) {
self.next_period();
}
let n = 0;
if sr.ccif(n + 1) && dier.ccie(n + 1) {
self.trigger_alarm(cs);
}
})
}
fn next_period(&self) {
let r = regs_gp16();
let period = self.period.load(Ordering::Relaxed) + 1;
self.period.store(period, Ordering::Relaxed);
let t = (period as u64) << 15;
critical_section::with(move |cs| {
r.dier().modify(move |w| {
let n = 0;
let alarm = self.alarm.borrow(cs);
let at = alarm.timestamp.get();
if at < t + 0xc000 {
w.set_ccie(n + 1, true);
}
})
})
}
fn trigger_alarm(&self, cs: CriticalSection) {
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 r = regs_gp16();
let n = 0;
self.alarm.borrow(cs).timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
r.dier().modify(|w| w.set_ccie(n + 1, false));
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
let diff = timestamp - t;
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
let t = self.now();
if timestamp <= t {
r.dier().modify(|w| w.set_ccie(n + 1, false));
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
true
}
}
impl Driver for RtcDriver {
fn now(&self) -> u64 {
let r = regs_gp16();
let period = self.period.load(Ordering::Relaxed);
compiler_fence(Ordering::Acquire);
let counter = r.cnt().read().cnt();
calc_now(period, counter)
}
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());
}
}
})
}
}
pub(crate) fn init(cs: CriticalSection) {
DRIVER.init(cs)
}