Nothing to see here, move along meow
1static TSC_FREQ_HZ: spin::Once<u64> = spin::Once::new();
2
3#[inline]
4pub fn read_tsc() -> u64 {
5 unsafe { core::arch::x86_64::_rdtsc() }
6}
7
8#[inline]
9pub fn read_tsc_fenced() -> u64 {
10 unsafe {
11 core::arch::x86_64::__cpuid(0);
12 core::arch::x86_64::_rdtsc()
13 }
14}
15
16struct Pit {
17 cmd: x86_64::instructions::port::Port<u8>,
18 data: x86_64::instructions::port::Port<u8>,
19}
20
21impl Pit {
22 fn channel0() -> Self {
23 Self {
24 cmd: x86_64::instructions::port::Port::<u8>::new(0x43),
25 data: x86_64::instructions::port::Port::<u8>::new(0x40),
26 }
27 }
28
29 fn start_oneshot(&mut self, count: u16) {
30 unsafe {
31 self.cmd.write(0x30);
32 self.data.write((count & 0xFF) as u8);
33 self.data.write(((count >> 8) & 0xFF) as u8);
34 }
35 }
36
37 fn poll_done(&mut self) -> bool {
38 unsafe {
39 self.cmd.write(0xE2);
40 self.data.read() & 0x80 != 0
41 }
42 }
43}
44
45pub fn calibrate() {
46 let pit_hz: u64 = 1_193_182;
47 let target_ms: u64 = 10;
48 let pit_count: u16 = (pit_hz * target_ms / 1000) as u16;
49
50 let mut pit = Pit::channel0();
51 pit.start_oneshot(pit_count);
52
53 let start = read_tsc_fenced();
54
55 const MAX_PIT_POLLS: u32 = 10_000_000;
56 let mut polls: u32 = 0;
57 while !pit.poll_done() {
58 polls += 1;
59 assert!(polls < MAX_PIT_POLLS, "PIT calibration timeout");
60 }
61
62 let end = read_tsc_fenced();
63 let elapsed = end - start;
64 let freq_hz = elapsed * 1000 / target_ms;
65
66 TSC_FREQ_HZ.call_once(|| freq_hz);
67}
68
69pub fn freq_hz() -> u64 {
70 *TSC_FREQ_HZ.get().expect("TSC not calibrated")
71}
72
73pub fn cycles_to_ns(cycles: u64) -> u64 {
74 let f = freq_hz();
75 ((cycles as u128) * 1_000_000_000 / (f as u128)) as u64
76}