x86-64: couldn't get HPET to work on real hw
This commit is contained in:
parent
c543576685
commit
3330beedfd
@ -5,6 +5,8 @@ edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[profile.dev.package.tock-registers]
|
||||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
@ -22,9 +24,6 @@ tock-registers = "0.8.1"
|
||||
cfg-if = "1.0.0"
|
||||
git-version = "0.3.5"
|
||||
|
||||
# bitmap-font = { version = "0.3.0", optional = true }
|
||||
# embedded-graphics = { version = "0.8.0", optional = true }
|
||||
|
||||
log = "0.4.20"
|
||||
futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "async-await"] }
|
||||
crossbeam-queue = { version = "0.3.8", default-features = false, features = ["alloc"] }
|
||||
|
@ -2,7 +2,7 @@
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi_lib::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel};
|
||||
use acpi_lib::{mcfg::Mcfg, AcpiTables, InterruptModel};
|
||||
use alloc::boxed::Box;
|
||||
use cpu::Cpu;
|
||||
use device_api::{
|
||||
@ -45,7 +45,7 @@ use self::{
|
||||
acpi::{AcpiAllocator, AcpiHandlerImpl},
|
||||
apic::ioapic::IoApic,
|
||||
intrinsics::{IoPort, IoPortAccess},
|
||||
peripherals::{hpet::Hpet, ps2::PS2Controller},
|
||||
peripherals::{i8253::I8253, ps2::PS2Controller},
|
||||
smp::CPU_COUNT,
|
||||
};
|
||||
|
||||
@ -140,7 +140,7 @@ pub struct X86_64 {
|
||||
combined_terminal: OneTimeInit<CombinedTerminal>,
|
||||
|
||||
ioapic: OneTimeInit<IoApic>,
|
||||
timer: OneTimeInit<Hpet>,
|
||||
timer: OneTimeInit<I8253>,
|
||||
}
|
||||
|
||||
/// x86-64 architecture implementation
|
||||
@ -392,9 +392,6 @@ impl X86_64 {
|
||||
|
||||
self.ioapic.init(IoApic::from_acpi(&apic_info).unwrap());
|
||||
|
||||
let hpet = HpetInfo::new(acpi).unwrap();
|
||||
self.timer.init(Hpet::from_acpi(&hpet).unwrap());
|
||||
|
||||
acpi::init_acpi(acpi).unwrap();
|
||||
|
||||
// Enumerate PCIe root devices
|
||||
@ -442,8 +439,11 @@ impl X86_64 {
|
||||
|
||||
if cpu_id == 0 {
|
||||
self.init_acpi_from_boot_data();
|
||||
|
||||
Self::disable_8259();
|
||||
|
||||
self.timer.init(I8253::new());
|
||||
|
||||
// Initialize debug output as soon as possible
|
||||
let com1_3 = Box::leak(Box::new(ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4))));
|
||||
debug::add_sink(com1_3.port_a(), LogLevel::Debug);
|
||||
@ -478,7 +478,7 @@ impl X86_64 {
|
||||
ps2.init_irq().unwrap();
|
||||
|
||||
device::register_device(self.ioapic.get());
|
||||
device::register_device(self.timer.get());
|
||||
// device::register_device(self.timer.get());
|
||||
device::register_device(ps2);
|
||||
|
||||
// Initialize devices from PCI bus
|
||||
|
@ -1,279 +0,0 @@
|
||||
//! x86-64 implementation of a HPET
|
||||
use core::time::Duration;
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi_lib::hpet::HpetInfo as AcpiHpet;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqLevel, IrqOptions, IrqTrigger},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
Device,
|
||||
};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{x86_64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
device::timer,
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
GeneralCapabilities [
|
||||
COUNTER_CLK_PERIOD OFFSET(32) NUMBITS(32) [],
|
||||
COUNT_SIZE_CAP OFFSET(13) NUMBITS(1) [
|
||||
Supports64Bit = 1
|
||||
],
|
||||
NUM_TIM_CAP OFFSET(8) NUMBITS(5) [],
|
||||
],
|
||||
GeneralConfiguration [
|
||||
LEG_RT_CNF OFFSET(1) NUMBITS(1) [],
|
||||
ENABLE_CNF OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
TimerConfigurationCapablities [
|
||||
TM_INT_TYPE_CNF OFFSET(1) NUMBITS(1) [
|
||||
EdgeTriggered = 0,
|
||||
LevelTriggered = 1,
|
||||
],
|
||||
TM_INT_ENB_CNF OFFSET(2) NUMBITS(1) [
|
||||
InterruptEnabled = 1,
|
||||
InterruptDisabled = 0
|
||||
],
|
||||
TM_TYPE_CNF OFFSET(3) NUMBITS(1) [
|
||||
Periodic = 1,
|
||||
OneShot = 0
|
||||
],
|
||||
TM_PER_INT_CAP OFFSET(4) NUMBITS(1) [
|
||||
SupportsPeriodic = 1,
|
||||
],
|
||||
TM_SIZE_CAP OFFSET(5) NUMBITS(1) [
|
||||
Timer64Bit = 1,
|
||||
Timer32Bit = 0,
|
||||
],
|
||||
TM_VAL_SET_CNF OFFSET(6) NUMBITS(1) [],
|
||||
TM_32MODE_CNF OFFSET(8) NUMBITS(1) [],
|
||||
TM_INT_ROUTE_CNF OFFSET(9) NUMBITS(5) [],
|
||||
TM_FSB_EN_CNF OFFSET(14) NUMBITS(1) [],
|
||||
TM_FSB_INT_DEL_CAP OFFSET(15) NUMBITS(1) [],
|
||||
TM_INT_ROUTE_CAP OFFSET(32) NUMBITS(32) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
TimerRegs {
|
||||
(0x000 => ConfigurationCapability: ReadWrite<u64, TimerConfigurationCapablities::Register>),
|
||||
(0x008 => Comparator: ReadWrite<u64>),
|
||||
(0x010 => FsbInterruptRoute: ReadWrite<u64>),
|
||||
(0x018 => _0),
|
||||
(0x020 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x000 => GeneralCapabilities: ReadOnly<u64, GeneralCapabilities::Register>),
|
||||
(0x008 => _0),
|
||||
(0x010 => GeneralConfiguration: ReadWrite<u64, GeneralConfiguration::Register>),
|
||||
(0x018 => _1),
|
||||
(0x020 => GeneralInterruptStatus: ReadWrite<u64>),
|
||||
(0x028 => _2),
|
||||
|
||||
(0x0F0 => MainCounter: ReadWrite<u64>),
|
||||
(0x0F8 => _3),
|
||||
(0x100 => Timers: [TimerRegs; 3]),
|
||||
(0x160 => _4),
|
||||
(0x400 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
tim0_period: u64,
|
||||
tim0_counter: u64,
|
||||
}
|
||||
|
||||
/// HPET timer group interface
|
||||
pub struct Hpet {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
unsafe fn new(base: usize) -> Result<Self, Error> {
|
||||
// Femtoseconds in a millisecond
|
||||
const FS_IN_MS: u64 = 1000000000000;
|
||||
|
||||
let regs = DeviceMemoryIo::<Regs>::map("hpet", base)?;
|
||||
|
||||
// Disable the counter first
|
||||
regs.GeneralConfiguration
|
||||
.modify(GeneralConfiguration::ENABLE_CNF::CLEAR);
|
||||
|
||||
// Disable legacy routing
|
||||
regs.GeneralConfiguration
|
||||
.modify(GeneralConfiguration::LEG_RT_CNF::CLEAR);
|
||||
|
||||
// Reset the counter
|
||||
regs.MainCounter.set(0);
|
||||
|
||||
// Disable all comparator interrupts
|
||||
for i in 0..3 {
|
||||
regs.Timers[i]
|
||||
.ConfigurationCapability
|
||||
.modify(TimerConfigurationCapablities::TM_INT_ENB_CNF::InterruptDisabled);
|
||||
}
|
||||
|
||||
// Calculate the interrupt period
|
||||
let clk_period = regs
|
||||
.GeneralCapabilities
|
||||
.read(GeneralCapabilities::COUNTER_CLK_PERIOD);
|
||||
|
||||
// Will give an interrupt interval of 1ms
|
||||
let tim0_period = FS_IN_MS / clk_period;
|
||||
|
||||
// Enable the main counter
|
||||
regs.GeneralConfiguration
|
||||
.modify(GeneralConfiguration::ENABLE_CNF::SET);
|
||||
|
||||
Ok(Self {
|
||||
regs,
|
||||
tim0_period,
|
||||
tim0_counter: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MonotonicTimestampProviderDevice for Hpet {
|
||||
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
|
||||
let inner = self.inner.lock();
|
||||
let counter = inner.regs.MainCounter.get();
|
||||
let period = inner.tim0_period;
|
||||
Ok(Duration::from_millis(counter / period))
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Hpet {
|
||||
fn handle_irq(&self) -> bool {
|
||||
let now = {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.regs.GeneralInterruptStatus.set(1);
|
||||
|
||||
inner.tim0_counter = inner.tim0_counter.wrapping_add(1);
|
||||
Duration::from_millis(inner.tim0_counter)
|
||||
};
|
||||
|
||||
timer::tick(now);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Hpet {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"HPET"
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
// Configure timer 0
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
let inner = self.inner.lock();
|
||||
|
||||
let tim0 = &inner.regs.Timers[0];
|
||||
|
||||
if inner.tim0_period > 0x100000000
|
||||
&& !tim0
|
||||
.ConfigurationCapability
|
||||
.matches_all(TimerConfigurationCapablities::TM_SIZE_CAP::Timer64Bit)
|
||||
{
|
||||
panic!("Period is too large and timer doesn't support 64-bit");
|
||||
}
|
||||
|
||||
// Temporarily disable the main counter
|
||||
inner
|
||||
.regs
|
||||
.GeneralConfiguration
|
||||
.modify(GeneralConfiguration::ENABLE_CNF::CLEAR);
|
||||
|
||||
assert!(tim0
|
||||
.ConfigurationCapability
|
||||
.matches_all(TimerConfigurationCapablities::TM_PER_INT_CAP::SupportsPeriodic));
|
||||
|
||||
let mut tim0_irq = None;
|
||||
let tim0_irqs = tim0
|
||||
.ConfigurationCapability
|
||||
.read(TimerConfigurationCapablities::TM_INT_ROUTE_CAP);
|
||||
|
||||
// Skip most likely legacy routes: IRQ2, IRQ0
|
||||
for i in 3..32 {
|
||||
if tim0_irqs & (1 << i) != 0 {
|
||||
tim0_irq = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let tim0_irq = tim0_irq.expect("Could not pick an IRQ for HPET TIM0");
|
||||
|
||||
// Bind and enable the IRQ
|
||||
let irq = IrqNumber::Gsi(tim0_irq);
|
||||
intc.register_irq(
|
||||
irq,
|
||||
IrqOptions {
|
||||
level: IrqLevel::ActiveLow,
|
||||
trigger: IrqTrigger::Level,
|
||||
},
|
||||
self,
|
||||
)?;
|
||||
intc.enable_irq(irq)?;
|
||||
|
||||
// Disable FSB interrupt route and 32 bit mode
|
||||
tim0.ConfigurationCapability.modify(
|
||||
TimerConfigurationCapablities::TM_FSB_EN_CNF::CLEAR
|
||||
+ TimerConfigurationCapablities::TM_32MODE_CNF::CLEAR
|
||||
+ TimerConfigurationCapablities::TM_VAL_SET_CNF::SET,
|
||||
);
|
||||
|
||||
// Setup interrupt route + edge-triggered
|
||||
tim0.ConfigurationCapability.modify(
|
||||
TimerConfigurationCapablities::TM_INT_ROUTE_CNF.val(tim0_irq as u64)
|
||||
+ TimerConfigurationCapablities::TM_INT_TYPE_CNF::LevelTriggered,
|
||||
);
|
||||
|
||||
// Setup periodic mode
|
||||
tim0.ConfigurationCapability
|
||||
.modify(TimerConfigurationCapablities::TM_TYPE_CNF::Periodic);
|
||||
|
||||
// Set the comparator
|
||||
tim0.Comparator.set(inner.tim0_period);
|
||||
|
||||
// Enable the timer
|
||||
tim0.ConfigurationCapability
|
||||
.modify(TimerConfigurationCapablities::TM_INT_ENB_CNF::InterruptEnabled);
|
||||
|
||||
// Reenable the main counter
|
||||
inner
|
||||
.regs
|
||||
.GeneralConfiguration
|
||||
.modify(GeneralConfiguration::ENABLE_CNF::SET);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hpet {
|
||||
/// Creates a HPET instance from its ACPI definition
|
||||
pub fn from_acpi(info: &AcpiHpet) -> Result<Self, Error> {
|
||||
infoln!("Initializing HPET:");
|
||||
infoln!("Address: {:#x}", info.base_address);
|
||||
|
||||
let inner = unsafe { Inner::new(info.base_address) }?;
|
||||
|
||||
Ok(Self {
|
||||
inner: IrqSafeSpinlock::new(inner),
|
||||
})
|
||||
}
|
||||
}
|
97
src/arch/x86_64/peripherals/i8253.rs
Normal file
97
src/arch/x86_64/peripherals/i8253.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use core::time::Duration;
|
||||
|
||||
use abi::error::Error;
|
||||
use device_api::{interrupt::InterruptHandler, timer::MonotonicTimestampProviderDevice, Device};
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{
|
||||
intrinsics::{IoPort, IoPortAccess},
|
||||
IrqNumber,
|
||||
},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::runtime,
|
||||
};
|
||||
|
||||
const FREQUENCY: u32 = 1193180;
|
||||
|
||||
const CMD_CH0: u8 = 0 << 6;
|
||||
const CMD_ACC_WORD: u8 = 3 << 4;
|
||||
const CMD_MODE_RATE: u8 = 2 << 1;
|
||||
|
||||
struct Inner {
|
||||
ch0_data: IoPort<u8>,
|
||||
#[allow(unused)]
|
||||
ch1_data: IoPort<u8>,
|
||||
#[allow(unused)]
|
||||
ch2_data: IoPort<u8>,
|
||||
cmd: IoPort<u8>,
|
||||
|
||||
tick: u64,
|
||||
}
|
||||
|
||||
pub struct I8253 {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
impl MonotonicTimestampProviderDevice for I8253 {
|
||||
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
|
||||
let tick = self.inner.lock().tick;
|
||||
|
||||
Ok(Duration::from_millis(tick))
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for I8253 {
|
||||
fn handle_irq(&self) -> bool {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.tick += 1;
|
||||
|
||||
let now = Duration::from_millis(inner.tick);
|
||||
drop(inner);
|
||||
|
||||
runtime::tick(now);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I8253 {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"i8253 PIT"
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
let inner = self.inner.lock();
|
||||
|
||||
let div: u16 = (FREQUENCY / 1000).try_into().unwrap();
|
||||
|
||||
inner.cmd.write(CMD_CH0 | CMD_ACC_WORD | CMD_MODE_RATE);
|
||||
inner.ch0_data.write(div as u8);
|
||||
inner.ch0_data.write((div >> 8) as u8);
|
||||
|
||||
let irq = IrqNumber::Isa(0);
|
||||
intc.register_irq(irq, Default::default(), self)?;
|
||||
intc.enable_irq(irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl I8253 {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: IrqSafeSpinlock::new(Inner {
|
||||
ch0_data: IoPort::new(0x40),
|
||||
ch1_data: IoPort::new(0x41),
|
||||
ch2_data: IoPort::new(0x42),
|
||||
cmd: IoPort::new(0x43),
|
||||
|
||||
tick: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
//! x86-64 platform peripheral drivers
|
||||
|
||||
pub mod hpet;
|
||||
pub mod i8253;
|
||||
pub mod ps2;
|
||||
pub mod serial;
|
||||
|
Loading…
x
Reference in New Issue
Block a user