mirror of
https://github.com/opnsense/src.git
synced 2026-02-18 18:20:26 -05:00
GPIO: Add ACPI _AEI support
Changes to acpi_gpiobus.c handle discovering and parsing the _AEI objects and storing necessary data in device ivars. A new gpioaei.c file implements the device, which simply requests an interrupt when the pin is triggered and invokes the appropriate _Exx or _Lxx ACPI method. This makes the GPIO "power button" work on arm64 Graviton systems, allowing EC2 "Stop"/"Reboot" instance calls to be handled cleanly. (Prior to this change, those requests would time out after 4 minutes and the instance would be forcibly killed.) Reviwed by: imp, andrew, Ahmad Khalifa MFC after: 3 days Sponsored by: Amazon Differential Revision: https://reviews.freebsd.org/D47253 Co-authored-by: Andrew Turner <andrew@FreeBSD.org> (cherry picked from commit 9709bda03cd0f20eba0ba4276fc3c2e06354a54f)
This commit is contained in:
parent
c54bdf84d5
commit
c2cd78d944
6 changed files with 317 additions and 1 deletions
|
|
@ -1745,6 +1745,7 @@ dev/gpio/acpi_gpiobus.c optional acpi gpio
|
|||
dev/gpio/dwgpio/dwgpio.c optional gpio dwgpio fdt
|
||||
dev/gpio/dwgpio/dwgpio_bus.c optional gpio dwgpio fdt
|
||||
dev/gpio/dwgpio/dwgpio_if.m optional gpio dwgpio fdt
|
||||
dev/gpio/gpioaei.c optional acpi gpio
|
||||
dev/gpio/gpiobacklight.c optional gpiobacklight fdt
|
||||
dev/gpio/gpiokeys.c optional gpiokeys fdt
|
||||
dev/gpio/gpiokeys_codes.c optional gpiokeys fdt
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@
|
|||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
#include <dev/gpio/gpiobusvar.h>
|
||||
#include <dev/gpio/acpi_gpiobusvar.h>
|
||||
|
||||
#include "gpiobus_if.h"
|
||||
|
||||
struct acpi_gpiobus_softc {
|
||||
struct gpiobus_softc super_sc;
|
||||
|
|
@ -46,6 +49,13 @@ struct acpi_gpiobus_ctx {
|
|||
ACPI_HANDLE dev_handle;
|
||||
};
|
||||
|
||||
struct acpi_gpiobus_ivar
|
||||
{
|
||||
struct gpiobus_ivar gpiobus; /* Must come first */
|
||||
ACPI_HANDLE dev_handle; /* ACPI handle for bus */
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static uint32_t
|
||||
acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res)
|
||||
{
|
||||
|
|
@ -136,6 +146,74 @@ acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context)
|
|||
return (AE_OK);
|
||||
}
|
||||
|
||||
static struct acpi_gpiobus_ivar *
|
||||
acpi_gpiobus_setup_devinfo(device_t bus, device_t child,
|
||||
ACPI_RESOURCE_GPIO *gpio_res)
|
||||
{
|
||||
struct acpi_gpiobus_ivar *devi;
|
||||
|
||||
devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO);
|
||||
if (devi == NULL)
|
||||
return (NULL);
|
||||
resource_list_init(&devi->gpiobus.rl);
|
||||
|
||||
devi->flags = acpi_gpiobus_convflags(gpio_res);
|
||||
if (acpi_quirks & ACPI_Q_AEI_NOPULL)
|
||||
devi->flags &= ~GPIO_PIN_PULLUP;
|
||||
|
||||
devi->gpiobus.npins = 1;
|
||||
if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) {
|
||||
free(devi, M_DEVBUF);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
for (int i = 0; i < devi->gpiobus.npins; i++)
|
||||
devi->gpiobus.pins[i] = gpio_res->PinTable[i];
|
||||
|
||||
return (devi);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
|
||||
{
|
||||
ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
|
||||
struct acpi_gpiobus_ctx *ctx = context;
|
||||
device_t bus = ctx->sc->sc_busdev;
|
||||
device_t child;
|
||||
struct acpi_gpiobus_ivar *devi;
|
||||
|
||||
/* Check that we have a GpioInt object. */
|
||||
if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
|
||||
return (AE_OK);
|
||||
if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
|
||||
return (AE_OK);
|
||||
|
||||
/* Add a child. */
|
||||
child = device_add_child_ordered(bus, 0, "gpio_aei", -1);
|
||||
if (child == NULL)
|
||||
return (AE_OK);
|
||||
devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res);
|
||||
if (devi == NULL) {
|
||||
device_delete_child(bus, child);
|
||||
return (AE_OK);
|
||||
}
|
||||
device_set_ivars(child, devi);
|
||||
|
||||
for (int i = 0; i < devi->gpiobus.npins; i++) {
|
||||
if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags)) {
|
||||
gpiobus_free_ivars(&devi->gpiobus);
|
||||
free(devi, M_DEVBUF);
|
||||
device_delete_child(bus, child);
|
||||
return (AE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass ACPI information to children. */
|
||||
devi->dev_handle = ctx->dev_handle;
|
||||
|
||||
return (AE_OK);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context,
|
||||
void **result)
|
||||
|
|
@ -272,6 +350,13 @@ acpi_gpiobus_attach(device_t dev)
|
|||
if (ACPI_FAILURE(status))
|
||||
device_printf(dev, "Failed to enumerate GPIO resources\n");
|
||||
|
||||
/* Look for AEI children */
|
||||
status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei,
|
||||
&ctx);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
device_printf(dev, "Failed to enumerate GPIO resources\n");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
@ -294,12 +379,48 @@ acpi_gpiobus_detach(device_t dev)
|
|||
return (gpiobus_detach(dev));
|
||||
}
|
||||
|
||||
int
|
||||
gpio_pin_get_by_acpi_index(device_t consumer, uint32_t idx,
|
||||
gpio_pin_t *out_pin)
|
||||
{
|
||||
struct acpi_gpiobus_ivar *devi;
|
||||
int rv;
|
||||
|
||||
rv = gpio_pin_get_by_child_index(consumer, idx, out_pin);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
|
||||
devi = device_get_ivars(consumer);
|
||||
(*out_pin)->flags = devi->flags;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
|
||||
{
|
||||
struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
|
||||
|
||||
switch (which) {
|
||||
case ACPI_GPIOBUS_IVAR_HANDLE:
|
||||
*result = (uintptr_t)devi->dev_handle;
|
||||
break;
|
||||
default:
|
||||
return (gpiobus_read_ivar(dev, child, which, result));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t acpi_gpiobus_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, acpi_gpiobus_probe),
|
||||
DEVMETHOD(device_attach, acpi_gpiobus_attach),
|
||||
DEVMETHOD(device_detach, acpi_gpiobus_detach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
|
|
|
|||
49
sys/dev/gpio/acpi_gpiobusvar.h
Normal file
49
sys/dev/gpio/acpi_gpiobusvar.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2024 Colin Percival
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ACPI_GPIOBUS_H__
|
||||
#define __ACPI_GPIOBUS_H__
|
||||
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
|
||||
enum acpi_gpiobus_ivars {
|
||||
ACPI_GPIOBUS_IVAR_HANDLE = 10600,
|
||||
};
|
||||
|
||||
#define ACPI_GPIOBUS_ACCESSOR(var, ivar, type) \
|
||||
__BUS_ACCESSOR(acpi_gpiobus, var, ACPI_GPIOBUS, ivar, type)
|
||||
|
||||
ACPI_GPIOBUS_ACCESSOR(handle, HANDLE, ACPI_HANDLE)
|
||||
|
||||
#undef ACPI_GPIOBUS_ACCESSOR
|
||||
|
||||
int gpio_pin_get_by_acpi_index(device_t consumer, uint32_t idx,
|
||||
gpio_pin_t *out_pin);
|
||||
|
||||
#endif /* __ACPI_GPIOBUS_H__ */
|
||||
131
sys/dev/gpio/gpioaei.c
Normal file
131
sys/dev/gpio/gpioaei.c
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2024 Colin Percival
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/gpio.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
#include "gpiobus_if.h"
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
#include <dev/gpio/gpiobusvar.h>
|
||||
#include <dev/gpio/acpi_gpiobusvar.h>
|
||||
|
||||
struct gpio_aei_softc {
|
||||
ACPI_HANDLE handle;
|
||||
char objname[5]; /* "_EXX" or "_LXX" */
|
||||
struct resource * intr_res;
|
||||
int intr_rid;
|
||||
void * intr_cookie;
|
||||
};
|
||||
|
||||
static int
|
||||
gpio_aei_probe(device_t dev)
|
||||
{
|
||||
|
||||
/* We only match when gpiobus explicitly requested gpio_aei. */
|
||||
return (BUS_PROBE_NOWILDCARD);
|
||||
}
|
||||
|
||||
static void
|
||||
gpio_aei_intr(void * arg)
|
||||
{
|
||||
struct gpio_aei_softc * sc = arg;
|
||||
|
||||
/* Ask ACPI to run the appropriate _Exx or _Lxx method. */
|
||||
AcpiEvaluateObject(sc->handle, sc->objname, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_aei_attach(device_t dev)
|
||||
{
|
||||
struct gpio_aei_softc * sc = device_get_softc(dev);
|
||||
gpio_pin_t pin;
|
||||
int err;
|
||||
|
||||
/* This is us. */
|
||||
device_set_desc(dev, "ACPI Event Information Device");
|
||||
|
||||
/* Store parameters needed by gpio_aei_intr. */
|
||||
sc->handle = acpi_gpiobus_get_handle(dev);
|
||||
if (gpio_pin_get_by_acpi_index(dev, 0, &pin) != 0) {
|
||||
device_printf(dev, "Unable to get the input pin\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
sprintf(sc->objname, "_%c%02X",
|
||||
(pin->flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin);
|
||||
|
||||
/* Support for GPIO pins > 255 is not implemented. */
|
||||
if (pin->pin > 255) {
|
||||
device_printf(dev, "ACPI Event Information Device does not support pins > 255");
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
/* Set up the interrupt. */
|
||||
if ((sc->intr_res = gpio_alloc_intr_resource(dev, &sc->intr_rid,
|
||||
RF_ACTIVE, pin, pin->flags & GPIO_INTR_MASK)) == NULL) {
|
||||
device_printf(dev, "Cannot allocate an IRQ\n");
|
||||
return (ENOTSUP);
|
||||
}
|
||||
err = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
|
||||
NULL, gpio_aei_intr, sc, &sc->intr_cookie);
|
||||
if (err != 0) {
|
||||
device_printf(dev, "Cannot set up IRQ\n");
|
||||
bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid,
|
||||
sc->intr_res);
|
||||
return (err);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gpio_aei_detach(device_t dev)
|
||||
{
|
||||
struct gpio_aei_softc * sc = device_get_softc(dev);
|
||||
|
||||
bus_teardown_intr(dev, sc->intr_res, sc->intr_cookie);
|
||||
bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t gpio_aei_methods[] = {
|
||||
/* Device interface. */
|
||||
DEVMETHOD(device_probe, gpio_aei_probe),
|
||||
DEVMETHOD(device_attach, gpio_aei_attach),
|
||||
DEVMETHOD(device_detach, gpio_aei_detach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_0(gpio_aei, gpio_aei_driver, gpio_aei_methods, sizeof(struct gpio_aei_softc));
|
||||
DRIVER_MODULE(gpio_aei, gpiobus, gpio_aei_driver, NULL, NULL);
|
||||
MODULE_DEPEND(gpio_aei, acpi_gpiobus, 1, 1, 1);
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
# SUCH DAMAGE.
|
||||
#
|
||||
|
||||
SUBDIR = gpiobus gpioiic gpioled gpiospi gpioths
|
||||
SUBDIR = gpioaei gpiobus gpioiic gpioled gpiospi gpioths
|
||||
|
||||
.if !empty(OPT_FDT)
|
||||
SUBDIR += gpiokeys gpiopps
|
||||
|
|
|
|||
14
sys/modules/gpio/gpioaei/Makefile
Normal file
14
sys/modules/gpio/gpioaei/Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.PATH: ${SRCTOP}/sys/dev/gpio/
|
||||
|
||||
KMOD= gpioaei
|
||||
SRCS= gpioaei.c
|
||||
|
||||
SRCS+= \
|
||||
bus_if.h \
|
||||
device_if.h \
|
||||
gpio_if.h \
|
||||
gpiobus_if.h
|
||||
|
||||
CFLAGS+= -I. -I${SRCTOP}/sys/dev/gpio/
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
Loading…
Reference in a new issue