diff mbox

[RFC] gpiolib-acpi: Add ACPI5 event model support to gpio.

Message ID 1359114505-4473-1-git-send-email-mathias.nyman@linux.intel.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Mathias Nyman Jan. 25, 2013, 11:48 a.m. UTC
Add ability to handle ACPI events signalled by GPIO interrupts.

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
controller's interrupt handler. acpi_gpio_request_interrupt() finds out which
gpio pins have acpi event methods and assigns interrupt handlers that calls
the acpi event methods for those pins.

Partially based on work by Rui Zhang <rui.zhang@intel.com>

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/gpio/gpiolib-acpi.c |   80 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/acpi_gpio.h   |    4 ++
 2 files changed, 84 insertions(+), 0 deletions(-)

Comments

Rafael Wysocki Jan. 25, 2013, 12:25 p.m. UTC | #1
On Friday, January 25, 2013 01:48:25 PM Mathias Nyman wrote:
> Add ability to handle ACPI events signalled by GPIO interrupts.
> 
> 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
> controller's interrupt handler. acpi_gpio_request_interrupt() finds out which
> gpio pins have acpi event methods and assigns interrupt handlers that calls
> the acpi event methods for those pins.
> 
> Partially based on work by Rui Zhang <rui.zhang@intel.com>
> 
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

> ---
>  drivers/gpio/gpiolib-acpi.c |   80 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/acpi_gpio.h   |    4 ++
>  2 files changed, 84 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
> index cbad6e9..b6ab041 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,82 @@ 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_gpio_request_interrupt() - Register isr for gpio controller ACPI events
> + * @chip:      gpio chip representation of the gpio controller
> + *
> + * 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
> + * controller's interrupt handler. acpi_gpio_request_interrupt finds out which
> + * gpio pins have acpi event methods and assigns interrupt handlers that calls
> + * the acpi event methods for those pins.
> + */
> +
> +void acpi_gpio_request_interrupt(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, irq;
> +	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 */
> +		devm_request_threaded_irq(chip->dev, irq, NULL,
> +					  acpi_gpio_irq_handler,
> +					  0,
> +					  "GPIO-signaled-ACPI-event",
> +					  ev_handle);
> +	}
> +}
> +EXPORT_SYMBOL(acpi_gpio_request_interrupt);
> diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h
> index 91615a3..43c541d 100644
> --- a/include/linux/acpi_gpio.h
> +++ b/include/linux/acpi_gpio.h
> @@ -2,10 +2,12 @@
>  #define _LINUX_ACPI_GPIO_H_
>  
>  #include <linux/errno.h>
> +#include <linux/gpio.h>
>  
>  #ifdef CONFIG_GPIO_ACPI
>  
>  int acpi_get_gpio(char *path, int pin);
> +void acpi_gpio_request_interrupt(struct gpio_chip *chip);
>  
>  #else /* CONFIG_GPIO_ACPI */
>  
> @@ -14,6 +16,8 @@ static inline int acpi_get_gpio(char *path, int pin)
>  	return -ENODEV;
>  }
>  
> +static inline void acpi_gpio_request_interrupt(struct gpio_chip *chip) { }
> +
>  #endif /* CONFIG_GPIO_ACPI */
>  
>  #endif /* _LINUX_ACPI_GPIO_H_ */
>
Linus Walleij Jan. 25, 2013, 12:54 p.m. UTC | #2
On Fri, Jan 25, 2013 at 12:48 PM, Mathias Nyman
<mathias.nyman@linux.intel.com> wrote:

> Add ability to handle ACPI events signalled by GPIO interrupts.
>
> 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
> controller's interrupt handler. acpi_gpio_request_interrupt() finds out which
> gpio pins have acpi event methods and assigns interrupt handlers that calls
> the acpi event methods for those pins.
>
> Partially based on work by Rui Zhang <rui.zhang@intel.com>
>
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
(...)
> +/**
> + * acpi_gpio_request_interrupt() - Register isr for gpio controller ACPI events
> + * @chip:      gpio chip representation of the gpio controller


Hm chip, controller, controller, chip chip, controller controller...
Are we using two different names for the same thing?

> + *
> + * 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
> + * controller's interrupt handler. acpi_gpio_request_interrupt finds out which
> + * gpio pins have acpi event methods and assigns interrupt handlers that calls
> + * the acpi event methods for those pins.
> + */
> +
> +void acpi_gpio_request_interrupt(struct gpio_chip *chip)

So I was like "um, what acpi requests an interrupt for a GPIO (maybe a pin)...

... read read ...

Aha the function should probably be named:
acpi_gpiochip_request_interrupts()

