acpica: make battery report working for thinkpads

tested on T460p, X201, T470p

by periodically checking and reporting.

Issue #4631
This commit is contained in:
Alexander Boettcher 2022-10-13 09:04:58 +02:00 committed by Christian Helmuth
parent 9079a083d2
commit 00ac4afb9f
5 changed files with 81 additions and 66 deletions

View File

@ -1,4 +1,4 @@
<runtime ram="6M" caps="150" binary="acpica"> <runtime ram="6M" caps="200" binary="acpica">
<requires> <requires>
<io_mem/> <io_mem/>
@ -16,6 +16,6 @@
<rom label="acpica"/> <rom label="acpica"/>
</content> </content>
<config reset="yes" poweroff="yes" report="yes"/> <config reset="yes" poweroff="yes" report="yes" report_period_ms="20000"/>
</runtime> </runtime>

View File

@ -184,7 +184,7 @@ set config {
<start name="acpica" caps="200"> <start name="acpica" caps="200">
<resource name="RAM" quantum="8M"/> <resource name="RAM" quantum="8M"/>
<config ld_verbose="yes" reset="yes" poweroff="yes" report="yes"> <config ld_verbose="yes" reset="yes" poweroff="yes" report="yes" report_period_ms="0">
</config> </config>
<route> <route>
<service name="ROM" label="system"> <child name="dynamic_rom"/> </service> <service name="ROM" label="system"> <child name="dynamic_rom"/> </service>

View File

@ -46,7 +46,7 @@ Excerpt of important parts of the acpica configuration
!<start name="acpica"> !<start name="acpica">
! <!-- <binary name="debug-acpica"/> --> ! <!-- <binary name="debug-acpica"/> -->
! ... ! ...
! <config reset="no" poweroff="no" report="yes"/> ! <config reset="no" poweroff="no" report="yes" report_period_ms="2000"/>
! <route> ! <route>
! <service name="ROM" label="system"> <child name="..."/> </service> ! <service name="ROM" label="system"> <child name="..."/> </service>
! <service name="Report"> <child name="..."/> </service> ! <service name="Report"> <child name="..."/> </service>

View File

@ -80,10 +80,8 @@ class Ec : Acpica::Callback<Ec> {
State::access_t state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); State::access_t state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta);
if (!State::Sci_evt::get(state)) { if (!State::Sci_evt::get(state))
Genode::error("unknown status ", Genode::Hex(state));
return ACPI_REENABLE_GPE; /* gpe is acked and re-enabled */ return ACPI_REENABLE_GPE; /* gpe is acked and re-enabled */
}
ec->ec_cmdsta->outb(ec->ec_port_cmdsta, QR_EC); ec->ec_cmdsta->outb(ec->ec_port_cmdsta, QR_EC);
do { do {

View File

@ -4,7 +4,7 @@
*/ */
/* /*
* Copyright (C) 2016-2017 Genode Labs GmbH * Copyright (C) 2016-2022 Genode Labs GmbH
* *
* This file is part of the Genode OS framework, which is distributed * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -20,6 +20,7 @@
#include <base/attached_rom_dataspace.h> #include <base/attached_rom_dataspace.h>
#include <os/reporter.h> #include <os/reporter.h>
#include <timer_session/connection.h>
#include <util/reconstructible.h> #include <util/reconstructible.h>
#include <util/xml_node.h> #include <util/xml_node.h>
@ -45,14 +46,17 @@ namespace Acpica {
#include "fixed.h" #include "fixed.h"
using namespace Genode;
struct Acpica::Statechange struct Acpica::Statechange
{ {
Genode::Signal_handler<Acpica::Statechange> _dispatcher; Signal_handler<Acpica::Statechange> _dispatcher;
Genode::Attached_rom_dataspace _system_state; Attached_rom_dataspace _system_state;
bool _enable_reset; bool _enable_reset;
bool _enable_poweroff; bool _enable_poweroff;
Statechange(Genode::Env &env, bool reset, bool poweroff) Statechange(Env &env, bool reset, bool poweroff)
: :
_dispatcher(env.ep(), *this, &Statechange::state_changed), _dispatcher(env.ep(), *this, &Statechange::state_changed),
_system_state(env, "system"), _system_state(env, "system"),
@ -69,17 +73,16 @@ struct Acpica::Statechange
if (!_system_state.valid()) return; if (!_system_state.valid()) return;
Genode::Xml_node system(_system_state.local_addr<char>(), Xml_node system(_system_state.local_addr<char>(),
_system_state.size()); _system_state.size());
typedef Genode::String<32> State; typedef String<32> State;
State const state = system.attribute_value("state", State()); State const state = system.attribute_value("state", State());
if (_enable_poweroff && state == "poweroff") { if (_enable_poweroff && state == "poweroff") {
ACPI_STATUS res0 = AcpiEnterSleepStatePrep(5); ACPI_STATUS res0 = AcpiEnterSleepStatePrep(5);
ACPI_STATUS res1 = AcpiEnterSleepState(5); ACPI_STATUS res1 = AcpiEnterSleepState(5);
Genode::error("system poweroff failed - " error("system poweroff failed - res=", Hex(res0), ",", Hex(res1));
"res=", Genode::Hex(res0), ",", Genode::Hex(res1));
return; return;
} }
@ -89,25 +92,29 @@ struct Acpica::Statechange
res = AcpiReset(); res = AcpiReset();
} catch (...) { } } catch (...) { }
Genode::uint64_t const space_addr = AcpiGbl_FADT.ResetRegister.Address; uint64_t const space_addr = AcpiGbl_FADT.ResetRegister.Address;
Genode::error("system reset failed - " error("system reset failed - err=", res,
"err=", res, " " " reset=", !!(AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER),
"reset=", !!(AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER), " " " spaceid=", Hex(AcpiGbl_FADT.ResetRegister.SpaceId),
"spaceid=", Genode::Hex(AcpiGbl_FADT.ResetRegister.SpaceId), " " " addr=", Hex(space_addr));
"addr=", Genode::Hex(space_addr));
} }
} }
}; };
struct Acpica::Main struct Acpica::Main
{ {
Genode::Env &env; Env &env;
Genode::Heap heap { env.ram(), env.rm() }; Heap heap { env.ram(), env.rm() };
Genode::Attached_rom_dataspace config { env, "config" }; Attached_rom_dataspace config { env, "config" };
Genode::Signal_handler<Acpica::Main> sci_irq; Signal_handler<Acpica::Main> sci_irq;
Genode::Constructible<Genode::Irq_connection> sci_conn; Constructible<Irq_connection> sci_conn;
Timer::Connection timer { env };
Signal_handler<Acpica::Main> timer_trigger { env.ep(), *this,
&Main::handle_timer };
Acpica::Reportstate * report { nullptr }; Acpica::Reportstate * report { nullptr };
@ -115,25 +122,25 @@ struct Acpica::Main
unsigned unchanged_state_max; unsigned unchanged_state_max;
static struct Irq_handler { static struct Irq_handler {
UINT32 irq; UINT32 irq;
Genode::Irq_session::Trigger trigger; Irq_session::Trigger trigger;
Genode::Irq_session::Polarity polarity; Irq_session::Polarity polarity;
ACPI_OSD_HANDLER handler;
ACPI_OSD_HANDLER handler; void *context;
void *context;
} irq_handler; } irq_handler;
void init_acpica(); void init_acpica();
Main(Genode::Env &env) Main(Env &env)
: :
env(env), env(env),
sci_irq(env.ep(), *this, &Main::acpi_irq), sci_irq(env.ep(), *this, &Main::acpi_irq),
unchanged_state_max(config.xml().attribute_value("update_unchanged", 20U)) unchanged_state_max(config.xml().attribute_value("update_unchanged", 10U))
{ {
bool const enable_reset = config.xml().attribute_value("reset", false); bool const enable_reset = config.xml().attribute_value("reset", false);
bool const enable_poweroff = config.xml().attribute_value("poweroff", false); bool const enable_poweroff = config.xml().attribute_value("poweroff", false);
bool const enable_report = config.xml().attribute_value("report", false); bool const enable_report = config.xml().attribute_value("report", false);
auto const periodic_ms = config.xml().attribute_value("report_period_ms", 0ULL);
if (enable_report) if (enable_report)
report = new (heap) Acpica::Reportstate(env); report = new (heap) Acpica::Reportstate(env);
@ -148,19 +155,37 @@ struct Acpica::Main
/* setup IRQ */ /* setup IRQ */
if (!irq_handler.handler) { if (!irq_handler.handler) {
Genode::warning("no IRQ handling available"); warning("no IRQ handling available");
return; return;
} }
sci_conn.construct(env, irq_handler.irq, irq_handler.trigger, irq_handler.polarity); sci_conn.construct(env, irq_handler.irq, irq_handler.trigger, irq_handler.polarity);
Genode::log("SCI IRQ: ", irq_handler.irq, log("SCI IRQ: ", irq_handler.irq, " (", irq_handler.trigger, "-",
" (", irq_handler.trigger, "-", irq_handler.polarity, ")"); irq_handler.polarity, ")");
sci_conn->sigh(sci_irq); sci_conn->sigh(sci_irq);
sci_conn->ack_irq(); sci_conn->ack_irq();
if (periodic_ms) {
timer.sigh(timer_trigger);
timer.trigger_periodic(Microseconds(periodic_ms * 1000).value);
}
} }
void handle_timer()
{
if (!irq_handler.handler)
return;
irq_handler.handler(irq_handler.context);
if (report)
report->generate_report(true /* force */);
}
void acpi_irq() void acpi_irq()
{ {
if (!irq_handler.handler) if (!irq_handler.handler)
@ -182,9 +207,6 @@ struct Acpica::Main
unchanged_state_count ++; unchanged_state_count ++;
if (unchanged_state_count >= unchanged_state_max) { if (unchanged_state_count >= unchanged_state_max) {
Genode::log("generate report because of ",
unchanged_state_count, " irqs without state "
"changes");
report->generate_report(true); report->generate_report(true);
unchanged_state_count = 0; unchanged_state_count = 0;
} }
@ -228,31 +250,29 @@ void Acpica::Main::init_acpica()
if (false) { if (false) {
AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES; AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES;
AcpiDbgLayer |= ACPI_TABLES; AcpiDbgLayer |= ACPI_TABLES;
Genode::log("debugging level=", Genode::Hex(AcpiDbgLevel), log("debugging level=", Hex(AcpiDbgLevel),
" layers=", Genode::Hex(AcpiDbgLayer)); " layers=", Hex(AcpiDbgLayer));
} }
ACPI_STATUS status = AcpiInitializeSubsystem(); ACPI_STATUS status = AcpiInitializeSubsystem();
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiInitializeSubsystem failed, status=", status); error("AcpiInitializeSubsystem failed, status=", status);
return; return;
} }
status = AcpiInitializeTables(nullptr, 0, true); status = AcpiInitializeTables(nullptr, 0, true);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiInitializeTables failed, status=", status); error("AcpiInitializeTables failed, status=", status);
return; return;
} }
status = AcpiLoadTables(); status = AcpiLoadTables();
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiLoadTables failed, status=", status); error("AcpiLoadTables failed, status=", status);
return; return;
} }
{ {
using Genode::Irq_session;
/* /*
* ACPI Spec 2.1 General ACPI Terminology * ACPI Spec 2.1 General ACPI Terminology
* *
@ -269,8 +289,6 @@ void Acpica::Main::init_acpica()
ACPI_STATUS status = AcpiGetTable(ACPI_STRING(ACPI_SIG_MADT), 0, (ACPI_TABLE_HEADER **)&madt); ACPI_STATUS status = AcpiGetTable(ACPI_STRING(ACPI_SIG_MADT), 0, (ACPI_TABLE_HEADER **)&madt);
if (status == AE_OK) { if (status == AE_OK) {
using Genode::String;
for_each_element(madt, (ACPI_SUBTABLE_HEADER *) nullptr, for_each_element(madt, (ACPI_SUBTABLE_HEADER *) nullptr,
[&](ACPI_SUBTABLE_HEADER const * const s) { [&](ACPI_SUBTABLE_HEADER const * const s) {
@ -313,89 +331,88 @@ void Acpica::Main::init_acpica()
status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION); status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiEnableSubsystem failed, status=", status); error("AcpiEnableSubsystem failed, status=", status);
return; return;
} }
status = AcpiInitializeObjects(ACPI_NO_DEVICE_INIT); status = AcpiInitializeObjects(ACPI_NO_DEVICE_INIT);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiInitializeObjects (no devices) failed, status=", status); error("AcpiInitializeObjects (no devices) failed, status=", status);
return; return;
} }
/* set APIC mode */ /* set APIC mode */
status = init_pic_mode(); status = init_pic_mode();
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("Setting PIC mode failed, status=", status); error("Setting PIC mode failed, status=", status);
return; return;
} }
/* Embedded controller */ /* Embedded controller */
status = AcpiGetDevices(ACPI_STRING("PNP0C09"), Ec::detect, this, nullptr); status = AcpiGetDevices(ACPI_STRING("PNP0C09"), Ec::detect, this, nullptr);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiGetDevices failed, status=", status); error("AcpiGetDevices failed, status=", status);
return; return;
} }
status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION); status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiInitializeObjects (full init) failed, status=", status); error("AcpiInitializeObjects (full init) failed, status=", status);
return; return;
} }
status = AcpiUpdateAllGpes(); status = AcpiUpdateAllGpes();
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiUpdateAllGpes failed, status=", status); error("AcpiUpdateAllGpes failed, status=", status);
return; return;
} }
status = AcpiEnableAllRuntimeGpes(); status = AcpiEnableAllRuntimeGpes();
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiEnableAllRuntimeGpes failed, status=", status); error("AcpiEnableAllRuntimeGpes failed, status=", status);
return; return;
} }
/* note: ACPI_EVENT_PMTIMER claimed by nova kernel - not usable by us */
Fixed * acpi_fixed = new (heap) Fixed(report); Fixed * acpi_fixed = new (heap) Fixed(report);
status = AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, status = AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
Fixed::handle_power_button, Fixed::handle_power_button,
acpi_fixed); acpi_fixed);
if (status != AE_OK) if (status != AE_OK)
Genode::log("failed - power button registration - error=", status); log("failed - power button registration - error=", status);
status = AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON, status = AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON,
Fixed::handle_sleep_button, Fixed::handle_sleep_button,
acpi_fixed); acpi_fixed);
if (status != AE_OK) if (status != AE_OK)
Genode::log("failed - sleep button registration - error=", status); log("failed - sleep button registration - error=", status);
/* AC Adapters and Power Source Objects */ /* AC Adapters and Power Source Objects */
status = AcpiGetDevices(ACPI_STRING("ACPI0003"), Ac::detect, this, nullptr); status = AcpiGetDevices(ACPI_STRING("ACPI0003"), Ac::detect, this, nullptr);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiGetDevices (ACPI0003) failed, status=", status); error("AcpiGetDevices (ACPI0003) failed, status=", status);
return; return;
} }
/* Smart battery control devices */ /* Smart battery control devices */
status = AcpiGetDevices(ACPI_STRING("PNP0C0A"), Battery::detect, this, nullptr); status = AcpiGetDevices(ACPI_STRING("PNP0C0A"), Battery::detect, this, nullptr);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiGetDevices (PNP0C0A) failed, status=", status); error("AcpiGetDevices (PNP0C0A) failed, status=", status);
return; return;
} }
/* LID device */ /* LID device */
status = AcpiGetDevices(ACPI_STRING("PNP0C0D"), Lid::detect, this, nullptr); status = AcpiGetDevices(ACPI_STRING("PNP0C0D"), Lid::detect, this, nullptr);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiGetDevices (PNP0C0D) failed, status=", status); error("AcpiGetDevices (PNP0C0D) failed, status=", status);
return; return;
} }
/* Fujitsu HID device */ /* Fujitsu HID device */
status = AcpiGetDevices(ACPI_STRING("FUJ02E3"), Fuj02e3::detect, this, nullptr); status = AcpiGetDevices(ACPI_STRING("FUJ02E3"), Fuj02e3::detect, this, nullptr);
if (status != AE_OK) { if (status != AE_OK) {
Genode::error("AcpiGetDevices (FUJ02E3) failed, status=", status); error("AcpiGetDevices (FUJ02E3) failed, status=", status);
return; return;
} }
} }
@ -408,7 +425,7 @@ ACPI_STATUS AcpiOsInstallInterruptHandler(UINT32 irq, ACPI_OSD_HANDLER handler,
void *context) void *context)
{ {
if (irq != Acpica::Main::irq_handler.irq) { if (irq != Acpica::Main::irq_handler.irq) {
Genode::error("SCI interrupt is ", Acpica::Main::irq_handler.irq, error("SCI interrupt is ", Acpica::Main::irq_handler.irq,
" but library requested ", irq); " but library requested ", irq);
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
} }
@ -419,7 +436,7 @@ ACPI_STATUS AcpiOsInstallInterruptHandler(UINT32 irq, ACPI_OSD_HANDLER handler,
} }
void Component::construct(Genode::Env &env) void Component::construct(Env &env)
{ {
/* XXX execute constructors of global statics */ /* XXX execute constructors of global statics */
env.exec_static_constructors(); env.exec_static_constructors();