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 ACPI_HANDLE handle; 83 int err; 84 85 /* This is us. */ 86 device_set_desc(dev, "ACPI Event Information Device"); 87 88 /* Store parameters needed by gpio_aei_intr. */ 89 handle = acpi_gpiobus_get_handle(dev); 90 if (gpio_pin_get_by_acpi_index(dev, 0, &pin) != 0) { 91 device_printf(dev, "Unable to get the input pin\n"); 92 return (ENXIO); 93 } 94 95 sc->type = ACPI_AEI_TYPE_UNKNOWN; 96 sc->pin = pin->pin; 97 if (pin->pin <= 255) { 98 char objname[5]; /* "_EXX" or "_LXX" */ 99 sprintf(objname, "_%c%02X", 100 (pin->flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin); 101 if (ACPI_SUCCESS(AcpiGetHandle(handle, objname, &sc->handle))) 102 sc->type = ACPI_AEI_TYPE_ELX; 103 } 104 if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { 105 if (ACPI_SUCCESS(AcpiGetHandle(handle, "_EVT", &sc->handle))) 106 sc->type = ACPI_AEI_TYPE_EVT; 107 } 108 109 if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { 110 device_printf(dev, "ACPI Event Information Device type is unknown"); 111 return (ENOTSUP); 112 } 113 114 /* Set up the interrupt. */ 115 if ((sc->intr_res = gpio_alloc_intr_resource(dev, &sc->intr_rid, 116 RF_ACTIVE, pin, pin->flags & GPIO_INTR_MASK)) == NULL) { 117 device_printf(dev, "Cannot allocate an IRQ\n"); 118 return (ENOTSUP); 119 } 120 err = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, 121 NULL, gpio_aei_intr, sc, &sc->intr_cookie); 122 if (err != 0) { 123 device_printf(dev, "Cannot set up IRQ\n"); 124 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, 125 sc->intr_res); 126 return (err); 127 } 128 129 return (0); 130 } 131 132 static int 133 gpio_aei_detach(device_t dev) 134 { 135 struct gpio_aei_softc * sc = device_get_softc(dev); 136 137 bus_teardown_intr(dev, sc->intr_res, sc->intr_cookie); 138 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res); 139 return (0); 140 } 141 142 static device_method_t gpio_aei_methods[] = { 143 /* Device interface. */ 144 DEVMETHOD(device_probe, gpio_aei_probe), 145 DEVMETHOD(device_attach, gpio_aei_attach), 146 DEVMETHOD(device_detach, gpio_aei_detach), 147 148 DEVMETHOD_END 149 }; 150 151 DEFINE_CLASS_0(gpio_aei, gpio_aei_driver, gpio_aei_methods, sizeof(struct gpio_aei_softc)); 152 DRIVER_MODULE(gpio_aei, gpiobus, gpio_aei_driver, NULL, NULL); 153 MODULE_DEPEND(gpio_aei, acpi_gpiobus, 1, 1, 1); 154