diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -1740,6 +1740,7 @@ 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 --- a/sys/dev/gpio/acpi_gpiobus.c +++ b/sys/dev/gpio/acpi_gpiobus.c @@ -35,6 +35,9 @@ #include #include +#include + +#include "gpiobus_if.h" struct acpi_gpiobus_softc { struct gpiobus_softc super_sc; @@ -44,6 +47,14 @@ struct acpi_gpiobus_ctx { struct gpiobus_softc *sc; ACPI_HANDLE dev_handle; + device_t dev; +}; + +struct acpi_gpiobus_ivar +{ + struct gpiobus_ivar gpiobus; /* Must come first */ + ACPI_HANDLE dev_handle; /* ACPI handle for bus */ + struct gpiobus_pin pin; /* Pin for _AEI */ }; static uint32_t @@ -136,6 +147,57 @@ return (AE_OK); } +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->dev; + 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 = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO); + if (devi == NULL) { + device_delete_child(bus, child); + return (AE_OK); + } + resource_list_init(&devi->gpiobus.rl); + device_set_ivars(child, devi); + + /* + * Set pin information in bus-managed variables. We need to do + * this in two different ways because the device needs the + * gpiobus_pin structure to set up interrupts. + */ + devi->pin.pin = gpio_res->PinTable[0]; + devi->pin.flags = acpi_gpiobus_convflags(gpio_res); + if (acpi_quirks & ACPI_Q_AEI_NOPULL) + devi->pin.flags &= ~GPIO_PIN_PULLUP; + devi->pin.dev = device_get_parent(bus); + gpiobus_set_npins(child, 1); + gpiobus_set_pins(child, &devi->pin.pin); + if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->pin.flags)) { + gpiobus_release_pin(bus, devi->pin.pin); + 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) @@ -156,6 +218,9 @@ /* Look for GPIO resources */ AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context); + /* Look for AEI children */ + AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei, context); + return (AE_OK); } @@ -263,6 +328,7 @@ return (ENXIO); } + ctx.dev = dev; ctx.dev_handle = handle; ctx.sc = &sc->super_sc; @@ -294,6 +360,25 @@ return (gpiobus_detach(dev)); } +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; + case ACPI_GPIOBUS_IVAR_PIN: + *result = (uintptr_t)&devi->pin; + 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), @@ -301,6 +386,7 @@ DEVMETHOD(device_detach, acpi_gpiobus_detach), /* Bus interface */ + DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), diff --git a/sys/dev/gpio/acpi_gpiobusvar.h b/sys/dev/gpio/acpi_gpiobusvar.h new file mode 100644 --- /dev/null +++ b/sys/dev/gpio/acpi_gpiobusvar.h @@ -0,0 +1,48 @@ +/*- + * 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 + +#include + +enum acpi_gpiobus_ivars { + ACPI_GPIOBUS_IVAR_HANDLE = 10600, + ACPI_GPIOBUS_IVAR_PIN, +}; + +#define ACPI_GPIOBUS_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(acpi_gpiobus, var, ACPI_GPIOBUS, ivar, type) + +ACPI_GPIOBUS_ACCESSOR(handle, HANDLE, ACPI_HANDLE) +ACPI_GPIOBUS_ACCESSOR(pin, PIN, struct gpiobus_pin *) + +#undef ACPI_GPIOBUS_ACCESSOR + +#endif /* __ACPI_GPIOBUS_H__ */ diff --git a/sys/dev/gpio/gpioaei.c b/sys/dev/gpio/gpioaei.c new file mode 100644 --- /dev/null +++ b/sys/dev/gpio/gpioaei.c @@ -0,0 +1,129 @@ +/*- + * 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 +#include +#include +#include +#include + +#include "gpiobus_if.h" + +#include +#include + +#include +#include + +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) +{ + + /* Only handle gpio_aei devices, not anything else on gpiobus. */ + if (strcmp(device_get_name(dev), "gpio_aei")) + return (ENXIO); + + /* This is us. */ + device_set_desc(dev, "ACPI Event Information Device"); + return (BUS_PROBE_DEFAULT); +} + +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); + struct gpiobus_pin * pin = acpi_gpiobus_get_pin(dev); + int err; + + /* 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); + } + + /* Store parameters needed by gpio_aei_intr. */ + sc->handle = acpi_gpiobus_get_handle(dev); + sprintf(sc->objname, "_%c%02X", + (pin->flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin); + + /* 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 --- 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 --- /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