2012-04-03 13:39:44 -07:00
/*
* LM8333 keypad driver
2015-04-20 10:23:38 -07:00
* Copyright ( C ) 2012 Wolfram Sang , Pengutronix < kernel @ pengutronix . de >
2012-04-03 13:39:44 -07:00
*
* 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 .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/irq.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/input/matrix_keypad.h>
# include <linux/input/lm8333.h>
# define LM8333_FIFO_READ 0x20
# define LM8333_DEBOUNCE 0x22
# define LM8333_READ_INT 0xD0
# define LM8333_ACTIVE 0xE4
# define LM8333_READ_ERROR 0xF0
# define LM8333_KEYPAD_IRQ (1 << 0)
# define LM8333_ERROR_IRQ (1 << 3)
# define LM8333_ERROR_KEYOVR 0x04
# define LM8333_ERROR_FIFOOVR 0x40
# define LM8333_FIFO_TRANSFER_SIZE 16
# define LM8333_NUM_ROWS 8
2012-05-10 22:37:08 -07:00
# define LM8333_NUM_COLS 16
# define LM8333_ROW_SHIFT 4
2012-04-03 13:39:44 -07:00
struct lm8333 {
struct i2c_client * client ;
struct input_dev * input ;
unsigned short keycodes [ LM8333_NUM_ROWS < < LM8333_ROW_SHIFT ] ;
} ;
/* The accessors try twice because the first access may be needed for wakeup */
# define LM8333_READ_RETRIES 2
int lm8333_read8 ( struct lm8333 * lm8333 , u8 cmd )
{
int retries = 0 , ret ;
do {
ret = i2c_smbus_read_byte_data ( lm8333 - > client , cmd ) ;
} while ( ret < 0 & & retries + + < LM8333_READ_RETRIES ) ;
return ret ;
}
int lm8333_write8 ( struct lm8333 * lm8333 , u8 cmd , u8 val )
{
int retries = 0 , ret ;
do {
ret = i2c_smbus_write_byte_data ( lm8333 - > client , cmd , val ) ;
} while ( ret < 0 & & retries + + < LM8333_READ_RETRIES ) ;
return ret ;
}
int lm8333_read_block ( struct lm8333 * lm8333 , u8 cmd , u8 len , u8 * buf )
{
int retries = 0 , ret ;
do {
ret = i2c_smbus_read_i2c_block_data ( lm8333 - > client ,
cmd , len , buf ) ;
} while ( ret < 0 & & retries + + < LM8333_READ_RETRIES ) ;
return ret ;
}
static void lm8333_key_handler ( struct lm8333 * lm8333 )
{
struct input_dev * input = lm8333 - > input ;
u8 keys [ LM8333_FIFO_TRANSFER_SIZE ] ;
u8 code , pressed ;
int i , ret ;
ret = lm8333_read_block ( lm8333 , LM8333_FIFO_READ ,
LM8333_FIFO_TRANSFER_SIZE , keys ) ;
if ( ret ! = LM8333_FIFO_TRANSFER_SIZE ) {
dev_err ( & lm8333 - > client - > dev ,
" Error %d while reading FIFO \n " , ret ) ;
return ;
}
2012-06-27 12:02:07 +01:00
for ( i = 0 ; i < LM8333_FIFO_TRANSFER_SIZE & & keys [ i ] ; i + + ) {
2012-04-03 13:39:44 -07:00
pressed = keys [ i ] & 0x80 ;
code = keys [ i ] & 0x7f ;
input_event ( input , EV_MSC , MSC_SCAN , code ) ;
input_report_key ( input , lm8333 - > keycodes [ code ] , pressed ) ;
}
input_sync ( input ) ;
}
static irqreturn_t lm8333_irq_thread ( int irq , void * data )
{
struct lm8333 * lm8333 = data ;
u8 status = lm8333_read8 ( lm8333 , LM8333_READ_INT ) ;
if ( ! status )
return IRQ_NONE ;
if ( status & LM8333_ERROR_IRQ ) {
u8 err = lm8333_read8 ( lm8333 , LM8333_READ_ERROR ) ;
if ( err & ( LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR ) ) {
u8 dummy [ LM8333_FIFO_TRANSFER_SIZE ] ;
lm8333_read_block ( lm8333 , LM8333_FIFO_READ ,
LM8333_FIFO_TRANSFER_SIZE , dummy ) ;
}
dev_err ( & lm8333 - > client - > dev , " Got error %02x \n " , err ) ;
}
if ( status & LM8333_KEYPAD_IRQ )
lm8333_key_handler ( lm8333 ) ;
return IRQ_HANDLED ;
}
2012-11-23 21:38:25 -08:00
static int lm8333_probe ( struct i2c_client * client ,
2012-04-03 13:39:44 -07:00
const struct i2c_device_id * id )
{
2013-12-05 19:21:10 -08:00
const struct lm8333_platform_data * pdata =
dev_get_platdata ( & client - > dev ) ;
2012-04-03 13:39:44 -07:00
struct lm8333 * lm8333 ;
struct input_dev * input ;
int err , active_time ;
if ( ! pdata )
return - EINVAL ;
active_time = pdata - > active_time ? : 500 ;
if ( active_time / 3 < = pdata - > debounce_time / 3 ) {
dev_err ( & client - > dev , " Active time not big enough! \n " ) ;
return - EINVAL ;
}
lm8333 = kzalloc ( sizeof ( * lm8333 ) , GFP_KERNEL ) ;
input = input_allocate_device ( ) ;
if ( ! lm8333 | | ! input ) {
err = - ENOMEM ;
goto free_mem ;
}
lm8333 - > client = client ;
lm8333 - > input = input ;
input - > name = client - > name ;
input - > dev . parent = & client - > dev ;
input - > id . bustype = BUS_I2C ;
input_set_capability ( input , EV_MSC , MSC_SCAN ) ;
2012-05-10 22:37:08 -07:00
err = matrix_keypad_build_keymap ( pdata - > matrix_data , NULL ,
LM8333_NUM_ROWS , LM8333_NUM_COLS ,
lm8333 - > keycodes , input ) ;
if ( err )
goto free_mem ;
2012-04-03 13:39:44 -07:00
if ( pdata - > debounce_time ) {
err = lm8333_write8 ( lm8333 , LM8333_DEBOUNCE ,
pdata - > debounce_time / 3 ) ;
if ( err )
dev_warn ( & client - > dev , " Unable to set debounce time \n " ) ;
}
if ( pdata - > active_time ) {
err = lm8333_write8 ( lm8333 , LM8333_ACTIVE ,
pdata - > active_time / 3 ) ;
if ( err )
dev_warn ( & client - > dev , " Unable to set active time \n " ) ;
}
err = request_threaded_irq ( client - > irq , NULL , lm8333_irq_thread ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" lm8333 " , lm8333 ) ;
if ( err )
goto free_mem ;
err = input_register_device ( input ) ;
if ( err )
goto free_irq ;
i2c_set_clientdata ( client , lm8333 ) ;
return 0 ;
free_irq :
free_irq ( client - > irq , lm8333 ) ;
free_mem :
input_free_device ( input ) ;
kfree ( lm8333 ) ;
return err ;
}
2012-11-23 21:50:47 -08:00
static int lm8333_remove ( struct i2c_client * client )
2012-04-03 13:39:44 -07:00
{
struct lm8333 * lm8333 = i2c_get_clientdata ( client ) ;
free_irq ( client - > irq , lm8333 ) ;
input_unregister_device ( lm8333 - > input ) ;
kfree ( lm8333 ) ;
return 0 ;
}
static const struct i2c_device_id lm8333_id [ ] = {
{ " lm8333 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm8333_id ) ;
static struct i2c_driver lm8333_driver = {
. driver = {
. name = " lm8333 " ,
} ,
. probe = lm8333_probe ,
2012-11-23 21:27:39 -08:00
. remove = lm8333_remove ,
2012-04-03 13:39:44 -07:00
. id_table = lm8333_id ,
} ;
module_i2c_driver ( lm8333_driver ) ;
2015-04-20 10:23:38 -07:00
MODULE_AUTHOR ( " Wolfram Sang <kernel@pengutronix.de> " ) ;
2012-04-03 13:39:44 -07:00
MODULE_DESCRIPTION ( " LM8333 keyboard driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;