1 /* 2 * Copyright (c) 2016 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Imre Vadász <imre@vdsz.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/malloc.h> 41 #include <sys/module.h> 42 #include <sys/errno.h> 43 #include <sys/lock.h> 44 #include <sys/bus.h> 45 46 #include "opt_acpi.h" 47 #include "acpi.h" 48 #include <dev/acpica/acpivar.h> 49 50 #include "gpio_acpivar.h" 51 52 #include "gpio_if.h" 53 54 struct acpi_event_info { 55 device_t dev; 56 u_int pin; 57 void *cookie; 58 int trigger; 59 }; 60 61 struct acpi_gpio_handler_data { 62 struct acpi_connection_info info; 63 device_t dev; 64 }; 65 66 struct gpio_acpi_softc { 67 device_t dev; 68 device_t parent; 69 struct acpi_event_info *infos; 70 int num_aei; 71 struct acpi_gpio_handler_data space_handler_data; 72 }; 73 74 static int gpio_acpi_probe(device_t dev); 75 static int gpio_acpi_attach(device_t dev); 76 static int gpio_acpi_detach(device_t dev); 77 78 static BOOLEAN gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio); 79 static void **gpioio_alloc_pins(device_t dev, device_t provider, 80 ACPI_RESOURCE_GPIO *gpio, uint16_t idx, uint16_t length, 81 void **buf); 82 83 /* GPIO Address Space Handler */ 84 static void gpio_acpi_install_address_space_handler( 85 struct gpio_acpi_softc *sc); 86 static void gpio_acpi_remove_address_space_handler( 87 struct gpio_acpi_softc *sc); 88 static ACPI_STATUS gpio_acpi_space_handler(UINT32 Function, 89 ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth, 90 UINT64 *Value, void *HandlerContext, 91 void *RegionContext); 92 93 /* ACPI Event Interrupts */ 94 static void gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc, 95 ACPI_RESOURCE_GPIO *gpio); 96 static void gpio_acpi_map_aei(struct gpio_acpi_softc *sc); 97 static void gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc); 98 static void gpio_acpi_handle_event(void *Context); 99 static void gpio_acpi_aei_handler(void *arg); 100 101 /* Sanity-Check for GpioInt resources */ 102 static BOOLEAN 103 gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio) 104 { 105 if (gpio->PinTableLength != 1) { 106 device_printf(dev, 107 "Unexepcted GpioInt resource PinTableLength %d\n", 108 gpio->PinTableLength); 109 return (FALSE); 110 } 111 switch (gpio->Triggering) { 112 case ACPI_LEVEL_SENSITIVE: 113 case ACPI_EDGE_SENSITIVE: 114 break; 115 default: 116 device_printf(dev, "Invalid GpioInt resource Triggering: %d\n", 117 gpio->Triggering); 118 return (FALSE); 119 } 120 switch (gpio->Polarity) { 121 case ACPI_ACTIVE_HIGH: 122 case ACPI_ACTIVE_LOW: 123 case ACPI_ACTIVE_BOTH: 124 break; 125 default: 126 device_printf(dev, "Invalid GpioInt resource Polarity: %d\n", 127 gpio->Polarity); 128 return (FALSE); 129 } 130 131 return (TRUE); 132 } 133 134 /* 135 * GpioIo ACPI resource handling 136 */ 137 138 static void ** 139 gpioio_alloc_pins(device_t dev, device_t provider, ACPI_RESOURCE_GPIO *gpio, 140 uint16_t idx, uint16_t length, void **buf) 141 { 142 void **pins; 143 int flags, i, j; 144 145 if (buf == NULL) { 146 pins = kmalloc(sizeof(*pins) * length, M_DEVBUF, 147 M_WAITOK | M_ZERO); 148 } else { 149 pins = buf; 150 } 151 152 if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) { 153 flags = (1U << 0); 154 } else if (gpio->IoRestriction == 155 ACPI_IO_RESTRICT_OUTPUT) { 156 flags = (1U << 1); 157 } else { 158 flags = (1U << 0) | (1U << 1); 159 } 160 for (i = 0; i < length; i++) { 161 if (GPIO_ALLOC_IO_PIN(provider, gpio->PinTable[idx + i], flags, 162 &pins[i]) != 0) { 163 device_printf(dev, "Failed to alloc GpioIo pin %u on " 164 "ResourceSource \"%s\"\n", gpio->PinTable[idx + i], 165 gpio->ResourceSource.StringPtr); 166 /* Release already alloc-ed pins */ 167 for (j = 0; j < i; j++) 168 GPIO_RELEASE_IO_PIN(provider, pins[j]); 169 goto err; 170 } 171 } 172 173 return (pins); 174 175 err: 176 if (buf == NULL) 177 kfree(pins, M_DEVBUF); 178 return (NULL); 179 } 180 181 /* 182 * GPIO Address space handler 183 */ 184 185 static void 186 gpio_acpi_install_address_space_handler(struct gpio_acpi_softc *sc) 187 { 188 struct acpi_gpio_handler_data *data = &sc->space_handler_data; 189 ACPI_HANDLE handle; 190 ACPI_STATUS s; 191 192 handle = acpi_get_handle(sc->parent); 193 data->dev = sc->parent; 194 s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, 195 &gpio_acpi_space_handler, NULL, data); 196 if (ACPI_FAILURE(s)) { 197 device_printf(sc->dev, 198 "Failed to install GPIO Address Space Handler in ACPI\n"); 199 } 200 } 201 202 static void 203 gpio_acpi_remove_address_space_handler(struct gpio_acpi_softc *sc) 204 { 205 ACPI_HANDLE handle; 206 ACPI_STATUS s; 207 208 handle = acpi_get_handle(sc->parent); 209 s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, 210 &gpio_acpi_space_handler); 211 if (ACPI_FAILURE(s)) { 212 device_printf(sc->dev, 213 "Failed to remove GPIO Address Space Handler from ACPI\n"); 214 } 215 } 216 217 static ACPI_STATUS 218 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, 219 UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext) 220 { 221 struct acpi_gpio_handler_data *data = HandlerContext; 222 device_t dev = data->dev; 223 struct acpi_connection_info *info = &data->info; 224 struct acpi_resource_gpio *gpio; 225 UINT64 val; 226 UINT8 action = Function & ACPI_IO_MASK; 227 ACPI_RESOURCE *Resource; 228 ACPI_STATUS s = AE_OK; 229 void **pins; 230 int i; 231 232 if (Value == NULL) 233 return (AE_BAD_PARAMETER); 234 235 /* XXX probably unnecessary */ 236 if (BitWidth == 0 || BitWidth > 64) 237 return (AE_BAD_PARAMETER); 238 239 s = AcpiBufferToResource(info->Connection, info->Length, &Resource); 240 if (ACPI_FAILURE(s)) { 241 device_printf(dev, "AcpiBufferToResource failed\n"); 242 return (s); 243 } 244 if (Resource->Type != ACPI_RESOURCE_TYPE_GPIO) { 245 device_printf(dev, "Resource->Type is wrong\n"); 246 s = AE_BAD_PARAMETER; 247 goto err; 248 } 249 gpio = &Resource->Data.Gpio; 250 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) { 251 device_printf(dev, "gpio->ConnectionType is wrong\n"); 252 s = AE_BAD_PARAMETER; 253 goto err; 254 } 255 256 if (Address + BitWidth > gpio->PinTableLength) { 257 device_printf(dev, "Address + BitWidth out of range\n"); 258 s = AE_BAD_ADDRESS; 259 goto err; 260 } 261 262 if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT && 263 action == ACPI_READ) { 264 device_printf(dev, 265 "IoRestriction is output only, but action is ACPI_READ\n"); 266 s = AE_BAD_PARAMETER; 267 goto err; 268 } 269 if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT && 270 action == ACPI_WRITE) { 271 device_printf(dev, 272 "IoRestriction is input only, but action is ACPI_WRITE\n"); 273 s = AE_BAD_PARAMETER; 274 goto err; 275 } 276 277 /* Make sure we can access all pins, before trying actual read/write */ 278 pins = gpioio_alloc_pins(dev, dev, gpio, Address, BitWidth, NULL); 279 if (pins == NULL) { 280 s = AE_BAD_PARAMETER; 281 goto err; 282 } 283 284 if (action == ACPI_READ) { 285 *Value = 0; 286 for (i = 0; i < BitWidth; i++) { 287 val = GPIO_READ_PIN(dev, pins[i]); 288 *Value |= val << i; 289 } 290 } else { 291 for (i = 0; i < BitWidth; i++) { 292 GPIO_WRITE_PIN(dev, pins[i], 293 (*Value & (1ULL << i)) ? 1 : 0); 294 } 295 } 296 for (i = 0; i < BitWidth; i++) 297 GPIO_RELEASE_IO_PIN(dev, pins[i]); 298 kfree(pins, M_DEVBUF); 299 300 err: 301 ACPI_FREE(Resource); 302 return (s); 303 } 304 305 /* 306 * ACPI Event Interrupts 307 */ 308 309 static void 310 gpio_acpi_handle_event(void *Context) 311 { 312 struct acpi_event_info *info = (struct acpi_event_info *)Context; 313 ACPI_HANDLE handle, h; 314 ACPI_STATUS s; 315 char buf[5]; 316 317 handle = acpi_get_handle(device_get_parent(info->dev)); 318 if (info->trigger == ACPI_EDGE_SENSITIVE) { 319 ksnprintf(buf, sizeof(buf), "_E%02X", info->pin); 320 } else { 321 ksnprintf(buf, sizeof(buf), "_L%02X", info->pin); 322 } 323 if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) { 324 s = AcpiEvaluateObject(handle, buf, NULL, NULL); 325 if (ACPI_FAILURE(s)) 326 device_printf(info->dev, "evaluating %s failed\n", buf); 327 } else { 328 ACPI_OBJECT_LIST arglist; 329 ACPI_OBJECT arg; 330 331 arglist.Pointer = &arg; 332 arglist.Count = 1; 333 arg.Type = ACPI_TYPE_INTEGER; 334 arg.Integer.Value = info->pin; 335 s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL); 336 if (ACPI_FAILURE(s)) 337 device_printf(info->dev, "evaluating _EVT failed\n"); 338 } 339 } 340 341 static void 342 gpio_acpi_aei_handler(void *arg) 343 { 344 struct acpi_event_info *info = (struct acpi_event_info *)arg; 345 ACPI_STATUS s; 346 347 s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg); 348 if (ACPI_FAILURE(s)) { 349 device_printf(info->dev, 350 "AcpiOsExecute for Acpi Event handler failed\n"); 351 } 352 } 353 354 static void 355 gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc, ACPI_RESOURCE_GPIO *gpio) 356 { 357 struct acpi_event_info *info = &sc->infos[sc->num_aei]; 358 uint16_t pin; 359 void *cookie; 360 361 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) { 362 device_printf(sc->dev, "Unexpected gpio type %d\n", 363 gpio->ConnectionType); 364 return; 365 } 366 367 /* sc->dev is correct here, since it's only used for device_printf */ 368 if (!gpio_acpi_check_gpioint(sc->dev, gpio)) 369 return; 370 371 pin = gpio->PinTable[0]; 372 373 if (GPIO_ALLOC_INTR(sc->parent, pin, gpio->Triggering, gpio->Polarity, 374 gpio->PinConfig, &cookie) != 0) { 375 device_printf(sc->dev, 376 "Failed to allocate AEI interrupt on pin %d\n", pin); 377 return; 378 } 379 380 info->dev = sc->dev; 381 info->pin = pin; 382 info->trigger = gpio->Triggering; 383 info->cookie = cookie; 384 385 GPIO_SETUP_INTR(sc->parent, cookie, info, gpio_acpi_aei_handler); 386 sc->num_aei++; 387 } 388 389 /* Map ACPI events */ 390 static void 391 gpio_acpi_map_aei(struct gpio_acpi_softc *sc) 392 { 393 ACPI_HANDLE handle = acpi_get_handle(sc->parent); 394 ACPI_RESOURCE_GPIO *gpio; 395 ACPI_RESOURCE *res, *end; 396 ACPI_BUFFER b; 397 ACPI_STATUS s; 398 int n; 399 400 sc->infos = NULL; 401 sc->num_aei = 0; 402 403 b.Pointer = NULL; 404 b.Length = ACPI_ALLOCATE_BUFFER; 405 s = AcpiGetEventResources(handle, &b); 406 if (ACPI_FAILURE(s)) 407 return; 408 409 end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length); 410 /* Count Gpio connections */ 411 n = 0; 412 for (res = (ACPI_RESOURCE *)b.Pointer; res < end; 413 res = ACPI_NEXT_RESOURCE(res)) { 414 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) { 415 break; 416 } else if (res->Type == ACPI_RESOURCE_TYPE_GPIO) { 417 n++; 418 } else { 419 device_printf(sc->dev, "Unexpected resource type %d\n", 420 res->Type); 421 } 422 } 423 if (n <= 0) { 424 AcpiOsFree(b.Pointer); 425 return; 426 } 427 sc->infos = kmalloc(n*sizeof(*sc->infos), M_DEVBUF, M_WAITOK | M_ZERO); 428 for (res = (ACPI_RESOURCE *)b.Pointer; res < end && sc->num_aei < n; 429 res = ACPI_NEXT_RESOURCE(res)) { 430 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) 431 break; 432 if (res->Type == ACPI_RESOURCE_TYPE_GPIO) { 433 gpio = (ACPI_RESOURCE_GPIO *)&res->Data; 434 gpio_acpi_do_map_aei(sc, gpio); 435 } 436 } 437 AcpiOsFree(b.Pointer); 438 } 439 440 /* Unmap ACPI events */ 441 static void 442 gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc) 443 { 444 struct acpi_event_info *info; 445 int i; 446 447 for (i = 0; i < sc->num_aei; i++) { 448 info = &sc->infos[i]; 449 KKASSERT(info->dev != NULL); 450 GPIO_TEARDOWN_INTR(sc->parent, info->cookie); 451 GPIO_FREE_INTR(sc->parent, info->cookie); 452 /* XXX Wait until ACPI Event handler has finished */ 453 memset(info, 0, sizeof(*info)); 454 } 455 kfree(sc->infos, M_DEVBUF); 456 sc->infos = NULL; 457 sc->num_aei = 0; 458 } 459 460 static int 461 gpio_acpi_probe(device_t dev) 462 { 463 if (acpi_get_handle(device_get_parent(dev)) == NULL) 464 return (ENXIO); 465 466 device_set_desc(dev, "ACPI GeneralPurposeIo backend"); 467 468 return (0); 469 } 470 471 static int 472 gpio_acpi_attach(device_t dev) 473 { 474 struct gpio_acpi_softc *sc = device_get_softc(dev); 475 476 sc->dev = dev; 477 sc->parent = device_get_parent(dev); 478 479 gpio_acpi_install_address_space_handler(sc); 480 481 gpio_acpi_map_aei(sc); 482 483 return (0); 484 } 485 486 static int 487 gpio_acpi_detach(device_t dev) 488 { 489 struct gpio_acpi_softc *sc = device_get_softc(dev); 490 491 if (sc->infos != NULL) 492 gpio_acpi_unmap_aei(sc); 493 494 gpio_acpi_remove_address_space_handler(sc); 495 496 return (0); 497 } 498 499 500 static device_method_t gpio_acpi_methods[] = { 501 /* Device interface */ 502 DEVMETHOD(device_probe, gpio_acpi_probe), 503 DEVMETHOD(device_attach, gpio_acpi_attach), 504 DEVMETHOD(device_detach, gpio_acpi_detach), 505 506 DEVMETHOD_END 507 }; 508 509 static driver_t gpio_acpi_driver = { 510 "gpio_acpi", 511 gpio_acpi_methods, 512 sizeof(struct gpio_acpi_softc) 513 }; 514 515 static devclass_t gpio_acpi_devclass; 516 517 DRIVER_MODULE(gpio_acpi, gpio_intel, gpio_acpi_driver, gpio_acpi_devclass, 518 NULL, NULL); 519 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1); 520 MODULE_VERSION(gpio_acpi, 1); 521