acpi_ged: New driver to ACPI generic event device

New driver to ACPI generic event device, defined in ACPI spec.
Some ACPI power button may not work without this.

In qemu arm64 with "virt" machine, with ACPI firmware,
enable devd check devd message by
and invoke following command in qemu monitor
(qemu) system_powerdown
and make sure some power button input event appear.
(setting sysctl hw.acpi.power_button_state=S5 is not work,
because ACPI tree does not have \_S5 object.)

Reviewed by: andrew, hrs
Differential Revision: https://reviews.freebsd.org/D37032
This commit is contained in:
Takanori Watanabe 2022-10-18 14:41:53 +09:00
parent 22893e5840
commit a9880bfe11
6 changed files with 345 additions and 1 deletions

64
share/man/man4/acpi_ged.4 Normal file
View File

@ -0,0 +1,64 @@
.\" Copyright (c) 2022 Takanori Watanabe
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd October 18, 2022
.Dt ACPI_GED 4
.Os
.Sh NAME
.Nm acpi_ged
.Nd "ACPI Generic Event Device"
.Sh SYNOPSIS
To compile this driver into the kernel,
place the following line in your
kernel configuration file:
.Bd -ragged -offset indent
.Cd "device acpi_ged"
.Ed
.Pp
Alternatively, to load the driver as a
module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
acpi_ged_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides support for generic events interface.
This handles interrupts and evaluates the specific ACPI method.
This may generate optionally ACPI notify for another device.
.Sh SEE ALSO
.Xr acpi 4
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx 14.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm
driver was written by
.An Takanori Watanabe Aq Mt takawata@FreeBSD.org

View File

@ -24,3 +24,4 @@ device vtnet # VirtIO Ethernet device
options FDT
device acpi
device acpi_ged

View File

@ -806,6 +806,7 @@ dev/acpica/acpi_button.c optional acpi
dev/acpica/acpi_cmbat.c optional acpi
dev/acpica/acpi_cpu.c optional acpi
dev/acpica/acpi_ec.c optional acpi
dev/acpica/acpi_ged.c optional acpi_ged acpi
dev/acpica/acpi_isab.c optional acpi isa
dev/acpica/acpi_lid.c optional acpi
dev/acpica/acpi_package.c optional acpi

267
sys/dev/acpica/acpi_ged.c Normal file
View File

