rustubs/arch/x86_64/interrupt/
pit.rs

1// x86 programmable interrupt timer
2// TODO this is a device, should not live under interrupt module
3// TODO there should be an machine level timer abstraction
4use crate::machine::device_io::IOPort;
5use crate::machine::time;
6use crate::proc::sched::SET_NEED_RESCHEDULE;
7use crate::proc::sync::bellringer::BellRinger;
8use crate::proc::sync::IRQHandlerEpilogue;
9use crate::proc::task::Task;
10// use crate::proc::sched::
11pub struct PIT {}
12
13impl PIT {
14	const CTRL_PORT: IOPort = IOPort::new(0x43);
15	const DATA_PORT: IOPort = IOPort::new(0x40);
16	// 1193182 Hz is roughly 838 ns
17	const PIT_BASE_NS: u64 = 838;
18	// max is around 54918 us (54 ms)
19	pub fn set_interval(us: u64) -> u64 {
20		let mut divider =
21			(us * 1000 + Self::PIT_BASE_NS / 2) / Self::PIT_BASE_NS;
22		if divider == 0 {
23			panic!("how on earth can you make a zero divider?")
24		}
25		if divider >= 65535 {
26			divider = 65535;
27		}
28		// TODO 65536 actually translates to 0
29		Self::CTRL_PORT.outb(0x34);
30		Self::DATA_PORT.outb((divider & 0xff) as u8);
31		Self::DATA_PORT.outb(((divider & 0xff00) >> 8) as u8);
32		divider * Self::PIT_BASE_NS
33	}
34}
35
36impl IRQHandlerEpilogue for PIT {
37	unsafe fn do_prologue() {
38		// half measure: we can't set the resschedule flag when the first
39		// task is not yet running i.e. before kickoff(). We need an aditional
40		// check here to see if there is a valid task struct on the kernel stack;
41		time::tick();
42		let _task = Task::current();
43		if _task.is_none() {
44			return;
45		}
46		let _ = SET_NEED_RESCHEDULE();
47	}
48	unsafe fn do_epilogue() { BellRinger::check_all(); }
49}