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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
//! The old/historical PIT "Programmable Interval Timer (PIT)"
use core::{hint, mem::transmute};
use bitfield_struct::bitfield;
use super::io::{Port, PortValue};
/// We only use PIT channel 2
///
/// Channel 2 is not able to send interrupts
const CHANNEL: u8 = 2;
const MODE: Port<Mode> = Port::new(0x43);
const CTRL: Port<Ctrl> = Port::new(0x61);
const DATA: Port<u8> = Port::new(0x40 + CHANNEL as u16);
const BASE_FREQUENCY: u64 = 1193182;
/// Historically, PCs had a Timer component of type 8253 or 8254,
/// modern systems come with a compatible chip.
/// Each of these chips provides three 16-bit wide counters ("channel"),
/// each running at a frequency of 1.19318 MHz.
/// The timer's counting speed is thereby independent from the CPU frequency.
///
/// Traditionally, the first counter (channel 0) was used for triggering interrupts,
/// the second one (channel 1) controlled
/// the memory refresh, and the third counter (channel 2) was assigned to the PC speaker.
///
/// As the PIT's frequency is fixed to a constant value of 1.19318 MHz,
/// the PIT can be used for calibration.
/// For this purpose, we use channel 2 only.
pub struct Timer;
impl Timer {
/// Initialize the timer for `us` micro seconds.
pub fn new(us: u16) -> Self {
let counter = BASE_FREQUENCY * us as u64 / 1000000;
assert!(counter < 0xffff);
unsafe {
CTRL.update(|v: Ctrl| {
v.with_enable_speaker_data(false)
.with_enable_timer_counter2(true)
});
MODE.write(
Mode::new()
.with_access(Access::LowAndHighByte)
.with_operation(Operation::InterruptOnTerminalCount)
.with_format(false)
.with_channel(CHANNEL),
);
DATA.write(counter as u8);
DATA.write((counter >> 8) as u8);
}
Self
}
/// Start the timer and actively wait for it to finish.
pub fn wait(&self) -> bool {
loop {
let ctrl = unsafe { CTRL.read() };
if !ctrl.enable_timer_counter2() {
return false;
} else if ctrl.status_timer_counter2() {
return true;
} else {
hint::spin_loop()
}
}
}
}
impl Drop for Timer {
fn drop(&mut self) {
// Disable
unsafe {
CTRL.update(|c| {
c.with_enable_speaker_data(false)
.with_enable_timer_counter2(false)
})
}
}
}
/// Mode register (only writable)
#[bitfield(u8)]
struct Mode {
format: bool,
#[bits(3)]
operation: Operation,
#[bits(2)]
access: Access,
#[bits(2)]
channel: u8,
}
impl PortValue for Mode {
type I = u8;
}
/// Operating mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(unused)]
enum Operation {
InterruptOnTerminalCount = 0,
ProgrammableOneShot = 1,
RateGenerator = 2,
/// useful for the PC speaker
SquareWaveGenerator = 3,
SoftwareTriggeredStrobe = 4,
HardwareTriggeredStrobe = 5,
}
impl Operation {
const fn from_bits(value: u8) -> Self {
unsafe { transmute(value) }
}
const fn into_bits(self) -> u8 {
self as _
}
}
/// Access mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(unused)]
enum Access {
LatchCountValue = 0,
LowByteOnly = 1,
HighByteOnly = 2,
LowAndHighByte = 3,
}
impl Access {
const fn from_bits(value: u8) -> Self {
unsafe { transmute(value) }
}
const fn into_bits(self) -> u8 {
self as _
}
}
/// Timer control register
#[bitfield(u8)]
struct Ctrl {
/// Enable the timer
enable_timer_counter2: bool,
/// If set, speaker output is equal to status_timer_counter2
enable_speaker_data: bool,
_enable_pci_serr: bool,
_enable_nmi_iochk: bool,
/// Must be zero on write
refresh_cycle_toggle: bool,
/// Will be set on timer expiration; must be 0 on write
status_timer_counter2: bool,
/// Not important, must be 0 on write
status_iochk_nmi_source: bool,
/// Not important, must be 0 on write
status_serr_nmi_source: bool,
}
impl PortValue for Ctrl {
type I = u8;
}