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_ctx { 49 SLIST_ENTRY(gpio_aei_ctx) next; 50 struct resource * intr_res; 51 void * intr_cookie; 52 ACPI_HANDLE handle; 53 gpio_pin_t gpio; 54 uint32_t pin; 55 int intr_rid; 56 enum gpio_aei_type type; 57 }; 58 59 struct gpio_aei_softc { 60 SLIST_HEAD(, gpio_aei_ctx) aei_ctx; 61 ACPI_HANDLE dev_handle; 62 device_t dev; 63 }; 64 65 static int 66 gpio_aei_probe(device_t dev) 67 { 68 69 /* We only match when gpiobus explicitly requested gpio_aei. */ 70 return (BUS_PROBE_NOWILDCARD); 71 } 72 73 static void 74 gpio_aei_intr(void * arg) 75 { 76 struct gpio_aei_ctx * ctx = arg; 77 78 /* Ask ACPI to run the appropriate _EVT, _Exx or _Lxx method. */ 79 if (ctx->type == ACPI_AEI_TYPE_EVT) 80 acpi_SetInteger(ctx->handle, NULL, ctx->pin); 81 else 82 AcpiEvaluateObject(ctx->handle, NULL, NULL, NULL); 83 } 84 85 static ACPI_STATUS 86 gpio_aei_enumerate(ACPI_RESOURCE * res, void * context) 87 { 88 ACPI_RESOURCE_GPIO * gpio_res = &res->Data.Gpio; 89 struct gpio_aei_softc * sc = context; 90 uint32_t flags, maxpin; 91 device_t busdev; 92 int err; 93 94 /* 95 * Check that we have a GpioInt object. 96 * Note that according to the spec this 97 * should always be the case. 98 */ 99 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 100 return (AE_OK); 101 if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) 102 return (AE_OK); 103 104 flags = acpi_gpiobus_convflags(gpio_res); 105 if (acpi_quirks & ACPI_Q_AEI_NOPULL) 106 flags &= ~GPIO_PIN_PULLUP; 107 108 err = GPIO_PIN_MAX(acpi_get_device(sc->dev_handle), &maxpin); 109 if (err != 0) 110 return (AE_ERROR); 111 112 busdev = GPIO_GET_BUS(acpi_get_device(sc->dev_handle)); 113 for (int i = 0; i < gpio_res->PinTableLength; i++) { 114 struct gpio_aei_ctx * ctx; 115 uint32_t pin = gpio_res->PinTable[i]; 116 117 if (__predict_false(pin > maxpin)) { 118 device_printf(sc->dev, 119 "Invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n", 120 pin, maxpin); 121 continue; 122 } 123 124 ctx = malloc(sizeof(struct gpio_aei_ctx), M_DEVBUF, M_WAITOK); 125 ctx->type = ACPI_AEI_TYPE_UNKNOWN; 126 if (pin <= 255) { 127 char objname[5]; /* "_EXX" or "_LXX" */ 128 sprintf(objname, "_%c%02X", 129 (flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin); 130 if (ACPI_SUCCESS(AcpiGetHandle(sc->dev_handle, objname, 131 &ctx->handle))) 132 ctx->type = ACPI_AEI_TYPE_ELX; 133 } 134 135 if (ctx->type == ACPI_AEI_TYPE_UNKNOWN) { 136 if (ACPI_SUCCESS(AcpiGetHandle(sc->dev_handle, "_EVT", 137 &ctx->handle))) 138 ctx->type = ACPI_AEI_TYPE_EVT; 139 else { 140 device_printf(sc->dev, 141 "AEI Device type is unknown for pin 0x%x\n", 142 pin); 143 144 free(ctx, M_DEVBUF); 145 continue; 146 } 147 } 148 149 err = gpio_pin_get_by_bus_pinnum(busdev, pin, &ctx->gpio); 150 if (err != 0) { 151 device_printf(sc->dev, "Cannot acquire pin 0x%x\n", 152 pin); 153 154 free(ctx, M_DEVBUF); 155 continue; 156 } 157 158 err = gpio_pin_setflags(ctx->gpio, flags & ~GPIO_INTR_MASK); 159 if (err != 0) { 160 device_printf(sc->dev, 161 "Cannot set pin flags for pin 0x%x\n", pin); 162 163 gpio_pin_release(ctx->gpio); 164 free(ctx, M_DEVBUF); 165 continue; 166 } 167 168 ctx->intr_rid = 0; 169 ctx->intr_res = gpio_alloc_intr_resource(sc->dev, 170 &ctx->intr_rid, RF_ACTIVE, ctx->gpio, 171 flags & GPIO_INTR_MASK); 172 if (ctx->intr_res == NULL) { 173 device_printf(sc->dev, 174 "Cannot allocate an IRQ for pin 0x%x\n", pin); 175 176 gpio_pin_release(ctx->gpio); 177 free(ctx, M_DEVBUF); 178 continue; 179 } 180 181 err = bus_setup_intr(sc->dev, ctx->intr_res, INTR_TYPE_MISC | 182 INTR_MPSAFE | INTR_EXCL | INTR_SLEEPABLE, NULL, 183 gpio_aei_intr, ctx, &ctx->intr_cookie); 184 if (err != 0) { 185 device_printf(sc->dev, 186 "Cannot set up an IRQ for pin 0x%x\n", pin); 187 188 bus_release_resource(sc->dev, ctx->intr_res); 189 gpio_pin_release(ctx->gpio); 190 free(ctx, M_DEVBUF); 191 continue; 192 } 193 194 ctx->pin = pin; 195 SLIST_INSERT_HEAD(&sc->aei_ctx, ctx, next); 196 } 197 198 return (AE_OK); 199 } 200 201 static int 202 gpio_aei_attach(device_t dev) 203 { 204 struct gpio_aei_softc * sc = device_get_softc(dev); 205 ACPI_HANDLE handle; 206 ACPI_STATUS status; 207 208 /* This is us. */ 209 device_set_desc(dev, "ACPI Event Information Device"); 210 211 handle = acpi_gpiobus_get_handle(dev); 212 status = AcpiGetParent(handle, &sc->dev_handle); 213 if (ACPI_FAILURE(status)) { 214 device_printf(dev, "Cannot get parent of %s\n", 215 acpi_name(handle)); 216 return (ENXIO); 217 } 218 219 SLIST_INIT(&sc->aei_ctx); 220 sc->dev = dev; 221 222 status = AcpiWalkResources(sc->dev_handle, "_AEI", 223 gpio_aei_enumerate, sc); 224 if (ACPI_FAILURE(status)) { 225 device_printf(dev, "Failed to enumerate AEI resources\n"); 226 return (ENXIO); 227 } 228 229 return (0); 230 } 231 232 static int 233 gpio_aei_detach(device_t dev) 234 { 235 struct gpio_aei_softc * sc = device_get_softc(dev); 236 struct gpio_aei_ctx * ctx, * tctx; 237 238 SLIST_FOREACH_SAFE(ctx, &sc->aei_ctx, next, tctx) { 239 bus_teardown_intr(dev, ctx->intr_res, ctx->intr_cookie); 240 bus_release_resource(dev, ctx->intr_res); 241 gpio_pin_release(ctx->gpio); 242 free(ctx, M_DEVBUF); 243 } 244 245 return (0); 246 } 247 248 static device_method_t gpio_aei_methods[] = { 249 /* Device interface. */ 250 DEVMETHOD(device_probe, gpio_aei_probe), 251 DEVMETHOD(device_attach, gpio_aei_attach), 252 DEVMETHOD(device_detach, gpio_aei_detach), 253 254 DEVMETHOD_END 255 }; 256 257 DEFINE_CLASS_0(gpio_aei, gpio_aei_driver, gpio_aei_methods, sizeof(struct gpio_aei_softc)); 258 DRIVER_MODULE(gpio_aei, gpiobus, gpio_aei_driver, NULL, NULL); 259 MODULE_DEPEND(gpio_aei, acpi_gpiobus, 1, 1, 1); 260