diff options
Diffstat (limited to 'drivers/gpio/gpiolib-acpi.c')
| -rw-r--r-- | drivers/gpio/gpiolib-acpi.c | 87 | 
1 files changed, 87 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index cbad6e908d3..a063eb04b6c 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);  |