1use core::{marker::PhantomData, sync::atomic::AtomicBool};
7
8use zynq7000::priv_tim::InterruptStatus;
9
10use crate::{clocks::ArmClocks, time::Hertz};
11
12static CORE_0_TIM_TAKEN: AtomicBool = AtomicBool::new(false);
13static CORE_1_TIM_TAKEN: AtomicBool = AtomicBool::new(false);
14
15pub struct CpuPrivateTimer {
17 regs: zynq7000::priv_tim::MmioCpuPrivateTimer<'static>,
18 cpu_3x2x_clock: Hertz,
19 _not_send: PhantomData<*const ()>,
23}
24
25impl CpuPrivateTimer {
26 pub fn take(clocks: &ArmClocks) -> Option<Self> {
30 let mpidr = cortex_ar::register::mpidr::Mpidr::read();
31 let core = mpidr.0 & 0xff;
32 if core != 0 && core != 1 {
33 return None;
34 }
35 if (core == 0 && CORE_0_TIM_TAKEN.swap(true, core::sync::atomic::Ordering::Relaxed))
36 || (core == 1 && CORE_1_TIM_TAKEN.swap(true, core::sync::atomic::Ordering::Relaxed))
37 {
38 return None;
39 }
40
41 Some(Self::steal(clocks))
42 }
43
44 pub fn steal(clocks: &ArmClocks) -> Self {
51 Self {
52 regs: unsafe { zynq7000::priv_tim::CpuPrivateTimer::new_mmio_fixed() },
53 cpu_3x2x_clock: clocks.cpu_3x2x_clk(),
54 _not_send: PhantomData,
55 }
56 }
57
58 #[inline]
61 pub fn write_reload(&mut self, value: u32) {
62 self.regs.write_reload(value);
63 }
64
65 #[inline]
66 pub fn write_counter(&mut self, value: u32) {
67 self.regs.write_counter(value);
68 }
69
70 #[inline]
71 pub fn counter(&self) -> u32 {
72 self.regs.read_counter()
73 }
74}
75
76impl embedded_hal::delay::DelayNs for CpuPrivateTimer {
77 fn delay_ns(&mut self, ns: u32) {
78 let ticks = (ns as u64 * self.cpu_3x2x_clock.raw() as u64) / 1_000_000_000;
81
82 let mut remaining = ticks;
84
85 self.regs.modify_control(|mut val| {
86 val.set_enable(false);
87 val.set_interrupt_enable(false);
89 val.set_auto_reload(false);
90 val
91 });
92 while remaining > 0 {
93 let chunk = (remaining as u32).min(u32::MAX - 1);
94
95 self.regs
97 .write_interrupt_status(InterruptStatus::builder().with_event_flag(true).build());
98
99 self.write_reload(chunk);
101 self.write_counter(chunk);
102 self.regs.modify_control(|mut val| {
103 val.set_enable(true);
104 val
105 });
106
107 while !self.regs.read_interrupt_status().event_flag() {}
109
110 remaining -= chunk as u64;
111 }
112 }
113}