1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Colin Percival 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/bus.h> 30 #include <sys/gpio.h> 31 #include <sys/kernel.h> 32 #include <sys/module.h> 33 34 #include "gpiobus_if.h" 35 36 #include <contrib/dev/acpica/include/acpi.h> 37 #include <dev/acpica/acpivar.h> 38 39 #include <dev/gpio/gpiobusvar.h> 40 #include <dev/gpio/acpi_gpiobusvar.h> 41 42 enum gpio_aei_type { 43 ACPI_AEI_TYPE_UNKNOWN, 44 ACPI_AEI_TYPE_ELX, 45 ACPI_AEI_TYPE_EVT 46 }; 47 48 struct gpio_aei_softc { 49 ACPI_HANDLE handle; 50 enum gpio_aei_type type; 51 int pin; 52 struct resource * intr_res; 53 int intr_rid; 54 void * intr_cookie; 55 }; 56 57 static int 58 gpio_aei_probe(device_t dev) 59 { 60 61 /* We only match when gpiobus explicitly requested gpio_aei. */ 62 return (BUS_PROBE_NOWILDCARD); 63 } 64 65 static void 66 gpio_aei_intr(void * arg) 67 { 68 struct gpio_aei_softc * sc = arg; 69 70 /* Ask ACPI to run the appropriate _EVT, _Exx or _Lxx method. */ 71 if (sc->type == ACPI_AEI_TYPE_EVT) 72 acpi_SetInteger(sc->handle, NULL, sc->pin); 73 else 74 AcpiEvaluateObject(sc->handle, NULL, NULL, NULL); 75 } 76 77 static int 78 gpio_aei_attach(device_t dev) 79 { 80 struct gpio_aei_softc * sc = device_get_softc(dev); 81 gpio_pin_t pin; 82 uint32_t flags; 83 ACPI_HANDLE handle; 84 int err; 85 86 /* This is us. */ 87 device_set_desc(dev, "ACPI Event Information Device"); 88 89 /* Store parameters needed by gpio_aei_intr. */ 90 handle = acpi_gpiobus_get_handle(dev); 91 if (gpio_pin_get_by_child_index(dev, 0, &pin) != 0) { 92 device_printf(dev, "Unable to get the input pin\n"); 93 return (ENXIO); 94 } 95 96 sc->type = ACPI_AEI_TYPE_UNKNOWN; 97 sc->pin = pin->pin; 98 99 flags = acpi_gpiobus_get_flags(dev); 100 if (pin->pin <= 255) { 101 char objname[5]; /* "_EXX" or "_LXX" */ 102 sprintf(objname, "_%c%02X", 103 (flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin); 104 if (ACPI_SUCCESS(AcpiGetHandle(handle, objname, &sc->handle))) 105 sc->type = ACPI_AEI_TYPE_ELX; 106 } 107 if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { 108 if (ACPI_SUCCESS(AcpiGetHandle(handle, "_EVT", &sc->handle))) 109 sc->type = ACPI_AEI_TYPE_EVT; 110 } 111 112 if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { 113 device_printf(dev, "ACPI Event Information Device type is unknown"); 114 return (ENOTSUP); 115 } 116 117 /* Set up the interrupt. */ 118 if ((sc->intr_res = gpio_alloc_intr_resource(dev, &sc->intr_rid, 119 RF_ACTIVE, pin, flags & GPIO_INTR_MASK)) == NULL) { 120 device_printf(dev, "Cannot allocate an IRQ\n"); 121 return (ENOTSUP); 122 } 123 err = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE | 124 INTR_EXCL | INTR_SLEEPABLE, NULL, gpio_aei_intr, sc, 125 &sc->intr_cookie); 126 if (err != 0) { 127 device_printf(dev, "Cannot set up IRQ\n"); 128 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, 129 sc->intr_res); 130 return (err); 131 } 132 133 return (0); 134 } 135 136 static int 137 gpio_aei_detach(device_t dev) 138 { 139 struct gpio_aei_softc * sc = device_get_softc(dev); 140 141 bus_teardown_intr(dev, sc->intr_res, sc->intr_cookie); 142 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res); 143 return (0); 144 } 145 146 static device_method_t gpio_aei_methods[] = { 147 /* Device interface. */ 148 DEVMETHOD(device_probe, gpio_aei_probe), 149 DEVMETHOD(device_attach, gpio_aei_attach), 150 DEVMETHOD(device_detach, gpio_aei_detach), 151 152 DEVMETHOD_END 153 }; 154 155 DEFINE_CLASS_0(gpio_aei, gpio_aei_driver, gpio_aei_methods, sizeof(struct gpio_aei_softc)); 156 DRIVER_MODULE(gpio_aei, gpiobus, gpio_aei_driver, NULL, NULL); 157 MODULE_DEPEND(gpio_aei, acpi_gpiobus, 1, 1, 1); 158