Skip to main content

proka_kernel/libs/time/
pit.rs

1use lazy_static::lazy_static;
2use spin::Mutex;
3use x86_64::instructions::port::Port;
4
5const PIT_BASE_FREQ: u64 = 1_193_182;
6
7lazy_static! {
8    pub static ref PIT: Mutex<Pit> = Mutex::new(Pit::new());
9}
10
11pub struct Pit {
12    _channel0: Port<u8>,
13    _channel1: Port<u8>,
14    channel2: Port<u8>,
15    command: Port<u8>,
16    speaker_port: Port<u8>,
17}
18
19impl Pit {
20    const fn new() -> Self {
21        Pit {
22            _channel0: Port::new(0x40),
23            _channel1: Port::new(0x41),
24            channel2: Port::new(0x42),
25            command: Port::new(0x43),
26            speaker_port: Port::new(0x61),
27        }
28    }
29    pub fn sleep_blocking(&mut self, us: u64) {
30        // Calculate ticks
31        // ticks = us * 1193182 / 1000000
32        //      = us * 1.193182
33        let ticks = (us * PIT_BASE_FREQ) / 1_000_000;
34
35        let mut remaining_ticks = ticks;
36
37        while remaining_ticks > 0 {
38            let current_ticks = if remaining_ticks > 0xFFFF {
39                0xFFFF
40            } else {
41                remaining_ticks as u16
42            };
43
44            self.wait_ch2_ticks(current_ticks);
45            remaining_ticks -= current_ticks as u64;
46        }
47    }
48
49    /// Read the current count of Channel 2
50    pub fn read_count(&mut self) -> u16 {
51        unsafe {
52            // Send Latch Command for Channel 2
53            // Command: 10 00 000 0 = 0x80
54            // Channel 2 (10)
55            // Latch Count (00) - snapshot current count to internal latch
56            // Mode X (don't care)
57            // BCD X (don't care)
58            self.command.write(0x80);
59
60            // Read LSB then MSB from Channel 2 data port
61            let low = self.channel2.read();
62            let high = self.channel2.read();
63
64            u16::from_le_bytes([low, high])
65        }
66    }
67
68    /// Prepare Channel 2 for one-shot counting (Mode 0)
69    /// Returns the timer to the starting state but does not wait
70    pub fn start_one_shot(&mut self, ticks: u16) {
71        unsafe {
72            // 1. Enable Channel 2 Gate (Port 0x61 Bit 0)
73            let mut port61_val = self.speaker_port.read();
74            // Set Bit 0 (Gate 2) to 1 to enable counting
75            // Set Bit 1 (Speaker) to 0 to disable speaker output
76            port61_val = (port61_val | 1) & !2;
77            self.speaker_port.write(port61_val);
78
79            // 2. Configure Channel 2
80            // Select Channel 2 (10)
81            // Access Mode LSB/MSB (11)
82            // Mode 0: Interrupt on Terminal Count (000)
83            // Binary (0)
84            // Command: 10 11 000 0 = 0xB0
85            self.command.write(0xB0);
86
87            // 3. Write Count
88            self.channel2.write((ticks & 0xFF) as u8);
89            self.channel2.write((ticks >> 8) as u8);
90        }
91    }
92
93    fn wait_ch2_ticks(&mut self, ticks: u16) {
94        self.start_one_shot(ticks);
95
96        // 4. Wait for Out 2 (Bit 5 of Port 0x61) to go HIGH
97        unsafe {
98            loop {
99                let status = self.speaker_port.read();
100                if (status & 0x20) != 0 {
101                    break;
102                }
103                core::hint::spin_loop();
104            }
105        }
106    }
107}