2010-07-21 00:45:10 -07:00
/*
* Samsung keypad driver
*
* Copyright ( C ) 2010 Samsung Electronics Co . Ltd
* Author : Joonyoung Shim < jy0922 . shim @ samsung . com >
* Author : Donghwa Lee < dh09 . lee @ samsung . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2011-12-29 09:58:16 -08:00
# include <linux/pm.h>
# include <linux/pm_runtime.h>
2010-07-21 00:45:10 -07:00
# include <linux/slab.h>
2011-11-02 19:37:22 +09:00
# include <linux/of.h>
# include <linux/of_gpio.h>
2010-07-21 00:45:10 -07:00
# include <linux/sched.h>
2011-11-07 23:59:41 -08:00
# include <linux/input/samsung-keypad.h>
2010-07-21 00:45:10 -07:00
# define SAMSUNG_KEYIFCON 0x00
# define SAMSUNG_KEYIFSTSCLR 0x04
# define SAMSUNG_KEYIFCOL 0x08
# define SAMSUNG_KEYIFROW 0x0c
# define SAMSUNG_KEYIFFC 0x10
/* SAMSUNG_KEYIFCON */
# define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0)
# define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1)
# define SAMSUNG_KEYIFCON_DF_EN (1 << 2)
# define SAMSUNG_KEYIFCON_FC_EN (1 << 3)
# define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4)
/* SAMSUNG_KEYIFSTSCLR */
# define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0)
# define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8)
# define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8
# define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0)
# define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16)
# define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16
/* SAMSUNG_KEYIFCOL */
# define SAMSUNG_KEYIFCOL_MASK (0xff << 0)
# define S5PV210_KEYIFCOLEN_MASK (0xff << 8)
/* SAMSUNG_KEYIFROW */
# define SAMSUNG_KEYIFROW_MASK (0xff << 0)
# define S5PV210_KEYIFROW_MASK (0x3fff << 0)
/* SAMSUNG_KEYIFFC */
# define SAMSUNG_KEYIFFC_MASK (0x3ff << 0)
enum samsung_keypad_type {
KEYPAD_TYPE_SAMSUNG ,
KEYPAD_TYPE_S5PV210 ,
} ;
struct samsung_keypad {
struct input_dev * input_dev ;
2011-12-29 09:58:16 -08:00
struct platform_device * pdev ;
2010-07-21 00:45:10 -07:00
struct clk * clk ;
void __iomem * base ;
wait_queue_head_t wait ;
bool stopped ;
2011-12-29 09:58:16 -08:00
bool wake_enabled ;
2010-07-21 00:45:10 -07:00
int irq ;
2011-11-02 19:37:22 +09:00
enum samsung_keypad_type type ;
2010-07-21 00:45:10 -07:00
unsigned int row_shift ;
unsigned int rows ;
unsigned int cols ;
unsigned int row_state [ SAMSUNG_MAX_COLS ] ;
2011-11-02 19:37:22 +09:00
# ifdef CONFIG_OF
int row_gpios [ SAMSUNG_MAX_ROWS ] ;
int col_gpios [ SAMSUNG_MAX_COLS ] ;
# endif
2010-07-21 00:45:10 -07:00
unsigned short keycodes [ ] ;
} ;
static void samsung_keypad_scan ( struct samsung_keypad * keypad ,
unsigned int * row_state )
{
unsigned int col ;
unsigned int val ;
for ( col = 0 ; col < keypad - > cols ; col + + ) {
2011-11-02 19:37:22 +09:00
if ( keypad - > type = = KEYPAD_TYPE_S5PV210 ) {
2010-07-21 00:45:10 -07:00
val = S5PV210_KEYIFCOLEN_MASK ;
val & = ~ ( 1 < < col ) < < 8 ;
} else {
val = SAMSUNG_KEYIFCOL_MASK ;
val & = ~ ( 1 < < col ) ;
}
writel ( val , keypad - > base + SAMSUNG_KEYIFCOL ) ;
mdelay ( 1 ) ;
val = readl ( keypad - > base + SAMSUNG_KEYIFROW ) ;
row_state [ col ] = ~ val & ( ( 1 < < keypad - > rows ) - 1 ) ;
}
/* KEYIFCOL reg clear */
writel ( 0 , keypad - > base + SAMSUNG_KEYIFCOL ) ;
}
static bool samsung_keypad_report ( struct samsung_keypad * keypad ,
unsigned int * row_state )
{
struct input_dev * input_dev = keypad - > input_dev ;
unsigned int changed ;
unsigned int pressed ;
unsigned int key_down = 0 ;
unsigned int val ;
unsigned int col , row ;
for ( col = 0 ; col < keypad - > cols ; col + + ) {
changed = row_state [ col ] ^ keypad - > row_state [ col ] ;
key_down | = row_state [ col ] ;
if ( ! changed )
continue ;
for ( row = 0 ; row < keypad - > rows ; row + + ) {
if ( ! ( changed & ( 1 < < row ) ) )
continue ;
pressed = row_state [ col ] & ( 1 < < row ) ;
dev_dbg ( & keypad - > input_dev - > dev ,
" key %s, row: %d, col: %d \n " ,
pressed ? " pressed " : " released " , row , col ) ;
val = MATRIX_SCAN_CODE ( row , col , keypad - > row_shift ) ;
input_event ( input_dev , EV_MSC , MSC_SCAN , val ) ;
input_report_key ( input_dev ,
keypad - > keycodes [ val ] , pressed ) ;
}
input_sync ( keypad - > input_dev ) ;
}
memcpy ( keypad - > row_state , row_state , sizeof ( keypad - > row_state ) ) ;
return key_down ;
}
static irqreturn_t samsung_keypad_irq ( int irq , void * dev_id )
{
struct samsung_keypad * keypad = dev_id ;
unsigned int row_state [ SAMSUNG_MAX_COLS ] ;
unsigned int val ;
bool key_down ;
2011-12-29 09:58:16 -08:00
pm_runtime_get_sync ( & keypad - > pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
do {
val = readl ( keypad - > base + SAMSUNG_KEYIFSTSCLR ) ;
/* Clear interrupt. */
writel ( ~ 0x0 , keypad - > base + SAMSUNG_KEYIFSTSCLR ) ;
samsung_keypad_scan ( keypad , row_state ) ;
key_down = samsung_keypad_report ( keypad , row_state ) ;
if ( key_down )
wait_event_timeout ( keypad - > wait , keypad - > stopped ,
msecs_to_jiffies ( 50 ) ) ;
} while ( key_down & & ! keypad - > stopped ) ;
2012-01-22 23:27:54 -08:00
pm_runtime_put ( & keypad - > pdev - > dev ) ;
2011-12-29 09:58:16 -08:00
2010-07-21 00:45:10 -07:00
return IRQ_HANDLED ;
}
static void samsung_keypad_start ( struct samsung_keypad * keypad )
{
unsigned int val ;
2011-12-29 09:58:16 -08:00
pm_runtime_get_sync ( & keypad - > pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
/* Tell IRQ thread that it may poll the device. */
keypad - > stopped = false ;
clk_enable ( keypad - > clk ) ;
/* Enable interrupt bits. */
val = readl ( keypad - > base + SAMSUNG_KEYIFCON ) ;
val | = SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN ;
writel ( val , keypad - > base + SAMSUNG_KEYIFCON ) ;
/* KEYIFCOL reg clear. */
writel ( 0 , keypad - > base + SAMSUNG_KEYIFCOL ) ;
2011-12-29 09:58:16 -08:00
2012-01-22 23:27:54 -08:00
pm_runtime_put ( & keypad - > pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
}
static void samsung_keypad_stop ( struct samsung_keypad * keypad )
{
unsigned int val ;
2011-12-29 09:58:16 -08:00
pm_runtime_get_sync ( & keypad - > pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
/* Signal IRQ thread to stop polling and disable the handler. */
keypad - > stopped = true ;
wake_up ( & keypad - > wait ) ;
disable_irq ( keypad - > irq ) ;
/* Clear interrupt. */
writel ( ~ 0x0 , keypad - > base + SAMSUNG_KEYIFSTSCLR ) ;
/* Disable interrupt bits. */
val = readl ( keypad - > base + SAMSUNG_KEYIFCON ) ;
val & = ~ ( SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN ) ;
writel ( val , keypad - > base + SAMSUNG_KEYIFCON ) ;
clk_disable ( keypad - > clk ) ;
/*
* Now that chip should not generate interrupts we can safely
* re - enable the handler .
*/
enable_irq ( keypad - > irq ) ;
2011-12-29 09:58:16 -08:00
2012-01-22 23:27:54 -08:00
pm_runtime_put ( & keypad - > pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
}
static int samsung_keypad_open ( struct input_dev * input_dev )
{
struct samsung_keypad * keypad = input_get_drvdata ( input_dev ) ;
samsung_keypad_start ( keypad ) ;
return 0 ;
}
static void samsung_keypad_close ( struct input_dev * input_dev )
{
struct samsung_keypad * keypad = input_get_drvdata ( input_dev ) ;
samsung_keypad_stop ( keypad ) ;
}
2011-11-02 19:37:22 +09:00
# ifdef CONFIG_OF
static struct samsung_keypad_platdata * samsung_keypad_parse_dt (
struct device * dev )
{
struct samsung_keypad_platdata * pdata ;
struct matrix_keymap_data * keymap_data ;
uint32_t * keymap , num_rows = 0 , num_cols = 0 ;
struct device_node * np = dev - > of_node , * key_np ;
2012-08-21 22:04:46 -07:00
unsigned int key_count ;
2011-11-02 19:37:22 +09:00
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata ) {
dev_err ( dev , " could not allocate memory for platform data \n " ) ;
return NULL ;
}
of_property_read_u32 ( np , " samsung,keypad-num-rows " , & num_rows ) ;
of_property_read_u32 ( np , " samsung,keypad-num-columns " , & num_cols ) ;
if ( ! num_rows | | ! num_cols ) {
dev_err ( dev , " number of keypad rows/columns not specified \n " ) ;
return NULL ;
}
pdata - > rows = num_rows ;
pdata - > cols = num_cols ;
keymap_data = devm_kzalloc ( dev , sizeof ( * keymap_data ) , GFP_KERNEL ) ;
if ( ! keymap_data ) {
dev_err ( dev , " could not allocate memory for keymap data \n " ) ;
return NULL ;
}
pdata - > keymap_data = keymap_data ;
2012-08-21 22:04:46 -07:00
key_count = of_get_child_count ( np ) ;
2011-11-02 19:37:22 +09:00
keymap_data - > keymap_size = key_count ;
keymap = devm_kzalloc ( dev , sizeof ( uint32_t ) * key_count , GFP_KERNEL ) ;
if ( ! keymap ) {
dev_err ( dev , " could not allocate memory for keymap \n " ) ;
return NULL ;
}
keymap_data - > keymap = keymap ;
for_each_child_of_node ( np , key_np ) {
u32 row , col , key_code ;
of_property_read_u32 ( key_np , " keypad,row " , & row ) ;
of_property_read_u32 ( key_np , " keypad,column " , & col ) ;
of_property_read_u32 ( key_np , " linux,code " , & key_code ) ;
* keymap + + = KEY ( row , col , key_code ) ;
}
if ( of_get_property ( np , " linux,input-no-autorepeat " , NULL ) )
pdata - > no_autorepeat = true ;
if ( of_get_property ( np , " linux,input-wakeup " , NULL ) )
pdata - > wakeup = true ;
return pdata ;
}
static void samsung_keypad_parse_dt_gpio ( struct device * dev ,
struct samsung_keypad * keypad )
{
struct device_node * np = dev - > of_node ;
int gpio , ret , row , col ;
for ( row = 0 ; row < keypad - > rows ; row + + ) {
gpio = of_get_named_gpio ( np , " row-gpios " , row ) ;
keypad - > row_gpios [ row ] = gpio ;
if ( ! gpio_is_valid ( gpio ) ) {
dev_err ( dev , " keypad row[%d]: invalid gpio %d \n " ,
row , gpio ) ;
continue ;
}
ret = gpio_request ( gpio , " keypad-row " ) ;
if ( ret )
dev_err ( dev , " keypad row[%d] gpio request failed \n " ,
row ) ;
}
for ( col = 0 ; col < keypad - > cols ; col + + ) {
gpio = of_get_named_gpio ( np , " col-gpios " , col ) ;
keypad - > col_gpios [ col ] = gpio ;
if ( ! gpio_is_valid ( gpio ) ) {
dev_err ( dev , " keypad column[%d]: invalid gpio %d \n " ,
col , gpio ) ;
continue ;
}
ret = gpio_request ( gpio , " keypad-col " ) ;
if ( ret )
dev_err ( dev , " keypad column[%d] gpio request failed \n " ,
col ) ;
}
}
static void samsung_keypad_dt_gpio_free ( struct samsung_keypad * keypad )
{
int cnt ;
for ( cnt = 0 ; cnt < keypad - > rows ; cnt + + )
if ( gpio_is_valid ( keypad - > row_gpios [ cnt ] ) )
gpio_free ( keypad - > row_gpios [ cnt ] ) ;
for ( cnt = 0 ; cnt < keypad - > cols ; cnt + + )
if ( gpio_is_valid ( keypad - > col_gpios [ cnt ] ) )
gpio_free ( keypad - > col_gpios [ cnt ] ) ;
}
# else
static
struct samsung_keypad_platdata * samsung_keypad_parse_dt ( struct device * dev )
{
return NULL ;
}
static void samsung_keypad_dt_gpio_free ( struct samsung_keypad * keypad )
{
}
# endif
2010-07-21 00:45:10 -07:00
static int __devinit samsung_keypad_probe ( struct platform_device * pdev )
{
const struct samsung_keypad_platdata * pdata ;
const struct matrix_keymap_data * keymap_data ;
struct samsung_keypad * keypad ;
struct resource * res ;
struct input_dev * input_dev ;
unsigned int row_shift ;
unsigned int keymap_size ;
int error ;
2011-11-02 19:37:22 +09:00
if ( pdev - > dev . of_node )
pdata = samsung_keypad_parse_dt ( & pdev - > dev ) ;
else
pdata = pdev - > dev . platform_data ;
2010-07-21 00:45:10 -07:00
if ( ! pdata ) {
dev_err ( & pdev - > dev , " no platform data defined \n " ) ;
return - EINVAL ;
}
keymap_data = pdata - > keymap_data ;
if ( ! keymap_data ) {
dev_err ( & pdev - > dev , " no keymap data defined \n " ) ;
return - EINVAL ;
}
if ( ! pdata - > rows | | pdata - > rows > SAMSUNG_MAX_ROWS )
return - EINVAL ;
if ( ! pdata - > cols | | pdata - > cols > SAMSUNG_MAX_COLS )
return - EINVAL ;
/* initialize the gpio */
if ( pdata - > cfg_gpio )
pdata - > cfg_gpio ( pdata - > rows , pdata - > cols ) ;
row_shift = get_count_order ( pdata - > cols ) ;
keymap_size = ( pdata - > rows < < row_shift ) * sizeof ( keypad - > keycodes [ 0 ] ) ;
keypad = kzalloc ( sizeof ( * keypad ) + keymap_size , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! keypad | | ! input_dev ) {
error = - ENOMEM ;
goto err_free_mem ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
error = - ENODEV ;
goto err_free_mem ;
}
keypad - > base = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! keypad - > base ) {
error = - EBUSY ;
goto err_free_mem ;
}
keypad - > clk = clk_get ( & pdev - > dev , " keypad " ) ;
if ( IS_ERR ( keypad - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get keypad clk \n " ) ;
error = PTR_ERR ( keypad - > clk ) ;
goto err_unmap_base ;
}
keypad - > input_dev = input_dev ;
2011-12-29 09:58:16 -08:00
keypad - > pdev = pdev ;
2010-07-21 00:45:10 -07:00
keypad - > row_shift = row_shift ;
keypad - > rows = pdata - > rows ;
keypad - > cols = pdata - > cols ;
2011-12-29 09:58:16 -08:00
keypad - > stopped = true ;
2010-07-21 00:45:10 -07:00
init_waitqueue_head ( & keypad - > wait ) ;
2011-11-02 19:37:22 +09:00
if ( pdev - > dev . of_node ) {
# ifdef CONFIG_OF
samsung_keypad_parse_dt_gpio ( & pdev - > dev , keypad ) ;
keypad - > type = of_device_is_compatible ( pdev - > dev . of_node ,
" samsung,s5pv210-keypad " ) ;
# endif
} else {
keypad - > type = platform_get_device_id ( pdev ) - > driver_data ;
}
2010-07-21 00:45:10 -07:00
input_dev - > name = pdev - > name ;
input_dev - > id . bustype = BUS_HOST ;
input_dev - > dev . parent = & pdev - > dev ;
input_dev - > open = samsung_keypad_open ;
input_dev - > close = samsung_keypad_close ;
2012-05-10 22:37:08 -07:00
error = matrix_keypad_build_keymap ( keymap_data , NULL ,
pdata - > rows , pdata - > cols ,
keypad - > keycodes , input_dev ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to build keymap \n " ) ;
goto err_put_clk ;
}
2010-07-21 00:45:10 -07:00
input_set_capability ( input_dev , EV_MSC , MSC_SCAN ) ;
2012-05-10 22:37:08 -07:00
if ( ! pdata - > no_autorepeat )
__set_bit ( EV_REP , input_dev - > evbit ) ;
2010-07-21 00:45:10 -07:00
2012-05-10 22:37:08 -07:00
input_set_drvdata ( input_dev , keypad ) ;
2010-07-21 00:45:10 -07:00
keypad - > irq = platform_get_irq ( pdev , 0 ) ;
if ( keypad - > irq < 0 ) {
error = keypad - > irq ;
goto err_put_clk ;
}
error = request_threaded_irq ( keypad - > irq , NULL , samsung_keypad_irq ,
IRQF_ONESHOT , dev_name ( & pdev - > dev ) , keypad ) ;
if ( error ) {
dev_err ( & pdev - > dev , " failed to register keypad interrupt \n " ) ;
goto err_put_clk ;
}
2011-12-29 09:58:16 -08:00
device_init_wakeup ( & pdev - > dev , pdata - > wakeup ) ;
platform_set_drvdata ( pdev , keypad ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
error = input_register_device ( keypad - > input_dev ) ;
if ( error )
goto err_free_irq ;
2011-11-02 19:37:22 +09:00
if ( pdev - > dev . of_node ) {
devm_kfree ( & pdev - > dev , ( void * ) pdata - > keymap_data - > keymap ) ;
devm_kfree ( & pdev - > dev , ( void * ) pdata - > keymap_data ) ;
devm_kfree ( & pdev - > dev , ( void * ) pdata ) ;
}
2010-07-21 00:45:10 -07:00
return 0 ;
err_free_irq :
free_irq ( keypad - > irq , keypad ) ;
2011-12-29 09:58:16 -08:00
pm_runtime_disable ( & pdev - > dev ) ;
device_init_wakeup ( & pdev - > dev , 0 ) ;
platform_set_drvdata ( pdev , NULL ) ;
2010-07-21 00:45:10 -07:00
err_put_clk :
clk_put ( keypad - > clk ) ;
2011-11-02 19:37:22 +09:00
samsung_keypad_dt_gpio_free ( keypad ) ;
2010-07-21 00:45:10 -07:00
err_unmap_base :
iounmap ( keypad - > base ) ;
err_free_mem :
input_free_device ( input_dev ) ;
kfree ( keypad ) ;
return error ;
}
static int __devexit samsung_keypad_remove ( struct platform_device * pdev )
{
struct samsung_keypad * keypad = platform_get_drvdata ( pdev ) ;
2011-12-29 09:58:16 -08:00
pm_runtime_disable ( & pdev - > dev ) ;
2010-07-21 00:45:10 -07:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
platform_set_drvdata ( pdev , NULL ) ;
input_unregister_device ( keypad - > input_dev ) ;
/*
* It is safe to free IRQ after unregistering device because
* samsung_keypad_close will shut off interrupts .
*/
free_irq ( keypad - > irq , keypad ) ;
clk_put ( keypad - > clk ) ;
2011-11-02 19:37:22 +09:00
samsung_keypad_dt_gpio_free ( keypad ) ;
2010-07-21 00:45:10 -07:00
iounmap ( keypad - > base ) ;
kfree ( keypad ) ;
return 0 ;
}
2011-12-29 09:58:16 -08:00
# ifdef CONFIG_PM_RUNTIME
static int samsung_keypad_runtime_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct samsung_keypad * keypad = platform_get_drvdata ( pdev ) ;
unsigned int val ;
int error ;
if ( keypad - > stopped )
return 0 ;
/* This may fail on some SoCs due to lack of controller support */
error = enable_irq_wake ( keypad - > irq ) ;
if ( ! error )
keypad - > wake_enabled = true ;
val = readl ( keypad - > base + SAMSUNG_KEYIFCON ) ;
val | = SAMSUNG_KEYIFCON_WAKEUPEN ;
writel ( val , keypad - > base + SAMSUNG_KEYIFCON ) ;
clk_disable ( keypad - > clk ) ;
return 0 ;
}
static int samsung_keypad_runtime_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct samsung_keypad * keypad = platform_get_drvdata ( pdev ) ;
unsigned int val ;
if ( keypad - > stopped )
return 0 ;
clk_enable ( keypad - > clk ) ;
val = readl ( keypad - > base + SAMSUNG_KEYIFCON ) ;
val & = ~ SAMSUNG_KEYIFCON_WAKEUPEN ;
writel ( val , keypad - > base + SAMSUNG_KEYIFCON ) ;
if ( keypad - > wake_enabled )
disable_irq_wake ( keypad - > irq ) ;
return 0 ;
}
# endif
2011-11-07 23:59:35 -08:00
# ifdef CONFIG_PM_SLEEP
2010-07-21 00:45:10 -07:00
static void samsung_keypad_toggle_wakeup ( struct samsung_keypad * keypad ,
bool enable )
{
unsigned int val ;
clk_enable ( keypad - > clk ) ;
val = readl ( keypad - > base + SAMSUNG_KEYIFCON ) ;
if ( enable ) {
val | = SAMSUNG_KEYIFCON_WAKEUPEN ;
2011-12-29 09:58:16 -08:00
if ( device_may_wakeup ( & keypad - > pdev - > dev ) )
2010-07-21 00:45:10 -07:00
enable_irq_wake ( keypad - > irq ) ;
} else {
val & = ~ SAMSUNG_KEYIFCON_WAKEUPEN ;
2011-12-29 09:58:16 -08:00
if ( device_may_wakeup ( & keypad - > pdev - > dev ) )
2010-07-21 00:45:10 -07:00
disable_irq_wake ( keypad - > irq ) ;
}
writel ( val , keypad - > base + SAMSUNG_KEYIFCON ) ;
clk_disable ( keypad - > clk ) ;
}
static int samsung_keypad_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct samsung_keypad * keypad = platform_get_drvdata ( pdev ) ;
struct input_dev * input_dev = keypad - > input_dev ;
mutex_lock ( & input_dev - > mutex ) ;
if ( input_dev - > users )
samsung_keypad_stop ( keypad ) ;
samsung_keypad_toggle_wakeup ( keypad , true ) ;
mutex_unlock ( & input_dev - > mutex ) ;
return 0 ;
}
static int samsung_keypad_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct samsung_keypad * keypad = platform_get_drvdata ( pdev ) ;
struct input_dev * input_dev = keypad - > input_dev ;
mutex_lock ( & input_dev - > mutex ) ;
samsung_keypad_toggle_wakeup ( keypad , false ) ;
if ( input_dev - > users )
samsung_keypad_start ( keypad ) ;
mutex_unlock ( & input_dev - > mutex ) ;
return 0 ;
}
# endif
2011-12-29 09:58:16 -08:00
static const struct dev_pm_ops samsung_keypad_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( samsung_keypad_suspend , samsung_keypad_resume )
SET_RUNTIME_PM_OPS ( samsung_keypad_runtime_suspend ,
samsung_keypad_runtime_resume , NULL )
} ;
2011-11-07 23:59:35 -08:00
2011-11-02 19:37:22 +09:00
# ifdef CONFIG_OF
static const struct of_device_id samsung_keypad_dt_match [ ] = {
{ . compatible = " samsung,s3c6410-keypad " } ,
{ . compatible = " samsung,s5pv210-keypad " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , samsung_keypad_dt_match ) ;
# endif
2010-07-21 00:45:10 -07:00
static struct platform_device_id samsung_keypad_driver_ids [ ] = {
{
. name = " samsung-keypad " ,
. driver_data = KEYPAD_TYPE_SAMSUNG ,
} , {
. name = " s5pv210-keypad " ,
. driver_data = KEYPAD_TYPE_S5PV210 ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , samsung_keypad_driver_ids ) ;
static struct platform_driver samsung_keypad_driver = {
. probe = samsung_keypad_probe ,
. remove = __devexit_p ( samsung_keypad_remove ) ,
. driver = {
. name = " samsung-keypad " ,
. owner = THIS_MODULE ,
2012-08-21 22:04:32 -07:00
. of_match_table = of_match_ptr ( samsung_keypad_dt_match ) ,
2010-07-21 00:45:10 -07:00
. pm = & samsung_keypad_pm_ops ,
} ,
. id_table = samsung_keypad_driver_ids ,
} ;
2011-11-29 11:08:39 -08:00
module_platform_driver ( samsung_keypad_driver ) ;
2010-07-21 00:45:10 -07:00
MODULE_DESCRIPTION ( " Samsung keypad driver " ) ;
MODULE_AUTHOR ( " Joonyoung Shim <jy0922.shim@samsung.com> " ) ;
MODULE_AUTHOR ( " Donghwa Lee <dh09.lee@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;