summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib-acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib-acpi.c')
-rw-r--r--drivers/gpio/gpiolib-acpi.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cbad6e908d30..a063eb04b6ce 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -15,6 +15,7 @@
#include <linux/export.h>
#include <linux/acpi_gpio.h>
#include <linux/acpi.h>
+#include <linux/interrupt.h>
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{
@@ -52,3 +53,89 @@ int acpi_get_gpio(char *path, int pin)
return chip->base + pin;
}
EXPORT_SYMBOL_GPL(acpi_get_gpio);
+
+
+static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
+{
+ acpi_handle handle = data;
+
+ acpi_evaluate_object(handle, NULL, NULL, NULL);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
+ * @chip: gpio chip
+ *
+ * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
+ * handled by ACPI event methods which need to be called from the GPIO
+ * chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
+ * gpio pins have acpi event methods and assigns interrupt handlers that calls
+ * the acpi event methods for those pins.
+ *
+ * Interrupts are automatically freed on driver detach
+ */
+
+void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
+{
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_resource *res;
+ acpi_handle handle, ev_handle;
+ acpi_status status;
+ unsigned int pin;
+ int irq, ret;
+ char ev_name[5];
+
+ if (!chip->dev || !chip->to_irq)
+ return;
+
+ handle = ACPI_HANDLE(chip->dev);
+ if (!handle)
+ return;
+
+ status = acpi_get_event_resources(handle, &buf);
+ if (ACPI_FAILURE(status))
+ return;
+
+ /* If a gpio interrupt has an acpi event handler method, then
+ * set up an interrupt handler that calls the acpi event handler
+ */
+
+ for (res = buf.pointer;
+ res && (res->type != ACPI_RESOURCE_TYPE_END_TAG);
+ res = ACPI_NEXT_RESOURCE(res)) {
+
+ if (res->type != ACPI_RESOURCE_TYPE_GPIO ||
+ res->data.gpio.connection_type !=
+ ACPI_RESOURCE_GPIO_TYPE_INT)
+ continue;
+
+ pin = res->data.gpio.pin_table[0];
+ if (pin > chip->ngpio)
+ continue;
+
+ sprintf(ev_name, "_%c%02X",
+ res->data.gpio.triggering ? 'E' : 'L', pin);
+
+ status = acpi_get_handle(handle, ev_name, &ev_handle);
+ if (ACPI_FAILURE(status))
+ continue;
+
+ irq = chip->to_irq(chip, pin);
+ if (irq < 0)
+ continue;
+
+ /* Assume BIOS sets the triggering, so no flags */
+ ret = devm_request_threaded_irq(chip->dev, irq, NULL,
+ acpi_gpio_irq_handler,
+ 0,
+ "GPIO-signaled-ACPI-event",
+ ev_handle);
+ if (ret)
+ dev_err(chip->dev,
+ "Failed to request IRQ %d ACPI event handler\n",
+ irq);
+ }
+}
+EXPORT_SYMBOL(acpi_gpiochip_request_interrupts);