2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-05-19 00:55:12 +03:00
/*
* Atmel Atmegaxx Capacitive Touch Button Driver
*
* Copyright ( C ) 2016 Google , inc .
*/
/*
* It ' s irrelevant that the HW used to develop captouch driver is based
* on Atmega88PA part and uses QtouchADC parts for sensing touch .
* Calling this driver " captouch " is an arbitrary way to distinguish
* the protocol this driver supported by other atmel / qtouch drivers .
*
* Captouch driver supports a newer / different version of the I2C
* registers / commands than the qt1070 . c driver .
* Don ' t let the similarity of the general driver structure fool you .
*
* For raw i2c access from userspace , use i2cset / i2cget
* to poke at / dev / i2c - N devices .
*/
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
/* Maximum number of buttons supported */
# define MAX_NUM_OF_BUTTONS 8
/* Registers */
# define REG_KEY1_THRESHOLD 0x02
# define REG_KEY2_THRESHOLD 0x03
# define REG_KEY3_THRESHOLD 0x04
# define REG_KEY4_THRESHOLD 0x05
# define REG_KEY1_REF_H 0x20
# define REG_KEY1_REF_L 0x21
# define REG_KEY2_REF_H 0x22
# define REG_KEY2_REF_L 0x23
# define REG_KEY3_REF_H 0x24
# define REG_KEY3_REF_L 0x25
# define REG_KEY4_REF_H 0x26
# define REG_KEY4_REF_L 0x27
# define REG_KEY1_DLT_H 0x30
# define REG_KEY1_DLT_L 0x31
# define REG_KEY2_DLT_H 0x32
# define REG_KEY2_DLT_L 0x33
# define REG_KEY3_DLT_H 0x34
# define REG_KEY3_DLT_L 0x35
# define REG_KEY4_DLT_H 0x36
# define REG_KEY4_DLT_L 0x37
# define REG_KEY_STATE 0x3C
/*
* @ i2c_client : I2C slave device client pointer
* @ input : Input device pointer
* @ num_btn : Number of buttons
* @ keycodes : map of button # to KeyCode
* @ prev_btn : Previous key state to detect button " press " or " release "
* @ xfer_buf : I2C transfer buffer
*/
struct atmel_captouch_device {
struct i2c_client * client ;
struct input_dev * input ;
u32 num_btn ;
u32 keycodes [ MAX_NUM_OF_BUTTONS ] ;
u8 prev_btn ;
u8 xfer_buf [ 8 ] ____cacheline_aligned ;
} ;
/*
* Read from I2C slave device
* The protocol is that the client has to provide both the register address
* and the length , and while reading back the device would prepend the data
* with address and length for verification .
*/
static int atmel_read ( struct atmel_captouch_device * capdev ,
u8 reg , u8 * data , size_t len )
{
struct i2c_client * client = capdev - > client ;
struct device * dev = & client - > dev ;
struct i2c_msg msg [ 2 ] ;
int err ;
if ( len > sizeof ( capdev - > xfer_buf ) - 2 )
return - EINVAL ;
capdev - > xfer_buf [ 0 ] = reg ;
capdev - > xfer_buf [ 1 ] = len ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . buf = capdev - > xfer_buf ;
msg [ 0 ] . len = 2 ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = I2C_M_RD ;
msg [ 1 ] . buf = capdev - > xfer_buf ;
msg [ 1 ] . len = len + 2 ;
err = i2c_transfer ( client - > adapter , msg , ARRAY_SIZE ( msg ) ) ;
if ( err ! = ARRAY_SIZE ( msg ) )
return err < 0 ? err : - EIO ;
if ( capdev - > xfer_buf [ 0 ] ! = reg ) {
dev_err ( dev ,
" I2C read error: register address does not match (%#02x vs %02x) \n " ,
capdev - > xfer_buf [ 0 ] , reg ) ;
return - ECOMM ;
}
memcpy ( data , & capdev - > xfer_buf [ 2 ] , len ) ;
return 0 ;
}
/*
* Handle interrupt and report the key changes to the input system .
* Multi - touch can be supported ; however , it really depends on whether
* the device can multi - touch .
*/
static irqreturn_t atmel_captouch_isr ( int irq , void * data )
{
struct atmel_captouch_device * capdev = data ;
struct device * dev = & capdev - > client - > dev ;
int error ;
int i ;
u8 new_btn ;
u8 changed_btn ;
error = atmel_read ( capdev , REG_KEY_STATE , & new_btn , 1 ) ;
if ( error ) {
dev_err ( dev , " failed to read button state: %d \n " , error ) ;
goto out ;
}
dev_dbg ( dev , " %s: button state %#02x \n " , __func__ , new_btn ) ;
changed_btn = new_btn ^ capdev - > prev_btn ;
capdev - > prev_btn = new_btn ;
for ( i = 0 ; i < capdev - > num_btn ; i + + ) {
if ( changed_btn & BIT ( i ) )
input_report_key ( capdev - > input ,
capdev - > keycodes [ i ] ,
new_btn & BIT ( i ) ) ;
}
input_sync ( capdev - > input ) ;
out :
return IRQ_HANDLED ;
}
/*
* Probe function to setup the device , input system and interrupt
*/
2022-11-19 01:39:03 +03:00
static int atmel_captouch_probe ( struct i2c_client * client )
2016-05-19 00:55:12 +03:00
{
struct atmel_captouch_device * capdev ;
struct device * dev = & client - > dev ;
struct device_node * node ;
int i ;
int err ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK ) ) {
dev_err ( dev , " needed i2c functionality is not supported \n " ) ;
return - EINVAL ;
}
capdev = devm_kzalloc ( dev , sizeof ( * capdev ) , GFP_KERNEL ) ;
if ( ! capdev )
return - ENOMEM ;
capdev - > client = client ;
err = atmel_read ( capdev , REG_KEY_STATE ,
& capdev - > prev_btn , sizeof ( capdev - > prev_btn ) ) ;
if ( err ) {
dev_err ( dev , " failed to read initial button state: %d \n " , err ) ;
return err ;
}
capdev - > input = devm_input_allocate_device ( dev ) ;
if ( ! capdev - > input ) {
dev_err ( dev , " failed to allocate input device \n " ) ;
return - ENOMEM ;
}
capdev - > input - > id . bustype = BUS_I2C ;
capdev - > input - > id . product = 0x880A ;
capdev - > input - > id . version = 0 ;
capdev - > input - > name = " ATMegaXX Capacitive Button Controller " ;
__set_bit ( EV_KEY , capdev - > input - > evbit ) ;
node = dev - > of_node ;
if ( ! node ) {
dev_err ( dev , " failed to find matching node in device tree \n " ) ;
return - EINVAL ;
}
if ( of_property_read_bool ( node , " autorepeat " ) )
__set_bit ( EV_REP , capdev - > input - > evbit ) ;
capdev - > num_btn = of_property_count_u32_elems ( node , " linux,keymap " ) ;
if ( capdev - > num_btn > MAX_NUM_OF_BUTTONS )
capdev - > num_btn = MAX_NUM_OF_BUTTONS ;
err = of_property_read_u32_array ( node , " linux,keycodes " ,
capdev - > keycodes ,
capdev - > num_btn ) ;
if ( err ) {
dev_err ( dev ,
" failed to read linux,keycode property: %d \n " , err ) ;
return err ;
}
for ( i = 0 ; i < capdev - > num_btn ; i + + )
__set_bit ( capdev - > keycodes [ i ] , capdev - > input - > keybit ) ;
capdev - > input - > keycode = capdev - > keycodes ;
capdev - > input - > keycodesize = sizeof ( capdev - > keycodes [ 0 ] ) ;
capdev - > input - > keycodemax = capdev - > num_btn ;
err = input_register_device ( capdev - > input ) ;
if ( err )
return err ;
err = devm_request_threaded_irq ( dev , client - > irq ,
NULL , atmel_captouch_isr ,
IRQF_ONESHOT ,
" atmel_captouch " , capdev ) ;
if ( err ) {
dev_err ( dev , " failed to request irq %d: %d \n " ,
client - > irq , err ) ;
return err ;
}
return 0 ;
}
static const struct of_device_id atmel_captouch_of_id [ ] = {
{
. compatible = " atmel,captouch " ,
} ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , atmel_captouch_of_id ) ;
static const struct i2c_device_id atmel_captouch_id [ ] = {
{ " atmel_captouch " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , atmel_captouch_id ) ;
static struct i2c_driver atmel_captouch_driver = {
2022-11-19 01:39:03 +03:00
. probe_new = atmel_captouch_probe ,
2016-05-19 00:55:12 +03:00
. id_table = atmel_captouch_id ,
. driver = {
. name = " atmel_captouch " ,
2022-10-10 21:05:02 +03:00
. of_match_table = atmel_captouch_of_id ,
2016-05-19 00:55:12 +03:00
} ,
} ;
module_i2c_driver ( atmel_captouch_driver ) ;
/* Module information */
MODULE_AUTHOR ( " Hung-yu Wu <hywu@google.com> " ) ;
MODULE_DESCRIPTION ( " Atmel ATmegaXX Capacitance Touch Sensor I2C Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;