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 struct gpio_aei_softc { 43 ACPI_HANDLE handle; 44 char objname[5]; /* "_EXX" or "_LXX" */ 45 struct resource * intr_res; 46 int intr_rid; 47 void * intr_cookie; 48 }; 49 50 static int 51 gpio_aei_probe(device_t dev) 52 { 53 54 /* We only match when gpiobus explicitly requested gpio_aei. */ 55 return (BUS_PROBE_NOWILDCARD); 56 } 57 58 static void 59 gpio_aei_intr(void * arg) 60 { 61 struct gpio_aei_softc * sc = arg; 62 63 /* Ask ACPI to run the appropriate _Exx or _Lxx method. */ 64 AcpiEvaluateObject(sc->handle, sc->objname, NULL, NULL); 65 } 66 67 static int 68 gpio_aei_attach(device_t dev) 69 { 70 struct gpio_aei_softc * sc = device_get_softc(dev); 71 gpio_pin_t pin; 72 int err; 73 74 /* This is us. */ 75 device_set_desc(dev, "ACPI Event Information Device"); 76 77 /* Store parameters needed by gpio_aei_intr. */ 78 sc->handle = acpi_gpiobus_get_handle(dev); 79 if (gpio_pin_get_by_acpi_index(dev, 0, &pin) != 0) { 80 device_printf(dev, "Unable to get the input pin\n"); 81 return (ENXIO); 82 } 83 sprintf(sc->objname, "_%c%02X", 84 (pin->flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin); 85 86 /* Support for GPIO pins > 255 is not implemented. */ 87 if (pin->pin > 255) { 88 device_printf(dev, "ACPI Event Information Device does not support pins > 255"); 89 return (ENOTSUP); 90 } 91 92 /* Set up the interrupt. */ 93 if ((sc->intr_res = gpio_alloc_intr_resource(dev, &sc->intr_rid, 94 RF_ACTIVE, pin, pin->flags & GPIO_INTR_MASK)) == NULL) { 95 device_printf(dev, "Cannot allocate an IRQ\n"); 96 return (ENOTSUP); 97 } 98 err = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE, 99 NULL, gpio_aei_intr, sc, &sc->intr_cookie); 100 if (err != 0) { 101 device_printf(dev, "Cannot set up IRQ\n"); 102 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, 103 sc->intr_res); 104 return (err); 105 } 106 107 return (0); 108 } 109 110 static int 111 gpio_aei_detach(device_t dev) 112 { 113 struct gpio_aei_softc * sc = device_get_softc(dev); 114 115 bus_teardown_intr(dev, sc->intr_res, sc->intr_cookie); 116 bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res); 117 return (0); 118 } 119 120 static device_method_t gpio_aei_methods[] = { 121 /* Device interface. */ 122 DEVMETHOD(device_probe, gpio_aei_probe), 123 DEVMETHOD(device_attach, gpio_aei_attach), 124 DEVMETHOD(device_detach, gpio_aei_detach), 125 126 DEVMETHOD_END 127 }; 128 129 DEFINE_CLASS_0(gpio_aei, gpio_aei_driver, gpio_aei_methods, sizeof(struct gpio_aei_softc)); 130 DRIVER_MODULE(gpio_aei, gpiobus, gpio_aei_driver, NULL, NULL); 131 MODULE_DEPEND(gpio_aei, acpi_gpiobus, 1, 1, 1); 132