The branch main has been updated by cperciva:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9709bda03cd0f20eba0ba4276fc3c2e06354a54f

commit 9709bda03cd0f20eba0ba4276fc3c2e06354a54f
Author:     Colin Percival <cperc...@freebsd.org>
AuthorDate: 2024-10-22 18:53:55 +0000
Commit:     Colin Percival <cperc...@freebsd.org>
CommitDate: 2024-10-31 21:26:34 +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 <and...@freebsd.org>
---
 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 7c3392fc7320..d04e75be3793 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1740,6 +1740,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 c01d825fabf5..e766c25e5336 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)
 {
@@ -138,6 +148,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", DEVICE_UNIT_ANY);
+       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)
@@ -274,6 +352,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);
 }
 
@@ -296,12 +381,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>

Reply via email to