2007-01-18 08:44:09 +03:00
/*
* Driver for keys on GPIO lines capable of generating interrupts .
*
* Copyright 2005 Phil Blundell
*
* 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/module.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/sched.h>
# include <linux/pm.h>
# include <linux/sysctl.h>
# include <linux/proc_fs.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/input.h>
2007-03-05 11:30:22 +03:00
# include <linux/gpio_keys.h>
2007-01-18 08:44:09 +03:00
2007-02-18 09:40:46 +03:00
# include <asm/gpio.h>
2007-01-18 08:44:09 +03:00
2008-05-08 00:30:15 +04:00
struct gpio_button_data {
struct gpio_keys_button * button ;
struct input_dev * input ;
struct timer_list timer ;
} ;
struct gpio_keys_drvdata {
struct input_dev * input ;
struct gpio_button_data data [ 0 ] ;
} ;
2008-08-08 20:14:36 +04:00
static void gpio_keys_report_event ( struct gpio_button_data * bdata )
2008-05-08 00:30:15 +04:00
{
2008-08-08 20:14:36 +04:00
struct gpio_keys_button * button = bdata - > button ;
struct input_dev * input = bdata - > input ;
2008-05-08 00:30:15 +04:00
unsigned int type = button - > type ? : EV_KEY ;
int state = ( gpio_get_value ( button - > gpio ) ? 1 : 0 ) ^ button - > active_low ;
input_event ( input , type , button - > code , ! ! state ) ;
input_sync ( input ) ;
}
static void gpio_check_button ( unsigned long _data )
{
struct gpio_button_data * data = ( struct gpio_button_data * ) _data ;
2008-08-08 20:14:36 +04:00
gpio_keys_report_event ( data ) ;
2008-05-08 00:30:15 +04:00
}
2007-01-18 08:44:09 +03:00
static irqreturn_t gpio_keys_isr ( int irq , void * dev_id )
{
2008-08-08 20:14:34 +04:00
struct gpio_button_data * bdata = dev_id ;
struct gpio_keys_button * button = bdata - > button ;
2007-01-18 08:44:09 +03:00
2008-08-08 20:14:34 +04:00
BUG_ON ( irq ! = gpio_to_irq ( button - > gpio ) ) ;
2007-05-01 08:39:13 +04:00
2008-08-08 20:14:34 +04:00
if ( button - > debounce_interval )
mod_timer ( & bdata - > timer ,
jiffies + msecs_to_jiffies ( button - > debounce_interval ) ) ;
else
2008-08-08 20:14:36 +04:00
gpio_keys_report_event ( bdata ) ;
2007-01-18 08:44:09 +03:00
2008-08-08 20:14:34 +04:00
return IRQ_HANDLED ;
2007-01-18 08:44:09 +03:00
}
static int __devinit gpio_keys_probe ( struct platform_device * pdev )
{
struct gpio_keys_platform_data * pdata = pdev - > dev . platform_data ;
2008-05-08 00:30:15 +04:00
struct gpio_keys_drvdata * ddata ;
2007-01-18 08:44:09 +03:00
struct input_dev * input ;
int i , error ;
2007-09-26 08:01:17 +04:00
int wakeup = 0 ;
2007-01-18 08:44:09 +03:00
2008-05-08 00:30:15 +04:00
ddata = kzalloc ( sizeof ( struct gpio_keys_drvdata ) +
pdata - > nbuttons * sizeof ( struct gpio_button_data ) ,
GFP_KERNEL ) ;
2007-01-18 08:44:09 +03:00
input = input_allocate_device ( ) ;
2008-05-08 00:30:15 +04:00
if ( ! ddata | | ! input ) {
error = - ENOMEM ;
goto fail1 ;
}
2007-01-18 08:44:09 +03:00
2008-05-08 00:30:15 +04:00
platform_set_drvdata ( pdev , ddata ) ;
2007-01-18 08:44:09 +03:00
input - > name = pdev - > name ;
input - > phys = " gpio-keys/input0 " ;
2007-04-12 09:34:58 +04:00
input - > dev . parent = & pdev - > dev ;
2007-01-18 08:44:09 +03:00
input - > id . bustype = BUS_HOST ;
input - > id . vendor = 0x0001 ;
input - > id . product = 0x0001 ;
input - > id . version = 0x0100 ;
2008-10-28 05:30:53 +03:00
/* Enable auto repeat feature of Linux input subsystem */
if ( pdata - > rep )
__set_bit ( EV_REP , input - > evbit ) ;
2008-05-08 00:30:15 +04:00
ddata - > input = input ;
2007-01-18 08:44:09 +03:00
for ( i = 0 ; i < pdata - > nbuttons ; i + + ) {
2007-05-01 08:39:13 +04:00
struct gpio_keys_button * button = & pdata - > buttons [ i ] ;
2008-05-08 00:30:15 +04:00
struct gpio_button_data * bdata = & ddata - > data [ i ] ;
2007-11-21 22:42:33 +03:00
int irq ;
2007-05-01 08:39:13 +04:00
unsigned int type = button - > type ? : EV_KEY ;
2007-01-18 08:44:09 +03:00
2008-05-08 00:30:15 +04:00
bdata - > input = input ;
2008-07-30 18:33:43 +04:00
bdata - > button = button ;
2008-05-08 00:30:15 +04:00
setup_timer ( & bdata - > timer ,
gpio_check_button , ( unsigned long ) bdata ) ;
2007-11-21 22:42:33 +03:00
error = gpio_request ( button - > gpio , button - > desc ? : " gpio_keys " ) ;
if ( error < 0 ) {
pr_err ( " gpio-keys: failed to request GPIO %d, "
" error %d \n " , button - > gpio , error ) ;
2008-05-08 00:30:15 +04:00
goto fail2 ;
2007-11-21 22:42:33 +03:00
}
error = gpio_direction_input ( button - > gpio ) ;
if ( error < 0 ) {
pr_err ( " gpio-keys: failed to configure input "
" direction for GPIO %d, error %d \n " ,
button - > gpio , error ) ;
gpio_free ( button - > gpio ) ;
2008-05-08 00:30:15 +04:00
goto fail2 ;
2007-11-21 22:42:33 +03:00
}
irq = gpio_to_irq ( button - > gpio ) ;
2007-09-26 08:01:03 +04:00
if ( irq < 0 ) {
error = irq ;
2007-11-21 22:42:33 +03:00
pr_err ( " gpio-keys: Unable to get irq number "
" for GPIO %d, error %d \n " ,
2007-09-26 08:01:03 +04:00
button - > gpio , error ) ;
2007-11-21 22:42:33 +03:00
gpio_free ( button - > gpio ) ;
2008-05-08 00:30:15 +04:00
goto fail2 ;
2007-09-26 08:01:03 +04:00
}
error = request_irq ( irq , gpio_keys_isr ,
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING ,
button - > desc ? button - > desc : " gpio_keys " ,
2008-08-08 20:14:34 +04:00
bdata ) ;
2007-01-18 08:44:09 +03:00
if ( error ) {
2007-11-21 22:42:33 +03:00
pr_err ( " gpio-keys: Unable to claim irq %d; error %d \n " ,
2007-02-18 09:40:46 +03:00
irq , error ) ;
2007-11-21 22:42:33 +03:00
gpio_free ( button - > gpio ) ;
2008-05-08 00:30:15 +04:00
goto fail2 ;
2007-01-18 08:44:09 +03:00
}
2007-05-01 08:39:13 +04:00
2007-09-26 08:01:17 +04:00
if ( button - > wakeup )
wakeup = 1 ;
2007-05-01 08:39:13 +04:00
input_set_capability ( input , type , button - > code ) ;
2007-01-18 08:44:09 +03:00
}
error = input_register_device ( input ) ;
if ( error ) {
2007-11-21 22:42:33 +03:00
pr_err ( " gpio-keys: Unable to register input device, "
2007-09-26 08:01:03 +04:00
" error: %d \n " , error ) ;
2008-05-08 00:30:15 +04:00
goto fail2 ;
2007-01-18 08:44:09 +03:00
}
2007-09-26 08:01:17 +04:00
device_init_wakeup ( & pdev - > dev , wakeup ) ;
2007-01-18 08:44:09 +03:00
return 0 ;
2008-05-08 00:30:15 +04:00
fail2 :
2007-11-21 22:42:33 +03:00
while ( - - i > = 0 ) {
2008-08-08 20:14:34 +04:00
free_irq ( gpio_to_irq ( pdata - > buttons [ i ] . gpio ) , & ddata - > data [ i ] ) ;
2008-05-08 00:30:15 +04:00
if ( pdata - > buttons [ i ] . debounce_interval )
del_timer_sync ( & ddata - > data [ i ] . timer ) ;
2007-11-21 22:42:33 +03:00
gpio_free ( pdata - > buttons [ i ] . gpio ) ;
}
2007-01-18 08:44:09 +03:00
2007-09-26 08:01:03 +04:00
platform_set_drvdata ( pdev , NULL ) ;
2008-05-08 00:30:15 +04:00
fail1 :
2007-01-18 08:44:09 +03:00
input_free_device ( input ) ;
2008-05-08 00:30:15 +04:00
kfree ( ddata ) ;
2007-01-18 08:44:09 +03:00
return error ;
}
static int __devexit gpio_keys_remove ( struct platform_device * pdev )
{
struct gpio_keys_platform_data * pdata = pdev - > dev . platform_data ;
2008-05-08 00:30:15 +04:00
struct gpio_keys_drvdata * ddata = platform_get_drvdata ( pdev ) ;
struct input_dev * input = ddata - > input ;
2007-01-18 08:44:09 +03:00
int i ;
2007-09-26 08:01:17 +04:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
2007-01-18 08:44:09 +03:00
for ( i = 0 ; i < pdata - > nbuttons ; i + + ) {
2007-02-18 09:40:46 +03:00
int irq = gpio_to_irq ( pdata - > buttons [ i ] . gpio ) ;
2008-08-08 20:14:34 +04:00
free_irq ( irq , & ddata - > data [ i ] ) ;
2008-05-08 00:30:15 +04:00
if ( pdata - > buttons [ i ] . debounce_interval )
del_timer_sync ( & ddata - > data [ i ] . timer ) ;
2007-11-21 22:42:33 +03:00
gpio_free ( pdata - > buttons [ i ] . gpio ) ;
2007-01-18 08:44:09 +03:00
}
input_unregister_device ( input ) ;
return 0 ;
}
2007-09-26 08:01:17 +04:00
# ifdef CONFIG_PM
static int gpio_keys_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct gpio_keys_platform_data * pdata = pdev - > dev . platform_data ;
int i ;
if ( device_may_wakeup ( & pdev - > dev ) ) {
for ( i = 0 ; i < pdata - > nbuttons ; i + + ) {
struct gpio_keys_button * button = & pdata - > buttons [ i ] ;
if ( button - > wakeup ) {
int irq = gpio_to_irq ( button - > gpio ) ;
enable_irq_wake ( irq ) ;
}
}
}
return 0 ;
}
static int gpio_keys_resume ( struct platform_device * pdev )
{
struct gpio_keys_platform_data * pdata = pdev - > dev . platform_data ;
int i ;
if ( device_may_wakeup ( & pdev - > dev ) ) {
for ( i = 0 ; i < pdata - > nbuttons ; i + + ) {
struct gpio_keys_button * button = & pdata - > buttons [ i ] ;
if ( button - > wakeup ) {
int irq = gpio_to_irq ( button - > gpio ) ;
disable_irq_wake ( irq ) ;
}
}
}
return 0 ;
}
# else
# define gpio_keys_suspend NULL
# define gpio_keys_resume NULL
# endif
2008-07-30 18:34:02 +04:00
static struct platform_driver gpio_keys_device_driver = {
2007-01-18 08:44:09 +03:00
. probe = gpio_keys_probe ,
. remove = __devexit_p ( gpio_keys_remove ) ,
2007-09-26 08:01:17 +04:00
. suspend = gpio_keys_suspend ,
. resume = gpio_keys_resume ,
2007-01-18 08:44:09 +03:00
. driver = {
. name = " gpio-keys " ,
2008-04-18 08:24:42 +04:00
. owner = THIS_MODULE ,
2007-01-18 08:44:09 +03:00
}
} ;
static int __init gpio_keys_init ( void )
{
return platform_driver_register ( & gpio_keys_device_driver ) ;
}
static void __exit gpio_keys_exit ( void )
{
platform_driver_unregister ( & gpio_keys_device_driver ) ;
}
module_init ( gpio_keys_init ) ;
module_exit ( gpio_keys_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Phil Blundell <pb@handhelds.org> " ) ;
MODULE_DESCRIPTION ( " Keyboard driver for CPU GPIOs " ) ;
2008-04-18 08:24:42 +04:00
MODULE_ALIAS ( " platform:gpio-keys " ) ;