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 41 #include "gpiobus_if.h" 42 43 struct acpi_gpiobus_softc { 44 struct gpiobus_softc super_sc; 45 ACPI_CONNECTION_INFO handler_info; 46 }; 47 48 struct acpi_gpiobus_ctx { 49 struct gpiobus_softc *sc; 50 ACPI_HANDLE dev_handle; 51 }; 52 53 struct acpi_gpiobus_ivar 54 { 55 struct gpiobus_ivar gpiobus; /* Must come first */ 56 ACPI_HANDLE dev_handle; /* ACPI handle for bus */ 57 uint32_t flags; 58 }; 59 60 static 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 struct acpi_gpiobus_ivar * 154 acpi_gpiobus_setup_devinfo(device_t bus, device_t child, 155 ACPI_RESOURCE_GPIO *gpio_res) 156 { 157 struct acpi_gpiobus_ivar *devi; 158 159 devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO); 160 if (devi == NULL) 161 return (NULL); 162 resource_list_init(&devi->gpiobus.rl); 163 164 devi->flags = acpi_gpiobus_convflags(gpio_res); 165 if (acpi_quirks & ACPI_Q_AEI_NOPULL) 166 devi->flags &= ~GPIO_PIN_PULLUP; 167 168 devi->gpiobus.npins = 1; 169 if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) { 170 free(devi, M_DEVBUF); 171 return (NULL); 172 } 173 174 for (int i = 0; i < devi->gpiobus.npins; i++) 175 devi->gpiobus.pins[i] = gpio_res->PinTable[i]; 176 177 return (devi); 178 } 179 180 static ACPI_STATUS 181 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context) 182 { 183 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio; 184 struct acpi_gpiobus_ctx *ctx = context; 185 device_t bus = ctx->sc->sc_busdev; 186 device_t child; 187 struct acpi_gpiobus_ivar *devi; 188 189 /* Check that we have a GpioInt object. */ 190 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 191 return (AE_OK); 192 if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) 193 return (AE_OK); 194 195 /* Add a child. */ 196 child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY); 197 if (child == NULL) 198 return (AE_OK); 199 devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res); 200 if (devi == NULL) { 201 device_delete_child(bus, child); 202 return (AE_OK); 203 } 204 device_set_ivars(child, devi); 205 206 for (int i = 0; i < devi->gpiobus.npins; i++) { 207 if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags & 208 ~GPIO_INTR_MASK)) { 209 device_delete_child(bus, child); 210 return (AE_OK); 211 } 212 } 213 214 /* Pass ACPI information to children. */ 215 devi->dev_handle = ctx->dev_handle; 216 217 return (AE_OK); 218 } 219 220 static ACPI_STATUS 221 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context, 222 void **result) 223 { 224 UINT32 sta; 225 226 /* 227 * If no _STA method or if it failed, then assume that 228 * the device is present. 229 */ 230 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 231 !ACPI_DEVICE_PRESENT(sta)) 232 return (AE_OK); 233 234 if (!acpi_has_hid(handle)) 235 return (AE_OK); 236 237 /* Look for GPIO resources */ 238 AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context); 239 240 return (AE_OK); 241 } 242 243 static ACPI_STATUS 244 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 245 UINT32 length, UINT64 *value, void *context, void *region_context) 246 { 247 ACPI_CONNECTION_INFO *info = context; 248 ACPI_RESOURCE_GPIO *gpio_res; 249 device_t controller; 250 ACPI_RESOURCE *res; 251 ACPI_STATUS status; 252 253 status = AcpiBufferToResource(info->Connection, info->Length, &res); 254 if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO) 255 goto err; 256 257 gpio_res = &res->Data.Gpio; 258 controller = __containerof(info, struct acpi_gpiobus_softc, 259 handler_info)->super_sc.sc_dev; 260 261 switch (function) { 262 case ACPI_WRITE: 263 if (__predict_false( 264 gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT)) 265 goto err; 266 267 for (int i = 0; i < length; i++) 268 if (GPIO_PIN_SET(controller, 269 gpio_res->PinTable[address + i], (*value & 1 << i) ? 270 GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0) 271 goto err; 272 break; 273 case ACPI_READ: 274 if (__predict_false( 275 gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT)) 276 goto err; 277 278 for (int i = 0; i < length; i++) { 279 uint32_t v; 280 281 if (GPIO_PIN_GET(controller, 282 gpio_res->PinTable[address + i], &v) != 0) 283 goto err; 284 *value |= v << i; 285 } 286 break; 287 default: 288 goto err; 289 } 290 291 ACPI_FREE(res); 292 return (AE_OK); 293 294 err: 295 ACPI_FREE(res); 296 return (AE_BAD_PARAMETER); 297 } 298 299 static int 300 acpi_gpiobus_probe(device_t dev) 301 { 302 device_t controller; 303 304 if (acpi_disabled("gpiobus")) 305 return (ENXIO); 306 307 controller = device_get_parent(dev); 308 if (controller == NULL) 309 return (ENXIO); 310 311 if (acpi_get_handle(controller) == NULL) 312 return (ENXIO); 313 314 device_set_desc(dev, "GPIO bus (ACPI-hinted)"); 315 return (BUS_PROBE_DEFAULT); 316 } 317 318 static int 319 acpi_gpiobus_attach(device_t dev) 320 { 321 struct acpi_gpiobus_softc *sc; 322 struct acpi_gpiobus_ctx ctx; 323 ACPI_HANDLE handle; 324 ACPI_STATUS status; 325 int err; 326 327 if ((err = gpiobus_attach(dev)) != 0) 328 return (err); 329 330 sc = device_get_softc(dev); 331 handle = acpi_get_handle(sc->super_sc.sc_dev); 332 if (handle == NULL) { 333 gpiobus_detach(dev); 334 return (ENXIO); 335 } 336 337 status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, 338 acpi_gpiobus_space_handler, NULL, &sc->handler_info); 339 340 if (ACPI_FAILURE(status)) { 341 device_printf(dev, 342 "Failed to install GPIO address space handler\n"); 343 gpiobus_detach(dev); 344 return (ENXIO); 345 } 346 347 ctx.dev_handle = handle; 348 ctx.sc = &sc->super_sc; 349 350 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 351 ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL); 352 353 if (ACPI_FAILURE(status)) 354 device_printf(dev, "Failed to enumerate GPIO resources\n"); 355 356 /* Look for AEI children */ 357 status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei, 358 &ctx); 359 360 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) 361 device_printf(dev, "Failed to enumerate AEI resources\n"); 362 363 return (0); 364 } 365 366 static int 367 acpi_gpiobus_detach(device_t dev) 368 { 369 struct gpiobus_softc *super_sc; 370 ACPI_STATUS status; 371 372 super_sc = device_get_softc(dev); 373 status = AcpiRemoveAddressSpaceHandler( 374 acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO, 375 acpi_gpiobus_space_handler 376 ); 377 378 if (ACPI_FAILURE(status)) 379 device_printf(dev, 380 "Failed to remove GPIO address space handler\n"); 381 382 return (gpiobus_detach(dev)); 383 } 384 385 static int 386 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, 387 uintptr_t *result) 388 { 389 struct acpi_gpiobus_ivar *devi = device_get_ivars(child); 390 391 switch (which) { 392 case ACPI_GPIOBUS_IVAR_HANDLE: 393 *result = (uintptr_t)devi->dev_handle; 394 break; 395 case ACPI_GPIOBUS_IVAR_FLAGS: 396 *result = (uintptr_t)devi->flags; 397 break; 398 default: 399 return (gpiobus_read_ivar(dev, child, which, result)); 400 } 401 402 return (0); 403 } 404 405 static device_method_t acpi_gpiobus_methods[] = { 406 /* Device interface */ 407 DEVMETHOD(device_probe, acpi_gpiobus_probe), 408 DEVMETHOD(device_attach, acpi_gpiobus_attach), 409 DEVMETHOD(device_detach, acpi_gpiobus_detach), 410 411 /* Bus interface */ 412 DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar), 413 414 DEVMETHOD_END 415 }; 416 417 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods, 418 sizeof(struct acpi_gpiobus_softc), gpiobus_driver); 419 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL, 420 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 421 MODULE_VERSION(acpi_gpiobus, 1); 422 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1); 423