aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpio/gpiolib-acpi.c82
-rw-r--r--include/linux/acpi_gpio.h4
2 files changed, 86 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cbad6e908d30..54ce2269ed25 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -15,6 +15,7 @@
15#include <linux/export.h> 15#include <linux/export.h>
16#include <linux/acpi_gpio.h> 16#include <linux/acpi_gpio.h>
17#include <linux/acpi.h> 17#include <linux/acpi.h>
18#include <linux/interrupt.h>
18 19
19static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) 20static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
20{ 21{
@@ -52,3 +53,84 @@ int acpi_get_gpio(char *path, int pin)
52 return chip->base + pin; 53 return chip->base + pin;
53} 54}
54EXPORT_SYMBOL_GPL(acpi_get_gpio); 55EXPORT_SYMBOL_GPL(acpi_get_gpio);
56
57
58static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
59{
60 acpi_handle handle = data;
61
62 acpi_evaluate_object(handle, NULL, NULL, NULL);
63
64 return IRQ_HANDLED;
65}
66
67/**
68 * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events
69 * @chip: gpio chip
70 *
71 * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are
72 * handled by ACPI event methods which need to be called from the GPIO
73 * chip's interrupt handler. acpi_gpiochip_request_interrupts finds out which
74 * gpio pins have acpi event methods and assigns interrupt handlers that calls
75 * the acpi event methods for those pins.
76 *
77 * Interrupts are automatically freed on driver detach
78 */
79
80void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
81{
82 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
83 struct acpi_resource *res;
84 acpi_handle handle, ev_handle;
85 acpi_status status;
86 unsigned int pin, irq;
87 char ev_name[5];
88
89 if (!chip->dev || !chip->to_irq)
90 return;
91
92 handle = ACPI_HANDLE(chip->dev);
93 if (!handle)
94 return;
95
96 status = acpi_get_event_resources(handle, &buf);
97 if (ACPI_FAILURE(status))
98 return;
99
100 /* If a gpio interrupt has an acpi event handler method, then
101 * set up an interrupt handler that calls the acpi event handler
102 */
103
104 for (res = buf.pointer;
105 res && (res->type != ACPI_RESOURCE_TYPE_END_TAG);
106 res = ACPI_NEXT_RESOURCE(res)) {
107
108 if (res->type != ACPI_RESOURCE_TYPE_GPIO ||
109 res->data.gpio.connection_type !=
110 ACPI_RESOURCE_GPIO_TYPE_INT)
111 continue;
112
113 pin = res->data.gpio.pin_table[0];
114 if (pin > chip->ngpio)
115 continue;
116
117 sprintf(ev_name, "_%c%02X",
118 res->data.gpio.triggering ? 'E' : 'L', pin);
119
120 status = acpi_get_handle(handle, ev_name, &ev_handle);
121 if (ACPI_FAILURE(status))
122 continue;
123
124 irq = chip->to_irq(chip, pin);
125 if (irq < 0)
126 continue;
127
128 /* Assume BIOS sets the triggering, so no flags */
129 devm_request_threaded_irq(chip->dev, irq, NULL,
130 acpi_gpio_irq_handler,
131 0,
132 "GPIO-signaled-ACPI-event",
133 ev_handle);
134 }
135}
136EXPORT_SYMBOL(acpi_gpiochip_request_interrupts);
diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h
index 91615a389b65..b76ebd08ff8e 100644
--- a/include/linux/acpi_gpio.h
+++ b/include/linux/acpi_gpio.h
@@ -2,10 +2,12 @@
2#define _LINUX_ACPI_GPIO_H_ 2#define _LINUX_ACPI_GPIO_H_
3 3
4#include <linux/errno.h> 4#include <linux/errno.h>
5#include <linux/gpio.h>
5 6
6#ifdef CONFIG_GPIO_ACPI 7#ifdef CONFIG_GPIO_ACPI
7 8
8int acpi_get_gpio(char *path, int pin); 9int acpi_get_gpio(char *path, int pin);
10void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
9 11
10#else /* CONFIG_GPIO_ACPI */ 12#else /* CONFIG_GPIO_ACPI */
11 13
@@ -14,6 +16,8 @@ static inline int acpi_get_gpio(char *path, int pin)
14 return -ENODEV; 16 return -ENODEV;
15} 17}
16 18
19static inline void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
20
17#endif /* CONFIG_GPIO_ACPI */ 21#endif /* CONFIG_GPIO_ACPI */
18 22
19#endif /* _LINUX_ACPI_GPIO_H_ */ 23#endif /* _LINUX_ACPI_GPIO_H_ */