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