summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2014-03-14 11:58:07 -0400
committerLinus Walleij <linus.walleij@linaro.org>2014-03-14 12:25:02 -0400
commit473ed7be0da041275d57ab0bde1c21a6f23e637f (patch)
tree28b6085da8ad829a6453c91502d47a69f315046e
parenta176973ee83e561c0e4a9f3a2fcf6ab671e5e87b (diff)
gpio / ACPI: Add support for ACPI GPIO operation regions
GPIO operation regions is a new feature introduced in ACPI 5.0 specification. This feature adds a way for platform ASL code to call back to OS GPIO driver and toggle GPIO pins. An example ASL code from Lenovo Miix 2 tablet with only relevant part listed: Device (\_SB.GPO0) { Name (AVBL, Zero) Method (_REG, 2, NotSerialized) { If (LEqual (Arg0, 0x08)) { // Marks the region available Store (Arg1, AVBL) } } OperationRegion (GPOP, GeneralPurposeIo, Zero, 0x0C) Field (GPOP, ByteAcc, NoLock, Preserve) { Connection ( GpioIo (Exclusive, PullDefault, 0, 0, IoRestrictionOutputOnly, "\\_SB.GPO0", 0x00, ResourceConsumer,,) { 0x003B } ), SHD3, 1, } } Device (SHUB) { Method (_PS0, 0, Serialized) { If (LEqual (\_SB.GPO0.AVBL, One)) { Store (One, \_SB.GPO0.SHD3) Sleep (0x32) } } Method (_PS3, 0, Serialized) { If (LEqual (\_SB.GPO0.AVBL, One)) { Store (Zero, \_SB.GPO0.SHD3) } } } How this works is that whenever _PS0 or _PS3 method is run (typically when SHUB device is transitioned to D0 or D3 respectively), ASL code checks if the GPIO operation region is available (\_SB.GPO0.AVBL). If it is we go and store either 0 or 1 to \_SB.GPO0.SHD3. Now, when ACPICA notices ACPI GPIO operation region access (the store above) it will call acpi_gpio_adr_space_handler() that then toggles the GPIO accordingly using standard gpiolib interfaces. Implement the support by registering GPIO operation region handlers for all GPIO devices that have an ACPI handle. First time the GPIO is used by the ASL code we make sure that the GPIO stays requested until the GPIO chip driver itself is unloaded. If we find out that the GPIO is already requested we just toggle it according to the value got from ASL code. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/gpio/gpiolib-acpi.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 092ea4e5c9a8..bf0f8b476696 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -16,6 +16,7 @@
16#include <linux/export.h> 16#include <linux/export.h>
17#include <linux/acpi.h> 17#include <linux/acpi.h>
18#include <linux/interrupt.h> 18#include <linux/interrupt.h>
19#include <linux/mutex.h>
19 20
20#include "gpiolib.h" 21#include "gpiolib.h"
21 22
@@ -26,7 +27,20 @@ struct acpi_gpio_event {
26 unsigned int irq; 27 unsigned int irq;
27}; 28};
28 29
30struct acpi_gpio_connection {
31 struct list_head node;
32 struct gpio_desc *desc;
33};
34
29struct acpi_gpio_chip { 35struct acpi_gpio_chip {
36 /*
37 * ACPICA requires that the first field of the context parameter
38 * passed to acpi_install_address_space_handler() is large enough
39 * to hold struct acpi_connection_info.
40 */
41 struct acpi_connection_info conn_info;
42 struct list_head conns;
43 struct mutex conn_lock;
30 struct gpio_chip *chip; 44 struct gpio_chip *chip;
31 struct list_head events; 45 struct list_head events;
32}; 46};
@@ -334,6 +348,153 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
334 return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT); 348 return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
335} 349}
336 350
351static acpi_status
352acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
353 u32 bits, u64 *value, void *handler_context,
354 void *region_context)
355{
356 struct acpi_gpio_chip *achip = region_context;
357 struct gpio_chip *chip = achip->chip;
358 struct acpi_resource_gpio *agpio;
359 struct acpi_resource *ares;
360 acpi_status status;
361 bool pull_up;
362 int i;
363
364 status = acpi_buffer_to_resource(achip->conn_info.connection,
365 achip->conn_info.length, &ares);
366 if (ACPI_FAILURE(status))
367 return status;
368
369 if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) {
370 ACPI_FREE(ares);
371 return AE_BAD_PARAMETER;
372 }
373
374 agpio = &ares->data.gpio;
375 pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
376
377 if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
378 function == ACPI_WRITE)) {
379 ACPI_FREE(ares);
380 return AE_BAD_PARAMETER;
381 }
382
383 for (i = 0; i < agpio->pin_table_length; i++) {
384 unsigned pin = agpio->pin_table[i];
385 struct acpi_gpio_connection *conn;
386 struct gpio_desc *desc;
387 bool found;
388
389 desc = gpiochip_get_desc(chip, pin);
390 if (IS_ERR(desc)) {
391 status = AE_ERROR;
392 goto out;
393 }
394
395 mutex_lock(&achip->conn_lock);
396
397 found = false;
398 list_for_each_entry(conn, &achip->conns, node) {
399 if (conn->desc == desc) {
400 found = true;
401 break;
402 }
403 }
404 if (!found) {
405 int ret;
406
407 ret = gpiochip_request_own_desc(desc, "ACPI:OpRegion");
408 if (ret) {
409 status = AE_ERROR;
410 mutex_unlock(&achip->conn_lock);
411 goto out;
412 }
413
414 switch (agpio->io_restriction) {
415 case ACPI_IO_RESTRICT_INPUT:
416 gpiod_direction_input(desc);
417 break;
418 case ACPI_IO_RESTRICT_OUTPUT:
419 /*
420 * ACPI GPIO resources don't contain an
421 * initial value for the GPIO. Therefore we
422 * deduce that value from the pull field
423 * instead. If the pin is pulled up we
424 * assume default to be high, otherwise
425 * low.
426 */
427 gpiod_direction_output(desc, pull_up);
428 break;
429 default:
430 /*
431 * Assume that the BIOS has configured the
432 * direction and pull accordingly.
433 */
434 break;
435 }
436
437 conn = kzalloc(sizeof(*conn), GFP_KERNEL);
438 if (!conn) {
439 status = AE_NO_MEMORY;
440 gpiochip_free_own_desc(desc);
441 mutex_unlock(&achip->conn_lock);
442 goto out;
443 }
444
445 conn->desc = desc;
446 list_add_tail(&conn->node, &achip->conns);
447 }
448
449 mutex_unlock(&achip->conn_lock);
450
451 if (function == ACPI_WRITE)
452 gpiod_set_raw_value(desc, !!((1 << i) & *value));
453 else
454 *value |= gpiod_get_raw_value(desc) << i;
455 }
456
457out:
458 ACPI_FREE(ares);
459 return status;
460}
461
462static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip)
463{
464 struct gpio_chip *chip = achip->chip;
465 acpi_handle handle = ACPI_HANDLE(chip->dev);
466 acpi_status status;
467
468 INIT_LIST_HEAD(&achip->conns);
469 mutex_init(&achip->conn_lock);
470 status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
471 acpi_gpio_adr_space_handler,
472 NULL, achip);
473 if (ACPI_FAILURE(status))
474 dev_err(chip->dev, "Failed to install GPIO OpRegion handler\n");
475}
476
477static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
478{
479 struct gpio_chip *chip = achip->chip;
480 acpi_handle handle = ACPI_HANDLE(chip->dev);
481 struct acpi_gpio_connection *conn, *tmp;
482 acpi_status status;
483
484 status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO,
485 acpi_gpio_adr_space_handler);
486 if (ACPI_FAILURE(status)) {
487 dev_err(chip->dev, "Failed to remove GPIO OpRegion handler\n");
488 return;
489 }
490
491 list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) {
492 gpiochip_free_own_desc(conn->desc);
493 list_del(&conn->node);
494 kfree(conn);
495 }
496}
497
337void acpi_gpiochip_add(struct gpio_chip *chip) 498void acpi_gpiochip_add(struct gpio_chip *chip)
338{ 499{
339 struct acpi_gpio_chip *acpi_gpio; 500 struct acpi_gpio_chip *acpi_gpio;
@@ -361,6 +522,7 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
361 } 522 }
362 523
363 acpi_gpiochip_request_interrupts(acpi_gpio); 524 acpi_gpiochip_request_interrupts(acpi_gpio);
525 acpi_gpiochip_request_regions(acpi_gpio);
364} 526}
365 527
366void acpi_gpiochip_remove(struct gpio_chip *chip) 528void acpi_gpiochip_remove(struct gpio_chip *chip)
@@ -379,6 +541,7 @@ void acpi_gpiochip_remove(struct gpio_chip *chip)
379 return; 541 return;
380 } 542 }
381 543
544 acpi_gpiochip_free_regions(acpi_gpio);
382 acpi_gpiochip_free_interrupts(acpi_gpio); 545 acpi_gpiochip_free_interrupts(acpi_gpio);
383 546
384 acpi_detach_data(handle, acpi_gpio_chip_dh); 547 acpi_detach_data(handle, acpi_gpio_chip_dh);