1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Ahmad Khalifa <ahmadkhalifa570@gmail.com> 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 AUTHORS 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 AUTHORS 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/kernel.h> 31 #include <sys/module.h> 32 #include <sys/gpio.h> 33 34 #include <contrib/dev/acpica/include/acpi.h> 35 #include <dev/acpica/acpivar.h> 36 37 #include <dev/gpio/gpiobusvar.h> 38 #include <dev/gpio/acpi_gpiobusvar.h> 39 #include <dev/gpio/gpiobus_internal.h> 40 #include <sys/sbuf.h> 41 42 #include "gpiobus_if.h" 43 44 struct acpi_gpiobus_softc { 45 struct gpiobus_softc super_sc; 46 ACPI_CONNECTION_INFO handler_info; 47 }; 48 49 struct acpi_gpiobus_ctx { 50 struct gpiobus_softc *sc; 51 ACPI_HANDLE dev_handle; 52 }; 53 54 struct acpi_gpiobus_ivar 55 { 56 struct gpiobus_ivar gpiobus; 57 ACPI_HANDLE handle; 58 }; 59 60 uint32_t 61 acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res) 62 { 63 uint32_t flags = 0; 64 65 /* Figure out pin flags */ 66 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT) { 67 switch (gpio_res->Polarity) { 68 case ACPI_ACTIVE_HIGH: 69 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ? 70 GPIO_INTR_LEVEL_HIGH : GPIO_INTR_EDGE_RISING; 71 break; 72 case ACPI_ACTIVE_LOW: 73 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ? 74 GPIO_INTR_LEVEL_LOW : GPIO_INTR_EDGE_FALLING; 75 break; 76 case ACPI_ACTIVE_BOTH: 77 flags = GPIO_INTR_EDGE_BOTH; 78 break; 79 } 80 81 flags |= GPIO_PIN_INPUT; 82 #ifdef NOT_YET 83 /* This is not currently implemented. */ 84 if (gpio_res->Shareable == ACPI_SHARED) 85 flags |= GPIO_INTR_SHAREABLE; 86 #endif 87 } 88 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO) { 89 switch (gpio_res->IoRestriction) { 90 case ACPI_IO_RESTRICT_INPUT: 91 flags |= GPIO_PIN_INPUT; 92 break; 93 case ACPI_IO_RESTRICT_OUTPUT: 94 flags |= GPIO_PIN_OUTPUT; 95 break; 96 } 97 } 98 99 switch (gpio_res->PinConfig) { 100 case ACPI_PIN_CONFIG_PULLUP: 101 flags |= GPIO_PIN_PULLUP; 102 break; 103 case ACPI_PIN_CONFIG_PULLDOWN: 104 flags |= GPIO_PIN_PULLDOWN; 105 break; 106 } 107 108 return (flags); 109 } 110 111 static ACPI_STATUS 112 acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context) 113 { 114 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio; 115 struct acpi_gpiobus_ctx *ctx = context; 116 struct gpiobus_softc *super_sc = ctx->sc; 117 ACPI_HANDLE handle; 118 uint32_t flags, i; 119 120 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 121 return (AE_OK); 122 123 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, 124 gpio_res->ResourceSource.StringPtr, &handle)) || 125 handle != ctx->dev_handle) 126 return (AE_OK); 127 128 if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) { 129 device_printf(super_sc->sc_busdev, 130 "invalid pin table length %hu, max: %d (bad ACPI tables?)\n", 131 gpio_res->PinTableLength, super_sc->sc_npins); 132 return (AE_LIMIT); 133 } 134 135 flags = acpi_gpiobus_convflags(gpio_res); 136 for (i = 0; i < gpio_res->PinTableLength; i++) { 137 UINT16 pin = gpio_res->PinTable[i]; 138 139 if (__predict_false(pin >= super_sc->sc_npins)) { 140 device_printf(super_sc->sc_busdev, 141 "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n", 142 pin, super_sc->sc_npins - 1); 143 return (AE_LIMIT); 144 } 145 146 GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags & 147 ~GPIO_INTR_MASK); 148 } 149 150 return (AE_OK); 151 } 152 153 static ACPI_STATUS 154 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context) 155 { 156 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio; 157 uint32_t *npins = context, *pins = npins + 1; 158 159 /* 160 * Check that we have a GpioInt object. 161 * Note that according to the spec this 162 * should always be the case. 163 */ 164 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 165 return (AE_OK); 166 if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) 167 return (AE_OK); 168 169 for (int i = 0; i < gpio_res->PinTableLength; i++) 170 pins[(*npins)++] = gpio_res->PinTable[i]; 171 return (AE_OK); 172 } 173 174 static ACPI_STATUS 175 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context, 176 void **result) 177 { 178 UINT32 sta; 179 180 /* 181 * If no _STA method or if it failed, then assume that 182 * the device is present. 183 */ 184 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 185 !ACPI_DEVICE_PRESENT(sta)) 186 return (AE_OK); 187 188 if (!acpi_has_hid(handle)) 189 return (AE_OK); 190 191 /* Look for GPIO resources */ 192 AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context); 193 194 return (AE_OK); 195 } 196 197 static ACPI_STATUS 198 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 199 UINT32 length, UINT64 *value, void *context, void *region_context) 200 { 201 ACPI_CONNECTION_INFO *info = context; 202 ACPI_RESOURCE_GPIO *gpio_res; 203 device_t controller; 204 ACPI_RESOURCE *res; 205 ACPI_STATUS status; 206 207 status = AcpiBufferToResource(info->Connection, info->Length, &res); 208 if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO) 209 goto err; 210 211 gpio_res = &res->Data.Gpio; 212 controller = __containerof(info, struct acpi_gpiobus_softc, 213 handler_info)->super_sc.sc_dev; 214 215 switch (function) { 216 case ACPI_WRITE: 217 if (__predict_false( 218 gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT)) 219 goto err; 220 221 for (int i = 0; i < length; i++) 222 if (GPIO_PIN_SET(controller, 223 gpio_res->PinTable[address + i], (*value & 1 << i) ? 224 GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0) 225 goto err; 226 break; 227 case ACPI_READ: 228 if (__predict_false( 229 gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT)) 230 goto err; 231 232 for (int i = 0; i < length; i++) { 233 uint32_t v; 234 235 if (GPIO_PIN_GET(controller, 236 gpio_res->PinTable[address + i], &v) != 0) 237 goto err; 238 *value |= v << i; 239 } 240 break; 241 default: 242 goto err; 243 } 244 245 ACPI_FREE(res); 246 return (AE_OK); 247 248 err: 249 ACPI_FREE(res); 250 return (AE_BAD_PARAMETER); 251 } 252 253 static void 254 acpi_gpiobus_attach_aei(struct acpi_gpiobus_softc *sc, ACPI_HANDLE handle) 255 { 256 struct acpi_gpiobus_ivar *devi; 257 ACPI_HANDLE aei_handle; 258 device_t child; 259 uint32_t *pins; 260 ACPI_STATUS status; 261 int err; 262 263 status = AcpiGetHandle(handle, "_AEI", &aei_handle); 264 if (ACPI_FAILURE(status)) 265 return; 266 267 /* pins[0] specifies the length of the array. */ 268 pins = mallocarray(sc->super_sc.sc_npins + 1, 269 sizeof(uint32_t), M_DEVBUF, M_WAITOK); 270 pins[0] = 0; 271 272 status = AcpiWalkResources(handle, "_AEI", 273 acpi_gpiobus_enumerate_aei, pins); 274 if (ACPI_FAILURE(status)) { 275 device_printf(sc->super_sc.sc_busdev, 276 "Failed to enumerate AEI resources\n"); 277 free(pins, M_DEVBUF); 278 return; 279 } 280 281 child = BUS_ADD_CHILD(sc->super_sc.sc_busdev, 0, "gpio_aei", 282 DEVICE_UNIT_ANY); 283 if (child == NULL) { 284 device_printf(sc->super_sc.sc_busdev, 285 "Failed to add gpio_aei child\n"); 286 free(pins, M_DEVBUF); 287 return; 288 } 289 290 devi = device_get_ivars(child); 291 devi->gpiobus.npins = pins[0]; 292 devi->handle = aei_handle; 293 294 err = gpiobus_alloc_ivars(&devi->gpiobus); 295 if (err != 0) { 296 device_printf(sc->super_sc.sc_busdev, 297 "Failed to allocate gpio_aei ivars\n"); 298 device_delete_child(sc->super_sc.sc_busdev, child); 299 free(pins, M_DEVBUF); 300 return; 301 } 302 303 for (int i = 0; i < pins[0]; i++) 304 devi->gpiobus.pins[i] = pins[i + 1]; 305 free(pins, M_DEVBUF); 306 307 bus_attach_children(sc->super_sc.sc_busdev); 308 } 309 310 static int 311 acpi_gpiobus_probe(device_t dev) 312 { 313 device_t controller; 314 315 if (acpi_disabled("gpiobus")) 316 return (ENXIO); 317 318 controller = device_get_parent(dev); 319 if (controller == NULL) 320 return (ENXIO); 321 322 if (acpi_get_handle(controller) == NULL) 323 return (ENXIO); 324 325 device_set_desc(dev, "GPIO bus (ACPI-hinted)"); 326 return (BUS_PROBE_DEFAULT); 327 } 328 329 static int 330 acpi_gpiobus_attach(device_t dev) 331 { 332 struct acpi_gpiobus_softc *sc; 333 struct acpi_gpiobus_ctx ctx; 334 ACPI_HANDLE handle; 335 ACPI_STATUS status; 336 int err; 337 338 if ((err = gpiobus_attach(dev)) != 0) 339 return (err); 340 341 sc = device_get_softc(dev); 342 handle = acpi_get_handle(sc->super_sc.sc_dev); 343 if (handle == NULL) { 344 gpiobus_detach(dev); 345 return (ENXIO); 346 } 347 348 status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, 349 acpi_gpiobus_space_handler, NULL, &sc->handler_info); 350 351 if (ACPI_FAILURE(status)) { 352 device_printf(dev, 353 "Failed to install GPIO address space handler\n"); 354 gpiobus_detach(dev); 355 return (ENXIO); 356 } 357 358 ctx.dev_handle = handle; 359 ctx.sc = &sc->super_sc; 360 361 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 362 ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL); 363 364 if (ACPI_FAILURE(status)) 365 device_printf(dev, "Failed to enumerate GPIO resources\n"); 366 367 /* Look for AEI child */ 368 acpi_gpiobus_attach_aei(sc, handle); 369 return (0); 370 } 371 372 static int 373 acpi_gpiobus_detach(device_t dev) 374 { 375 struct gpiobus_softc *super_sc; 376 ACPI_STATUS status; 377 378 super_sc = device_get_softc(dev); 379 status = AcpiRemoveAddressSpaceHandler( 380 acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO, 381 acpi_gpiobus_space_handler 382 ); 383 384 if (ACPI_FAILURE(status)) 385 device_printf(dev, 386 "Failed to remove GPIO address space handler\n"); 387 388 return (gpiobus_detach(dev)); 389 } 390 391 static int 392 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, 393 uintptr_t *result) 394 { 395 struct acpi_gpiobus_ivar *devi = device_get_ivars(child); 396 397 switch (which) { 398 case ACPI_GPIOBUS_IVAR_HANDLE: 399 *result = (uintptr_t)devi->handle; 400 break; 401 default: 402 return (gpiobus_read_ivar(dev, child, which, result)); 403 } 404 405 return (0); 406 } 407 408 static device_t 409 acpi_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) 410 { 411 return (gpiobus_add_child_common(dev, order, name, unit, 412 sizeof(struct acpi_gpiobus_ivar))); 413 } 414 415 static int 416 acpi_gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb) 417 { 418 struct acpi_gpiobus_ivar *devi; 419 int err; 420 421 err = gpiobus_child_location(bus, child, sb); 422 if (err != 0) 423 return (err); 424 425 devi = device_get_ivars(child); 426 sbuf_printf(sb, " handle=%s", acpi_name(devi->handle)); 427 return (0); 428 } 429 430 static device_method_t acpi_gpiobus_methods[] = { 431 /* Device interface */ 432 DEVMETHOD(device_probe, acpi_gpiobus_probe), 433 DEVMETHOD(device_attach, acpi_gpiobus_attach), 434 DEVMETHOD(device_detach, acpi_gpiobus_detach), 435 436 /* Bus interface */ 437 DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar), 438 DEVMETHOD(bus_add_child, acpi_gpiobus_add_child), 439 DEVMETHOD(bus_child_location, acpi_gpiobus_child_location), 440 441 DEVMETHOD_END 442 }; 443 444 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods, 445 sizeof(struct acpi_gpiobus_softc), gpiobus_driver); 446 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL, 447 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 448 MODULE_VERSION(acpi_gpiobus, 1); 449 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1); 450