git: c2cd78d9446e - stable/14 - GPIO: Add ACPI _AEI support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 03 Nov 2024 16:02:28 UTC
The branch stable/14 has been updated by cperciva: URL: https://cgit.FreeBSD.org/src/commit/?id=c2cd78d9446ebe79accf6c1862230dfbe7276525 commit c2cd78d9446ebe79accf6c1862230dfbe7276525 Author: Colin Percival <cperciva@FreeBSD.org> AuthorDate: 2024-10-22 18:53:55 +0000 Commit: Colin Percival <cperciva@FreeBSD.org> CommitDate: 2024-11-03 16:01:28 +0000 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) --- sys/conf/files | 1 + sys/dev/gpio/acpi_gpiobus.c | 121 +++++++++++++++++++++++++++++++++++ sys/dev/gpio/acpi_gpiobusvar.h | 49 ++++++++++++++ sys/dev/gpio/gpioaei.c | 131 ++++++++++++++++++++++++++++++++++++++ sys/modules/gpio/Makefile | 2 +- sys/modules/gpio/gpioaei/Makefile | 14 ++++ 6 files changed, 317 insertions(+), 1 deletion(-) diff --git a/sys/conf/files b/sys/conf/files index 5239f38d5154..3810032f7094 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 diff --git a/sys/dev/gpio/acpi_gpiobus.c b/sys/dev/gpio/acpi_gpiobus.c index 9828170daeca..14ded4539a5e 100644 --- a/sys/dev/gpio/acpi_gpiobus.c +++ b/sys/dev/gpio/acpi_gpiobus.c @@ -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 }; diff --git a/sys/dev/gpio/acpi_gpiobusvar.h b/sys/dev/gpio/acpi_gpiobusvar.h new file mode 100644 index 000000000000..77cb91110cb5 --- /dev/null +++ b/sys/dev/gpio/acpi_gpiobusvar.h @@ -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__ */ diff --git a/sys/dev/gpio/gpioaei.c b/sys/dev/gpio/gpioaei.c new file mode 100644 index 000000000000..050f259a2127 --- /dev/null +++ b/sys/dev/gpio/gpioaei.c @@ -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); diff --git a/sys/modules/gpio/Makefile b/sys/modules/gpio/Makefile index ffb3581d1f01..1d7f69f1836d 100644 --- a/sys/modules/gpio/Makefile +++ b/sys/modules/gpio/Makefile @@ -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 diff --git a/sys/modules/gpio/gpioaei/Makefile b/sys/modules/gpio/gpioaei/Makefile new file mode 100644 index 000000000000..514dcd80a35e --- /dev/null +++ b/sys/modules/gpio/gpioaei/Makefile @@ -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>