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