kernel/devices/clock/hardware_timer/
pit.rs1use core::sync::atomic::{AtomicU16, AtomicU64, Ordering};
2
3use alloc::sync::Arc;
4
5use crate::{
6 cpu::{
7 self,
8 idt::{BasicInterruptHandler, InterruptStackFrame64},
9 interrupts::apic,
10 },
11 devices::clock::{ClockDevice, ClockTime, FEMTOS_PER_SEC, NANOS_PER_FEMTO},
12 sync::once::OnceLock,
13};
14
15static PIT_CLOCK: OnceLock<Arc<Pit>> = OnceLock::new();
16
17const PIT_TICK_PERIOD_FEMTOS: u64 = 838095345;
18const PIT_TICK_PERIOD_NANOS: u64 = PIT_TICK_PERIOD_FEMTOS / NANOS_PER_FEMTO;
19
20#[allow(dead_code)]
21pub mod pit_io {
22 pub const PORT_CONTROL: u16 = 0x43;
24 pub const PORT_CHANNEL_0: u16 = 0x40;
25 pub const PORT_CHANNEL_1: u16 = 0x41;
26 pub const PORT_CHANNEL_2: u16 = 0x42;
27
28 pub const SELECT_CHANNEL_0: u8 = 0b00 << 6;
30 pub const SELECT_CHANNEL_1: u8 = 0b01 << 6;
31 pub const SELECT_CHANNEL_2: u8 = 0b10 << 6;
32 pub const SELECT_READ_BACK: u8 = 0b11 << 6;
33
34 pub const ACCESS_LATCH_COUNT: u8 = 0b00 << 4;
36 pub const ACCESS_LOBYTE: u8 = 0b01 << 4;
37 pub const ACCESS_HIBYTE: u8 = 0b10 << 4;
38 pub const ACCESS_LOBYTE_HIBYTE: u8 = 0b11 << 4;
39
40 pub const MODE_INTERRUPT_ON_TERMINAL_COUNT: u8 = 0b000 << 1;
42 pub const MODE_HARDWARE_RETRIGGERABLE_ONE_SHOT: u8 = 0b001 << 1;
43 pub const MODE_RATE_GENERATOR: u8 = 0b010 << 1;
44 pub const MODE_SQUARE_WAVE_GENERATOR: u8 = 0b011 << 1;
45 pub const MODE_SOFTWARE_TRIGGERED_STROBE: u8 = 0b100 << 1;
46 pub const MODE_HARDWARE_TRIGGERED_STROBE: u8 = 0b101 << 1;
47
48 pub const MODE_BINARY: u8 = 0b0;
50 pub const MODE_BCD: u8 = 0b1;
51
52 pub const DEFAULT_INTERRUPT: u8 = 0;
53}
54
55pub fn disable() {
56 unsafe {
58 cpu::io_out(
76 pit_io::PORT_CONTROL,
77 pit_io::SELECT_CHANNEL_0
78 | pit_io::ACCESS_LOBYTE
79 | pit_io::MODE_INTERRUPT_ON_TERMINAL_COUNT
80 | pit_io::MODE_BINARY,
81 );
82 cpu::io_out(pit_io::PORT_CHANNEL_0, 1u8);
83 }
84}
85
86pub fn init() -> Arc<Pit> {
87 cpu::cpu().push_cli();
90
91 if PIT_CLOCK.try_get().is_some() {
93 panic!("PIT already initialized");
94 }
95
96 let clock = PIT_CLOCK.get_or_init(|| Arc::new(Pit::new()));
97
98 cpu::cpu().pop_cli();
99
100 clock.clone()
101}
102
103pub struct Pit {
104 total_counter: AtomicU64,
105 last_counter: AtomicU16,
106}
107
108impl Pit {
109 pub fn new() -> Pit {
110 const RELOAD_VALUE: u16 = 0x0000;
115
116 unsafe {
119 cpu::io_out(
120 pit_io::PORT_CONTROL,
121 pit_io::SELECT_CHANNEL_0
122 | pit_io::ACCESS_LOBYTE_HIBYTE
123 | pit_io::MODE_RATE_GENERATOR
124 | pit_io::MODE_BINARY,
125 );
126
127 cpu::io_out(pit_io::PORT_CHANNEL_0, (RELOAD_VALUE & 0xFF) as u8);
128 cpu::io_out(pit_io::PORT_CHANNEL_0, (RELOAD_VALUE >> 8) as u8);
129 }
130
131 apic::assign_io_irq(
132 pit_interrupt as BasicInterruptHandler,
133 pit_io::DEFAULT_INTERRUPT,
134 cpu::cpu(),
135 );
136
137 Pit {
138 last_counter: AtomicU16::new(0),
139 total_counter: AtomicU64::new(0),
140 }
141 }
142
143 fn read_counter(&self) -> u16 {
144 let lo;
145 let hi;
146
147 cpu::cpu().push_cli();
148
149 unsafe {
152 cpu::io_out(
153 pit_io::PORT_CONTROL,
154 pit_io::SELECT_CHANNEL_0 | pit_io::ACCESS_LATCH_COUNT | pit_io::MODE_BINARY,
155 );
156
157 lo = cpu::io_in::<u8>(pit_io::PORT_CHANNEL_0) as u16;
158 hi = cpu::io_in::<u8>(pit_io::PORT_CHANNEL_0) as u16;
159 }
160 cpu::cpu().pop_cli();
161
162 (hi << 8) | lo
163 }
164
165 fn get_elapsed(&self) -> u64 {
166 let counter = self.read_counter();
167
168 self.last_counter
169 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |_| Some(counter))
170 .unwrap()
171 .wrapping_sub(counter) as u64
172 }
173
174 fn tick_total_counter(&self) -> u64 {
176 self.total_counter
177 .fetch_add(self.get_elapsed(), Ordering::Relaxed);
178
179 self.total_counter.load(Ordering::Relaxed)
180 }
181}
182
183impl ClockDevice for Pit {
184 fn name(&self) -> &'static str {
185 "PIT"
186 }
187
188 fn get_time(&self) -> ClockTime {
189 let counter = self.tick_total_counter();
190
191 let seconds_divider = FEMTOS_PER_SEC / PIT_TICK_PERIOD_FEMTOS;
192 let seconds = counter / seconds_divider;
193 let nanoseconds = (counter % seconds_divider) * PIT_TICK_PERIOD_NANOS;
194
195 ClockTime {
196 seconds,
197 nanoseconds,
198 }
199 }
200
201 fn granularity(&self) -> u64 {
202 PIT_TICK_PERIOD_FEMTOS / NANOS_PER_FEMTO
203 }
204
205 fn require_calibration(&self) -> bool {
206 false
207 }
208
209 fn rating(&self) -> u64 {
210 40
211 }
212}
213
214extern "x86-interrupt" fn pit_interrupt(_stack_frame: InterruptStackFrame64) {
215 PIT_CLOCK.get().tick_total_counter();
216
217 apic::return_from_interrupt();
219}