2012-11-30 15:37:36 +04:00
/*
* ACPI helpers for GPIO API
*
* Copyright ( C ) 2012 , Intel Corporation
* Authors : Mathias Nyman < mathias . nyman @ linux . intel . com >
* Mika Westerberg < mika . westerberg @ linux . intel . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/errno.h>
# include <linux/gpio.h>
# include <linux/export.h>
# include <linux/acpi_gpio.h>
# include <linux/acpi.h>
2013-01-28 18:23:10 +04:00
# include <linux/interrupt.h>
2012-11-30 15:37:36 +04:00
static int acpi_gpiochip_find ( struct gpio_chip * gc , void * data )
{
if ( ! gc - > dev )
return false ;
return ACPI_HANDLE ( gc - > dev ) = = data ;
}
/**
* acpi_get_gpio ( ) - Translate ACPI GPIO pin to GPIO number usable with GPIO API
* @ path : ACPI GPIO controller full path name , ( e . g . " \\ _SB.GPO1 " )
* @ pin : ACPI GPIO pin number ( 0 - based , controller - relative )
*
* Returns GPIO number to use with Linux generic GPIO API , or errno error value
*/
int acpi_get_gpio ( char * path , int pin )
{
struct gpio_chip * chip ;
acpi_handle handle ;
acpi_status status ;
status = acpi_get_handle ( NULL , path , & handle ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
chip = gpiochip_find ( handle , acpi_gpiochip_find ) ;
if ( ! chip )
return - ENODEV ;
if ( ! gpio_is_valid ( chip - > base + pin ) )
return - EINVAL ;
return chip - > base + pin ;
}
EXPORT_SYMBOL_GPL ( acpi_get_gpio ) ;
2013-01-28 18:23:10 +04:00
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 ;
2013-02-04 13:32:22 +04:00
unsigned int pin ;
int irq , ret ;
2013-01-28 18:23:10 +04:00
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 */
2013-02-04 13:32:22 +04:00
ret = devm_request_threaded_irq ( chip - > dev , irq , NULL ,
2013-01-28 18:23:10 +04:00
acpi_gpio_irq_handler ,
0 ,
" GPIO-signaled-ACPI-event " ,
ev_handle ) ;
2013-02-04 13:32:22 +04:00
if ( ret )
dev_err ( chip - > dev ,
" Failed to request IRQ %d ACPI event handler \n " ,
irq ) ;
2013-01-28 18:23:10 +04:00
}
}
EXPORT_SYMBOL ( acpi_gpiochip_request_interrupts ) ;