@ -0,0 +1,267 @@
/*-
* Copyright (c) 2022 Takanori Watanabe
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_acpi.h"
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <contrib/dev/acpica/include/accommon.h>
#include <dev/acpica/acpivar.h>
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_GED
ACPI_MODULE_NAME("GED")
static MALLOC_DEFINE(M_ACPIGED, "acpiged", "ACPI Generic event data");
struct acpi_ged_event {
device_t dev;
struct resource *r;
int rid;
void *cookie;
ACPI_HANDLE ah;
ACPI_OBJECT_LIST args;
ACPI_OBJECT arg1;
};
struct acpi_ged_softc {
int numevts;
struct acpi_ged_event *evts;
};
static int acpi_ged_probe(device_t dev);
static int acpi_ged_attach(device_t dev);
static int acpi_ged_detach(device_t dev);
static char *ged_ids[] = { "ACPI0013", NULL };
static device_method_t acpi_ged_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, acpi_ged_probe),
DEVMETHOD(device_attach, acpi_ged_attach),
DEVMETHOD(device_detach, acpi_ged_detach),
DEVMETHOD_END
};
static driver_t acpi_ged_driver = {
"acpi_ged",
acpi_ged_methods,
sizeof(struct acpi_ged_softc),
};
DRIVER_MODULE(acpi_ged, acpi, acpi_ged_driver, 0, 0);
MODULE_DEPEND(acpi_ged, acpi, 1, 1, 1);
static void
acpi_ged_evt(void *arg)
{
struct acpi_ged_event *evt = arg;
AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
}
static void
acpi_ged_intr(void *arg)
{
AcpiOsExecute(OSL_GPE_HANDLER, acpi_ged_evt, arg);
}
static int
acpi_ged_probe(device_t dev)
{
int rv;
if (acpi_disabled("ged"))
return (ENXIO);
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ged_ids, NULL);
if (rv > 0)
return (ENXIO);
device_set_desc(dev, "Generic Event Device");
return (rv);
}
/*this should be in acpi_resource.*/
static int
acpi_get_trigger(ACPI_RESOURCE *res)
{
int trig;
switch (res->Type) {
case ACPI_RESOURCE_TYPE_IRQ:
KASSERT(res->Data.Irq.InterruptCount == 1,
("%s: multiple interrupts", __func__));
trig = res->Data.Irq.Triggering;
break;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
KASSERT(res->Data.ExtendedIrq.InterruptCount == 1,
("%s: multiple interrupts", __func__));
trig = res->Data.ExtendedIrq.Triggering;
break;
default:
panic("%s: bad resource type %u", __func__, res->Type);
}
return (trig == ACPI_EDGE_SENSITIVE)
? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
}
static int
acpi_ged_attach(device_t dev)
{
struct acpi_ged_softc *sc = device_get_softc(dev);
struct resource_list *rl;
struct resource_list_entry *rle;
ACPI_RESOURCE ares;
ACPI_HANDLE evt_method;
int i;
int rawirq, trig;
char name[] = "_Xnn";
ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(dev), "_EVT",
&evt_method))) {
device_printf(dev, "_EVT not found\n");
evt_method = NULL;
}
rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
STAILQ_FOREACH(rle, rl, link) {
if (rle->type == SYS_RES_IRQ) {
sc->numevts++;
}
}
sc->evts = mallocarray(sc->numevts, sizeof(*sc->evts), M_ACPIGED,
M_WAITOK | M_ZERO);
for (i = 0; i < sc->numevts; i++) {
sc->evts[i].dev = dev;
sc->evts[i].rid = i;
sc->evts[i].r = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->evts[i].rid, RF_ACTIVE | RF_SHAREABLE);
if (sc->evts[i].r == NULL) {
device_printf(dev, "Cannot alloc %dth irq\n", i);
continue;
}
#ifdef INTRNG
{
struct intr_map_data_acpi *ima;
ima = rman_get_virtual(sc->evts[i].r);
if (ima == NULL) {
device_printf(dev, "map not found"
" non-intrng?\n");
rawirq = rman_get_start(sc->evts[i].r);
trig = INTR_TRIGGER_LEVEL;
if (ACPI_SUCCESS(acpi_lookup_irq_resource
(dev, sc->evts[i].rid,
sc->evts[i].r, &ares))) {
trig = acpi_get_trigger(&ares);
}
} else if (ima->hdr.type == INTR_MAP_DATA_ACPI) {
device_printf(dev, "Raw IRQ %d\n", ima->irq);
rawirq = ima->irq;
trig = ima->trig;
} else {
device_printf(dev, "Not supported intr"
" type%d\n", ima->hdr.type);
continue;
}
}
#else
rawirq = rman_get_start(sc->evt[i].r);
trig = INTR_TRIGGER_LEVEL;
if (ACPI_SUCCESS(acpi_lookup_irq_resource
(dev, sc->evts[i].rid,
sc->evts[i].r, &ares))) {
trig = acpi_get_trigger(&ares);
}
#endif
if (rawirq < 0x100) {
sprintf(name, "_%c%02X",
((trig == INTR_TRIGGER_EDGE) ? 'E' : 'L'),
rawirq);
if (ACPI_SUCCESS(AcpiGetHandle
(acpi_get_handle(dev),
name, &sc->evts[i].ah))) {
sc->evts[i].args.Count = 0; /* ensure */
} else {
sc->evts[i].ah = NULL; /* ensure */
}
}
if (sc->evts[i].ah == NULL) {
if (evt_method != NULL) {
sc->evts[i].ah = evt_method;
sc->evts[i].arg1.Type = ACPI_TYPE_INTEGER;
sc->evts[i].arg1.Integer.Value = rawirq;
sc->evts[i].args.Count = 1;
sc->evts[i].args.Pointer = &sc->evts[i].arg1;
} else{
device_printf
(dev,
"Cannot find handler method %d\n",
i);
continue;
}
}
if (bus_setup_intr(dev, sc->evts[i].r,
INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr,
&sc->evts[i], &sc->evts[i].cookie) != 0) {
device_printf(dev, "Failed to setup intr %d\n", i);
}
}
return_VALUE(0);
}
static int
acpi_ged_detach(device_t dev)
{
struct acpi_ged_softc *sc = device_get_softc(dev);
int i;
for (i = 0; i < sc->numevts; i++) {
if (sc->evts[i].cookie) {
bus_teardown_intr(dev, sc->evts[i].r,
sc->evts[i].cookie);
}
if (sc->evts[i].r) {
bus_release_resource(dev, SYS_RES_IRQ, sc->evts[i].rid,
sc->evts[i].r);
}
}
free(sc->evts, M_ACPIGED);
return (0);
}

View File

@ -1,7 +1,7 @@
# $FreeBSD$
SUBDIR= acpi_asus acpi_asus_wmi acpi_dock acpi_fujitsu acpi_hp \
acpi_ibm acpi_panasonic acpi_sony acpi_toshiba \
acpi_ged acpi_ibm acpi_panasonic acpi_sony acpi_toshiba \
acpi_video acpi_wmi aibs
.include <bsd.subdir.mk>

View File

@ -0,0 +1,11 @@
# $FreeBSD$
.PATH: ${SRCTOP}/sys/dev/acpica
.if ${TARGET_ARCH} == aarch64
CFLAGS += -DINTRNG
.endif
KMOD= acpi_ged
SRCS= acpi_ged.c
SRCS+= opt_acpi.h opt_evdev.h acpi_if.h bus_if.h device_if.h
.include <bsd.kmod.mk>