x86: fix hpet on ThinkPad T430 by enabling FSB delivery
This commit is contained in:
parent
a0cdc39f30
commit
975df985ac
@ -5,7 +5,9 @@ use acpi::HpetInfo;
|
||||
use alloc::{collections::btree_map::BTreeMap, format, string::String, sync::Arc};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, Irq, IrqOptions, IrqTrigger, IrqVector},
|
||||
interrupt::{
|
||||
InterruptAffinity, InterruptHandler, Irq, IrqOptions, IrqTrigger, IrqVector, MsiInfo,
|
||||
},
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, task::runtime, time};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
@ -16,7 +18,7 @@ use tock_registers::{
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
use crate::arch::x86::dummy_init_context;
|
||||
use crate::arch::{x86::dummy_init_context, x86_64::X86_64};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
@ -49,7 +51,11 @@ register_bitfields! {
|
||||
Edge = 0,
|
||||
Level = 1,
|
||||
],
|
||||
]
|
||||
],
|
||||
Tn_FSB_ROUTE [
|
||||
Tn_FSB_INT_VAL OFFSET(0) NUMBITS(32) [],
|
||||
Tn_FSB_INT_ADDR OFFSET(32) NUMBITS(32) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
@ -72,7 +78,7 @@ register_structs! {
|
||||
TimerRegs {
|
||||
(0x00 => Tn_CONFIG: ReadWrite<u64, Tn_CONFIG::Register>),
|
||||
(0x08 => Tn_COMPARATOR: ReadWrite<u64>),
|
||||
(0x10 => Tn_FSB_ROUTE: ReadWrite<u64>),
|
||||
(0x10 => Tn_FSB_ROUTE: ReadWrite<u64, Tn_FSB_ROUTE::Register>),
|
||||
(0x18 => _0),
|
||||
(0x20 => @END),
|
||||
}
|
||||
@ -136,12 +142,20 @@ impl Device for HpetTimer {
|
||||
|
||||
let regs = self.regs.write();
|
||||
|
||||
if regs.is_fsb_capable() {
|
||||
log::warn!("TODO: HPET supports FSB interrupt delivery, but the kernel doesn't yet");
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let irq = if regs.is_fsb_capable() {
|
||||
let lapic = X86_64::cpu_local_apic();
|
||||
let msi_info = lapic.register_msi(InterruptAffinity::Any, self.clone())?;
|
||||
log::info!(
|
||||
"Set up {:?}, period={}t, periodic={}, msi mode",
|
||||
self.name,
|
||||
self.period,
|
||||
self.periodic
|
||||
);
|
||||
|
||||
IrqDestination::Fsb(msi_info)
|
||||
} else {
|
||||
let irq = regs.select_irq().ok_or(Error::InvalidArgument)?;
|
||||
|
||||
log::info!(
|
||||
"Set up {:?}, period={}t, periodic={}, irq={:?}",
|
||||
self.name,
|
||||
@ -159,7 +173,9 @@ impl Device for HpetTimer {
|
||||
self.clone(),
|
||||
)?;
|
||||
intc.enable_irq(irq)?;
|
||||
let irq = IrqDestination::Irq(irq);
|
||||
|
||||
IrqDestination::Irq(irq)
|
||||
};
|
||||
|
||||
let now = self.parent.read_counter();
|
||||
|
||||
@ -216,6 +232,8 @@ impl HpetTimer {
|
||||
let periodic = regs.is_periodic();
|
||||
let bits64 = regs.is_64_bit();
|
||||
|
||||
log::info!("hpet{index}: periodic={periodic}, bits64={bits64}, period={period}");
|
||||
|
||||
let timer = Arc::new(HpetTimer {
|
||||
name: format!("hpet{index}"),
|
||||
regs: IrqSafeRwLock::new(regs),
|
||||
@ -235,14 +253,42 @@ impl Hpet {
|
||||
fn new(base: PhysicalAddress) -> Result<Self, Error> {
|
||||
let regs = unsafe { DeviceMemoryIo::<GeneralRegs>::map(base, Default::default()) }?;
|
||||
let base_period = regs.timer_period();
|
||||
// qemu: 100000000
|
||||
// t430: 14318179
|
||||
let base_frequency = FS_IN_S / base_period;
|
||||
Ok(Self {
|
||||
|
||||
let hpet = Self {
|
||||
base,
|
||||
base_period,
|
||||
base_frequency,
|
||||
regs: IrqSafeRwLock::new(regs),
|
||||
timers: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
})
|
||||
};
|
||||
|
||||
hpet.test_timer_counts()?;
|
||||
|
||||
Ok(hpet)
|
||||
}
|
||||
|
||||
fn test_timer_counts(&self) -> Result<(), Error> {
|
||||
let regs = self.regs.write();
|
||||
regs.stop();
|
||||
for _ in 0..10000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let start = regs.counter();
|
||||
regs.start();
|
||||
for _ in 0..100000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
regs.stop();
|
||||
let now = regs.counter();
|
||||
|
||||
if now > start {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_from_acpi(acpi: &HpetInfo) -> Result<Arc<Self>, Error> {
|
||||
@ -332,14 +378,28 @@ impl TimerRegs {
|
||||
}
|
||||
|
||||
fn enable(&self, irq: IrqDestination) {
|
||||
let config = match irq {
|
||||
match irq {
|
||||
IrqDestination::Irq(Irq::External(irq)) => {
|
||||
Tn_CONFIG::Tn_INT_ROUTE_CNF.val(irq as u64) + Tn_CONFIG::Tn_INT_TYPE_CNF::Edge
|
||||
self.Tn_CONFIG.modify(
|
||||
Tn_CONFIG::Tn_INT_ROUTE_CNF.val(irq as u64)
|
||||
+ Tn_CONFIG::Tn_INT_TYPE_CNF::Edge
|
||||
+ Tn_CONFIG::Tn_FSB_EN_CNF::CLEAR,
|
||||
);
|
||||
}
|
||||
IrqDestination::Fsb(MsiInfo { address, value, .. }) => {
|
||||
let address: u32 = address
|
||||
.try_into()
|
||||
.expect("FIXME: non-32-bit LAPIC MSI address");
|
||||
self.Tn_CONFIG
|
||||
.modify(Tn_CONFIG::Tn_INT_TYPE_CNF::Edge + Tn_CONFIG::Tn_FSB_EN_CNF::SET);
|
||||
self.Tn_FSB_ROUTE.write(
|
||||
Tn_FSB_ROUTE::Tn_FSB_INT_VAL.val(value as u64)
|
||||
+ Tn_FSB_ROUTE::Tn_FSB_INT_ADDR.val(address as u64),
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
self.Tn_CONFIG.modify(config);
|
||||
self.Tn_CONFIG.modify(Tn_CONFIG::Tn_INT_ENB_CNF::SET);
|
||||
}
|
||||
|
||||
@ -396,4 +456,5 @@ impl GeneralRegs {
|
||||
|
||||
enum IrqDestination {
|
||||
Irq(Irq),
|
||||
Fsb(MsiInfo),
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use kernel_arch_x86_64::{
|
||||
table::{PageAttributes, PageEntry, PageTable, L1, L2, L3},
|
||||
EarlyMapping, MEMORY_LIMIT, RAM_MAPPING_L1, RAM_MAPPING_OFFSET,
|
||||
},
|
||||
PerCpuData,
|
||||
LocalApicInterface, PerCpuData,
|
||||
};
|
||||
use libk::{
|
||||
arch::Cpu,
|
||||
@ -471,6 +471,10 @@ impl X86_64 {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn cpu_local_apic() -> Arc<dyn LocalApicInterface> {
|
||||
Cpu::local().local_apic.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl InitrdSource for LoadProtocolV1 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user