Initial embedded controller support
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use acpi::aml::object::{Object, WrappedObject};
|
||||
use acpi::aml::op_region::{RegionHandler, RegionSpace};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::error::Error;
|
||||
@@ -243,14 +244,16 @@ pub struct AmlSymbols {
|
||||
// k = name, v = description
|
||||
symbol_cache: FxHashMap<String, String>,
|
||||
page_cache: Arc<Mutex<AmlPageCache>>,
|
||||
aml_region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
}
|
||||
|
||||
impl AmlSymbols {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(aml_region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler>)>) -> Self {
|
||||
Self {
|
||||
aml_context: None,
|
||||
symbol_cache: FxHashMap::default(),
|
||||
page_cache: Arc::new(Mutex::new(AmlPageCache::default())),
|
||||
aml_region_handlers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +269,9 @@ impl AmlSymbols {
|
||||
unsafe { AcpiTables::from_rsdp(handler.clone(), rsdp_address).map_err(format_err)? };
|
||||
let platform = AcpiPlatform::new(tables, handler).map_err(format_err)?;
|
||||
let interpreter = Interpreter::new_from_platform(&platform).map_err(format_err)?;
|
||||
for (region, handler) in self.aml_region_handlers.drain(..) {
|
||||
interpreter.install_region_handler(region, handler);
|
||||
}
|
||||
self.aml_context = Some(interpreter);
|
||||
Ok(())
|
||||
}
|
||||
@@ -412,7 +418,10 @@ impl AcpiContext {
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn init(rxsdt_physaddrs: impl Iterator<Item = u64>) -> Self {
|
||||
pub fn init(
|
||||
rxsdt_physaddrs: impl Iterator<Item = u64>,
|
||||
ec: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
) -> Self {
|
||||
let tables = rxsdt_physaddrs
|
||||
.map(|physaddr| {
|
||||
let physaddr: usize = physaddr
|
||||
@@ -431,7 +440,7 @@ impl AcpiContext {
|
||||
fadt: None,
|
||||
|
||||
// Temporary values
|
||||
aml_symbols: RwLock::new(AmlSymbols::new()),
|
||||
aml_symbols: RwLock::new(AmlSymbols::new(ec)),
|
||||
|
||||
next_ctx: RwLock::new(0),
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use acpi::aml::{
|
||||
op_region::{OpRegion, RegionHandler, RegionSpace},
|
||||
AmlError,
|
||||
};
|
||||
use common::{
|
||||
io::{Io, Pio},
|
||||
timeout::Timeout,
|
||||
};
|
||||
use log::*;
|
||||
|
||||
const EC_DATA: u16 = 0x62;
|
||||
const EC_SC: u16 = 0x66;
|
||||
|
||||
const OBF: u8 = 1 << 0; // output full / data ready for host <> empty
|
||||
const IBF: u8 = 1 << 1; // input full / data ready for ec <> empty
|
||||
const CMD: u8 = 1 << 3; // byte in data reg is command <> data
|
||||
const BURST: u8 = 1 << 4; // burst mode <> normal mode
|
||||
const SCI_EVT: u8 = 1 << 5; // sci event pending <> not
|
||||
const SMI_EVT: u8 = 1 << 6; // smi event pending <> not
|
||||
|
||||
const RD_EC: u8 = 0x80;
|
||||
const WR_EC: u8 = 0x81;
|
||||
const BE_EC: u8 = 0x82;
|
||||
const BD_EC: u8 = 0x83;
|
||||
const QR_EC: u8 = 0x84;
|
||||
|
||||
const BURST_ACK: u8 = 0x90;
|
||||
|
||||
pub const DEFAULT_EC_TIMEOUT: Duration = Duration::from_millis(10);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ScBits(u8);
|
||||
#[allow(dead_code)]
|
||||
impl ScBits {
|
||||
const fn obf(&self) -> bool {
|
||||
(self.0 & OBF) != 0
|
||||
}
|
||||
const fn ibf(&self) -> bool {
|
||||
(self.0 & IBF) != 0
|
||||
}
|
||||
const fn cmd(&self) -> bool {
|
||||
(self.0 & CMD) != 0
|
||||
}
|
||||
const fn burst(&self) -> bool {
|
||||
(self.0 & BURST) != 0
|
||||
}
|
||||
const fn sci_evt(&self) -> bool {
|
||||
(self.0 & SCI_EVT) != 0
|
||||
}
|
||||
const fn smi_evt(&self) -> bool {
|
||||
(self.0 & SMI_EVT) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Ec {
|
||||
sc: u16,
|
||||
data: u16,
|
||||
|
||||
timeout: Duration,
|
||||
}
|
||||
impl Ec {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sc: EC_SC,
|
||||
data: EC_DATA,
|
||||
timeout: DEFAULT_EC_TIMEOUT,
|
||||
}
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn with_address(sc: u16, data: u16, timeout: Duration) -> Self {
|
||||
Self { sc, data, timeout }
|
||||
}
|
||||
#[inline]
|
||||
fn read_reg_sc(&self) -> ScBits {
|
||||
ScBits(Pio::<u8>::new(self.sc).read())
|
||||
}
|
||||
#[inline]
|
||||
fn read_reg_data(&self) -> u8 {
|
||||
Pio::<u8>::new(self.data).read()
|
||||
}
|
||||
#[inline]
|
||||
fn write_reg_sc(&self, value: u8) {
|
||||
Pio::<u8>::new(self.sc).write(value);
|
||||
}
|
||||
#[inline]
|
||||
fn write_reg_data(&self, value: u8) {
|
||||
Pio::<u8>::new(self.data).write(value);
|
||||
}
|
||||
#[inline]
|
||||
fn wait_for_write_ready(&self) -> Option<()> {
|
||||
let timeout = Timeout::new(self.timeout);
|
||||
loop {
|
||||
if !self.read_reg_sc().ibf() {
|
||||
return Some(());
|
||||
}
|
||||
timeout.run().ok()?;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn wait_for_read_ready(&self) -> Option<()> {
|
||||
let timeout = Timeout::new(self.timeout);
|
||||
loop {
|
||||
if self.read_reg_sc().obf() {
|
||||
return Some(());
|
||||
}
|
||||
timeout.run().ok()?;
|
||||
}
|
||||
}
|
||||
|
||||
//https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/12_ACPI_Embedded_Controller_Interface_Specification/embedded-controller-command-set.html
|
||||
pub fn read(&self, address: u8) -> Option<u8> {
|
||||
trace!("ec read addr: {:x}", address);
|
||||
self.wait_for_write_ready()?;
|
||||
|
||||
self.write_reg_sc(RD_EC);
|
||||
|
||||
self.wait_for_write_ready()?;
|
||||
|
||||
self.write_reg_data(address);
|
||||
|
||||
self.wait_for_read_ready()?;
|
||||
|
||||
let val = self.read_reg_data();
|
||||
trace!("got: {:x}", val);
|
||||
Some(val)
|
||||
}
|
||||
pub fn write(&self, address: u8, value: u8) -> Option<()> {
|
||||
trace!("ec write addr: {:x}, with: {:x}", address, value);
|
||||
self.wait_for_write_ready()?;
|
||||
|
||||
self.write_reg_sc(WR_EC);
|
||||
|
||||
self.wait_for_write_ready()?;
|
||||
|
||||
self.write_reg_data(address);
|
||||
|
||||
self.wait_for_write_ready()?;
|
||||
|
||||
self.write_reg_data(value);
|
||||
trace!("done");
|
||||
Some(())
|
||||
}
|
||||
// disabled if not met
|
||||
// First Access - 400 microseconds
|
||||
// Subsequent Accesses - 50 microseconds each
|
||||
// Total Burst Time - 1 millisecond
|
||||
//Accesses should be responded to within 50 microseconds.
|
||||
#[allow(dead_code)]
|
||||
fn enable_burst(&self) -> bool {
|
||||
trace!("ec burst enable");
|
||||
self.wait_for_write_ready();
|
||||
|
||||
self.write_reg_sc(BE_EC);
|
||||
|
||||
self.wait_for_read_ready();
|
||||
|
||||
let res = self.read_reg_data() == BURST_ACK;
|
||||
trace!("success: {}", res);
|
||||
res
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn disable_burst(&self) {
|
||||
trace!("ec burst disable");
|
||||
self.wait_for_write_ready();
|
||||
self.write_reg_sc(BD_EC);
|
||||
trace!("done");
|
||||
}
|
||||
//OSPM driver sends this command when the SCI_EVT flag in the EC_SC register is set.
|
||||
#[allow(dead_code)]
|
||||
fn queue_query(&mut self) -> u8 {
|
||||
trace!("ec query");
|
||||
self.wait_for_write_ready();
|
||||
|
||||
self.write_reg_sc(QR_EC);
|
||||
|
||||
self.wait_for_read_ready();
|
||||
|
||||
let val = self.read_reg_data();
|
||||
trace!("got: {}", val);
|
||||
val
|
||||
}
|
||||
}
|
||||
impl RegionHandler for Ec {
|
||||
fn read_u8(
|
||||
&self,
|
||||
region: &acpi::aml::op_region::OpRegion,
|
||||
offset: usize,
|
||||
) -> Result<u8, acpi::aml::AmlError> {
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
self.read(offset as u8).ok_or(AmlError::MutexAcquireTimeout) // TODO proper error type
|
||||
}
|
||||
fn write_u8(
|
||||
&self,
|
||||
region: &OpRegion,
|
||||
offset: usize,
|
||||
value: u8,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
assert_eq!(region.space, RegionSpace::EmbeddedControl);
|
||||
self.write(offset as u8, value)
|
||||
.ok_or(AmlError::MutexAcquireTimeout) // TODO proper error type
|
||||
}
|
||||
fn read_u16(&self, _region: &OpRegion, _offset: usize) -> Result<u16, acpi::aml::AmlError> {
|
||||
warn!("Got u16 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
fn read_u32(&self, _region: &OpRegion, _offset: usize) -> Result<u32, acpi::aml::AmlError> {
|
||||
warn!("Got u32 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
fn read_u64(&self, _region: &OpRegion, _offset: usize) -> Result<u64, acpi::aml::AmlError> {
|
||||
warn!("Got u64 EC read from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
fn write_u16(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u16,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u16 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
fn write_u32(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u32,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u32 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
fn write_u64(
|
||||
&self,
|
||||
_region: &OpRegion,
|
||||
_offset: usize,
|
||||
_value: u64,
|
||||
) -> Result<(), acpi::aml::AmlError> {
|
||||
warn!("Got u64 EC write from AML!");
|
||||
Err(acpi::aml::AmlError::NoHandlerForRegionAccess(
|
||||
RegionSpace::EmbeddedControl,
|
||||
)) // TODO proper error type
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,15 @@ use std::mem;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::acpi::aml::op_region::{RegionHandler, RegionSpace};
|
||||
use event::{EventFlags, RawEventQueue};
|
||||
use redox_scheme::{RequestKind, SignalBehavior, Socket};
|
||||
use syscall::{EAGAIN, EWOULDBLOCK};
|
||||
|
||||
mod acpi;
|
||||
mod aml_physmem;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
mod ec;
|
||||
|
||||
mod scheme;
|
||||
|
||||
@@ -61,7 +64,11 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
_ => panic!("acpid: expected [RX]SDT from kernel to be either of those"),
|
||||
};
|
||||
|
||||
let acpi_context = self::acpi::AcpiContext::init(physaddrs_iter);
|
||||
let region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler + 'static>)> = vec![
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
(RegionSpace::EmbeddedControl, Box::new(ec::Ec::new())),
|
||||
];
|
||||
let acpi_context = self::acpi::AcpiContext::init(physaddrs_iter, region_handlers);
|
||||
|
||||
// TODO: I/O permission bitmap?
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
|
||||
Reference in New Issue
Block a user