1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Copyright (c) 2017 Stefan Lankes, RWTH Aachen University
//                    Colin Finck, RWTH Aachen University
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

#![allow(dead_code)]

use crate::arch::x86_64::kernel::pic;
use crate::x86::io::*;

const PIT_CLOCK: u64 = 1_193_182;
pub const PIT_INTERRUPT_NUMBER: u8 = pic::PIC1_INTERRUPT_OFFSET + 0;

const PIT_CHANNEL0_DATA_PORT: u16 = 0x40;
const PIT_CHANNEL1_DATA_PORT: u16 = 0x41;
const PIT_CHANNEL2_DATA_PORT: u16 = 0x42;
const PIT_COMMAND_PORT: u16 = 0x43;

const PIT_BINARY_OUTPUT: u8 = 0b0000_0000;
const PIT_BCD_OUTPUT: u8 = 0b0000_0001;

const PIT_COUNTDOWN_MODE: u8 = 0b0000_0000;
const PIT_ONESHOT_MODE: u8 = 0b0000_0010;
const PIT_RATE_GENERATOR_MODE: u8 = 0b0000_0100;
const PIT_SQUARE_WAVE_GENERATOR_MODE: u8 = 0b0000_0110;
const PIT_SW_TRIGGERED_STROBE_MODE: u8 = 0b0000_1000;
const PIT_HW_TRIGGERED_STROBE_MODE: u8 = 0b0000_1010;

const PIT_LOBYTE_ACCESS: u8 = 0b0001_0000;
const PIT_HIBYTE_ACCESS: u8 = 0b0010_0000;

const PIT_CHANNEL0: u8 = 0b0000_0000;
const PIT_CHANNEL1: u8 = 0b0100_0000;
const PIT_CHANNEL2: u8 = 0b1000_0000;

pub fn init(frequency_in_hz: u64) {
	pic::unmask(PIT_INTERRUPT_NUMBER);

	unsafe {
		// Reset the Programmable Interval Timer (PIT).
		outb(
			PIT_COMMAND_PORT,
			PIT_BINARY_OUTPUT
				| PIT_RATE_GENERATOR_MODE
				| PIT_LOBYTE_ACCESS
				| PIT_HIBYTE_ACCESS
				| PIT_CHANNEL0,
		);

		// Calculate the reload value to count down (round it to the closest integer).
		// Then transmit it as two individual bytes to the PIT.
		let count = (PIT_CLOCK + frequency_in_hz / 2) / frequency_in_hz;
		outb(PIT_CHANNEL0_DATA_PORT, count as u8);
		outb(PIT_CHANNEL0_DATA_PORT, (count >> 8) as u8);
	}
}

pub fn deinit() {
	pic::mask(PIT_INTERRUPT_NUMBER);
}