Because it just grabs all IRQs coming from that chip right?

Second: why is there no mirror function *releasing* all the IRQs again?
One-way interface?

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mathias Nyman Jan. 25, 2013, 2:05 p.m. UTC | #3
On 01/25/2013 02:54 PM, Linus Walleij wrote:
> On Fri, Jan 25, 2013 at 12:48 PM, Mathias Nyman
> <mathias.nyman@linux.intel.com>  wrote:
>
>> Add ability to handle ACPI events signalled by GPIO interrupts.
>>
>> 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
>> controller's interrupt handler. acpi_gpio_request_interrupt() finds out which
>> gpio pins have acpi event methods and assigns interrupt handlers that calls
>> the acpi event methods for those pins.
>>
>> Partially based on work by Rui Zhang<rui.zhang@intel.com>
>>
>> Signed-off-by: Mathias Nyman<mathias.nyman@linux.intel.com>
> (...)
>> +/**
>> + * acpi_gpio_request_interrupt() - Register isr for gpio controller ACPI events
>> + * @chip:      gpio chip representation of the gpio controller
>
>
> Hm chip, controller, controller, chip chip, controller controller...
> Are we using two different names for the same thing?

They get mixed up a bit here yes.
I'll change them all to 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
>> + * controller's interrupt handler. acpi_gpio_request_interrupt finds out which
>> + * gpio pins have acpi event methods and assigns interrupt handlers that calls
>> + * the acpi event methods for those pins.
>> + */
>> +
>> +void acpi_gpio_request_interrupt(struct gpio_chip *chip)
>
> So I was like "um, what acpi requests an interrupt for a GPIO (maybe a pin)...
>
> ... read read ...
>
> Aha the function should probably be named:
> acpi_gpiochip_request_interrupts()
>

True, will rename it.

> Because it just grabs all IRQs coming from that chip right?

Not all, only the ones that have an ACPI handler method defined. 
Basically ACPI5 supports using some of the gpio interrupts for the same 
purpose as SCI interrupts.

>
> Second: why is there no mirror function *releasing* all the IRQs again?
> One-way interface?

My thinking was that using devm_request_threaded_irq(chip->dev, ...) 
will automatically free the interrupts on driver detach, making the 
release function unnecessary.

-Mathias

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Linus Walleij Jan. 28, 2013, 8:30 a.m. UTC | #4
On Fri, Jan 25, 2013 at 3:05 PM, Mathias Nyman
<mathias.nyman@linux.intel.com> wrote:
> On 01/25/2013 02:54 PM, Linus Walleij wrote:

>> Second: why is there no mirror function *releasing* all the IRQs again?
>> One-way interface?
>
> My thinking was that using devm_request_threaded_irq(chip->dev, ...) will
> automatically free the interrupts on driver detach, making the release
> function unnecessary.

Oh that's probably correct. Thanks!

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cbad6e9..b6ab041 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,82 @@  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_gpio_request_interrupt() - Register isr for gpio controller ACPI events
+ * @chip:      gpio chip representation of the gpio controller
+ *
+ * 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
+ * controller's interrupt handler. acpi_gpio_request_interrupt finds out which
+ * gpio pins have acpi event methods and assigns interrupt handlers that calls
+ * the acpi event methods for those pins.
+ */
+
+void acpi_gpio_request_interrupt(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, irq;
+	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 */
+		devm_request_threaded_irq(chip->dev, irq, NULL,
+					  acpi_gpio_irq_handler,
+					  0,
+					  "GPIO-signaled-ACPI-event",
+					  ev_handle);
+	}
+}
+EXPORT_SYMBOL(acpi_gpio_request_interrupt);
diff --git a/include/linux/acpi_gpio.h b/include/linux/acpi_gpio.h
index 91615a3..43c541d 100644
--- a/include/linux/acpi_gpio.h
+++ b/include/linux/acpi_gpio.h
@@ -2,10 +2,12 @@ 
 #define _LINUX_ACPI_GPIO_H_
 
 #include <linux/errno.h>
+#include <linux/gpio.h>
 
 #ifdef CONFIG_GPIO_ACPI
 
 int acpi_get_gpio(char *path, int pin);
+void acpi_gpio_request_interrupt(struct gpio_chip *chip);
 
 #else /* CONFIG_GPIO_ACPI */
 
@@ -14,6 +16,8 @@  static inline int acpi_get_gpio(char *path, int pin)
 	return -ENODEV;
 }
 
+static inline void acpi_gpio_request_interrupt(struct gpio_chip *chip) { }
+
 #endif /* CONFIG_GPIO_ACPI */
 
 #endif /* _LINUX_ACPI_GPIO_H_ */