rstubs/arch/pit.rs
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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
//! The old/historical PIT "Programmable Interval Timer (PIT)"
use core::hint;
use core::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);
/// Standard frequency in Hz
const BASE_FREQUENCY: u64 = 1_193_182;
/// 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.
#[must_use = "Timer initialized but not used"]
pub fn new(us: u16) -> Self {
let counter = BASE_FREQUENCY * us as u64 / 1_000_000;
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;
}
if ctrl.status_timer_counter2() {
return true;
}
hint::spin_loop();
}
}
/// Beep with a certain frequency.
pub fn beep(frequency: u16) {
if frequency == 0 {
unsafe {
CTRL.update(|v: Ctrl| {
v.with_enable_speaker_data(false)
.with_enable_timer_counter2(false)
});
}
return;
}
unsafe {
// Check if already configured
if !CTRL.read().enable_speaker_data() {
MODE.write(
Mode::new()
.with_access(Access::LowAndHighByte)
.with_operation(Operation::SquareWaveGenerator)
.with_format(false)
.with_channel(CHANNEL),
);
}
// Set the frequency
let counter = (BASE_FREQUENCY / frequency as u64).min(0xffff);
DATA.write(counter as u8);
DATA.write((counter >> 8) as u8);
CTRL.update(|v: Ctrl| {
// Already configured?
if !v.enable_speaker_data() {
v.with_enable_speaker_data(true)
.with_enable_timer_counter2(true)
} else {
v
}
});
}
}
}
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;
}