acpid: general GPE _Lxx/_Exx dispatch + ACPI PM timer (ACPICA port)
- GpeBlocks::enabled_active_gpes(): scans all GPE block status+enable
register pairs, returns every GPE with both bits set (evgpe detect
semantics — a GPE only fires the SCI when enabled AND active).
- handle_sci general GPE dispatch (acpi_ev_gpe_detect): every
enabled+active GPE that is not the EC GPE gets its \_GPE._L{gpe:02X}
(level, tried first) or \_GPE._E{gpe:02X} (edge) control method
evaluated; status cleared after. This is the ACPICA core GPE model
that drives lid, device wake, and all non-EC GPE events — previously
only the EC GPE and PM1 fixed events were dispatched.
- pm_timer_read(): reads the FADT pm_timer_block free-running counter
(3.579545 MHz, PM_TIMER_FREQUENCY_HZ). Exposed at
/scheme/acpi/pmtimer for precise sleep-path timing.
This commit is contained in:
@@ -12,6 +12,16 @@ use common::io::{Io, Pio};
|
||||
|
||||
use crate::acpi::FadtStruct;
|
||||
|
||||
pub const PM_TIMER_FREQUENCY_HZ: u64 = 3_579_545;
|
||||
|
||||
pub fn pm_timer_read(fadt: &FadtStruct) -> Option<u32> {
|
||||
let port = fadt.pm_timer_block as u16;
|
||||
if fadt.pm_timer_block == 0 || fadt.pm_timer_length == 0 {
|
||||
return None;
|
||||
}
|
||||
Some(Pio::<u32>::new(port).read())
|
||||
}
|
||||
|
||||
// PM1 fixed-event bits in PM1_STS/PM1_EN (ACPI 6.4 §4.8.3.1).
|
||||
pub const PM1_PWRBTN: u16 = 1 << 8;
|
||||
pub const PM1_SLPBTN: u16 = 1 << 9;
|
||||
@@ -129,6 +139,28 @@ impl GpeBlocks {
|
||||
Pio::<u8>::new(port).write(1 << bit);
|
||||
}
|
||||
|
||||
/// GPEs with both status and enable bits set (evgpe detect semantics).
|
||||
pub fn enabled_active_gpes(&self) -> Vec<u8> {
|
||||
let mut out = Vec::new();
|
||||
for block in [self.gpe0, self.gpe1].into_iter().flatten() {
|
||||
let half = block.len / 2;
|
||||
for byte in 0..half {
|
||||
let status_port = block.port + byte as u16;
|
||||
let enable_port = block.port + (half as u16) + byte as u16;
|
||||
let active = Pio::<u8>::new(status_port).read() & Pio::<u8>::new(enable_port).read();
|
||||
if active == 0 {
|
||||
continue;
|
||||
}
|
||||
for bit in 0..8 {
|
||||
if active & (1 << bit) != 0 {
|
||||
out.push(block.base + byte * 8 + bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// PM1 fixed-event status register (16-bit across the a/b blocks).
|
||||
pub fn pm1_status(&self) -> u16 {
|
||||
let mut value = 0u16;
|
||||
|
||||
@@ -237,6 +237,28 @@ pub fn handle_sci(context: &AcpiContext) -> Vec<PowerEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
// General GPE dispatch (evgpe.c acpi_ev_gpe_detect): non-EC enabled+active
|
||||
// GPEs get \_GPE._Lxx (level, tried first) or \_GPE._Exx (edge) evaluated.
|
||||
let ec_gpe = context.ec_device.read().clone().map(|(_, g)| g);
|
||||
for gpe in blocks.enabled_active_gpes() {
|
||||
if Some(gpe) == ec_gpe {
|
||||
continue;
|
||||
}
|
||||
let level = format!("\\_GPE._L{gpe:02X}");
|
||||
let edge = format!("\\_GPE._E{gpe:02X}");
|
||||
let handled = context.evaluate_acpi_method(&level, "", &[]).is_ok()
|
||||
|| context.evaluate_acpi_method(&edge, "", &[]).is_ok();
|
||||
if handled {
|
||||
log::info!("acpid: GPE {:#04x} dispatched to AML control method", gpe);
|
||||
} else {
|
||||
log::debug!(
|
||||
"acpid: GPE {:#04x} active but no _L{gpe:02X}/_E{gpe:02X} method",
|
||||
gpe
|
||||
);
|
||||
}
|
||||
blocks.clear_gpe(gpe);
|
||||
}
|
||||
|
||||
// Drain AML notifications (from _Qxx methods and any other Notify).
|
||||
let lid_path = context.lid_device.read().clone();
|
||||
for (device, value) in context.notifications().drain() {
|
||||
|
||||
@@ -18,7 +18,7 @@ use syscall::FobtainFdFlags;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{Error, Result};
|
||||
use syscall::error::{EACCES, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
|
||||
use syscall::error::{EACCES, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENODEV, ENOENT, ENOTDIR};
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK};
|
||||
use syscall::{EOVERFLOW, EPERM};
|
||||
@@ -67,6 +67,7 @@ enum HandleKind<'a> {
|
||||
/// listing is empty.
|
||||
Thermal,
|
||||
ThermalZone { zone: String, kind: ThermalFileKind },
|
||||
PmTimer,
|
||||
/// `/scheme/acpi/power` -- entries are PowerResource objects in
|
||||
/// the AML namespace. On laptops these are AC adapters and
|
||||
/// battery controllers. On desktops and QEMU the listing is
|
||||
@@ -153,6 +154,7 @@ impl HandleKind<'_> {
|
||||
Self::DmiField(_) => false,
|
||||
Self::ProcFile { .. } => false,
|
||||
Self::LidState | Self::Button { .. } | Self::Notifications => false,
|
||||
Self::PmTimer => false,
|
||||
Self::Lid | Self::ButtonDir => true,
|
||||
Self::Fan => true,
|
||||
Self::FanState(_) | Self::FanSpeed(_) => false,
|
||||
@@ -182,6 +184,7 @@ impl HandleKind<'_> {
|
||||
Self::Notifications | Self::Lid | Self::ButtonDir | Self::Fan => 0,
|
||||
Self::FanState(_) | Self::FanSpeed(_) => 4,
|
||||
Self::ThermalZone { .. } => 16,
|
||||
Self::PmTimer => 16,
|
||||
// Directories
|
||||
Self::TopLevel | Self::Symbols(_) | Self::Tables => 0,
|
||||
Self::Thermal | Self::Power | Self::Processor | Self::DmiDir => 0,
|
||||
@@ -479,6 +482,7 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
}
|
||||
|
||||
["lid"] => HandleKind::Lid,
|
||||
["pmtimer"] => HandleKind::PmTimer,
|
||||
["lid", "state"] => HandleKind::LidState,
|
||||
["button"] => HandleKind::ButtonDir,
|
||||
["button", "power"] => HandleKind::Button { power: true },
|
||||
@@ -692,6 +696,12 @@ impl SchemeSync for AcpiScheme<'_, '_> {
|
||||
dmi_buf = format!("{}\n", raw);
|
||||
dmi_buf.as_bytes()
|
||||
}
|
||||
HandleKind::PmTimer => {
|
||||
let fadt = self.ctx.fadt().ok_or(Error::new(ENODEV))?;
|
||||
let value = crate::gpe::pm_timer_read(fadt).ok_or(Error::new(ENODEV))?;
|
||||
dmi_buf = format!("{value}\n");
|
||||
dmi_buf.as_bytes()
|
||||
}
|
||||
HandleKind::Processor | HandleKind::DmiDir | HandleKind::Thermal | HandleKind::Power | HandleKind::PowerBatteries | HandleKind::Symbols(_) | HandleKind::RegisterPci | HandleKind::TopLevel | HandleKind::SchemeRoot | HandleKind::Lid | HandleKind::ButtonDir | HandleKind::Fan => {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user