2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-10-04 00:24:27 +04:00
/*
* Elan I2C / SMBus Touchpad driver
*
* Copyright ( c ) 2013 ELAN Microelectronics Corp .
*
* Author : 林 政 維 ( Duson Lin ) < dusonlin @ emc . com . tw >
2016-07-13 21:12:12 +03:00
* Author : KT Liao < kt . liao @ emc . com . tw >
2016-11-28 07:59:29 +03:00
* Version : 1.6 .3
2014-10-04 00:24:27 +04:00
*
* Based on cyapa driver :
* copyright ( c ) 2011 - 2012 Cypress Semiconductor , Inc .
* copyright ( c ) 2011 - 2012 Google , Inc .
*
* Trademarks are the property of their respective owners .
*/
# include <linux/acpi.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/firmware.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/input/mt.h>
# include <linux/interrupt.h>
2017-09-28 19:57:34 +03:00
# include <linux/irq.h>
2014-10-04 00:24:27 +04:00
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/input.h>
# include <linux/uaccess.h>
# include <linux/jiffies.h>
# include <linux/completion.h>
# include <linux/of.h>
2022-11-23 00:44:11 +03:00
# include <linux/pm_wakeirq.h>
2018-05-23 03:23:04 +03:00
# include <linux/property.h>
2014-10-04 00:24:27 +04:00
# include <linux/regulator/consumer.h>
# include <asm/unaligned.h>
# include "elan_i2c.h"
# define DRIVER_NAME "elan_i2c"
2015-12-15 22:32:10 +03:00
# define ELAN_VENDOR_ID 0x04f3
2014-10-04 00:24:27 +04:00
# define ETP_MAX_PRESSURE 255
# define ETP_FWIDTH_REDUCE 90
# define ETP_FINGER_WIDTH 15
# define ETP_RETRY_COUNT 3
2021-03-08 05:23:25 +03:00
/* quirks to control the device */
# define ETP_QUIRK_QUICK_WAKEUP BIT(0)
2014-10-04 00:24:27 +04:00
/* The main device structure */
struct elan_tp_data {
struct i2c_client * client ;
struct input_dev * input ;
2018-05-23 03:23:04 +03:00
struct input_dev * tp_input ; /* trackpoint input node */
2014-10-04 00:24:27 +04:00
struct regulator * vcc ;
const struct elan_transport_ops * ops ;
/* for fw update */
struct completion fw_completion ;
bool in_fw_update ;
struct mutex sysfs_mutex ;
unsigned int max_x ;
unsigned int max_y ;
unsigned int width_x ;
unsigned int width_y ;
unsigned int x_res ;
unsigned int y_res ;
2016-11-28 07:59:29 +03:00
u8 pattern ;
2015-09-21 19:26:46 +03:00
u16 product_id ;
2014-10-04 00:24:27 +04:00
u8 fw_version ;
u8 sm_version ;
u8 iap_version ;
u16 fw_checksum ;
2020-07-20 22:46:10 +03:00
unsigned int report_features ;
unsigned int report_len ;
2015-04-13 02:01:05 +03:00
int pressure_adjustment ;
2014-10-04 00:24:27 +04:00
u8 mode ;
2016-11-28 07:59:29 +03:00
u16 ic_type ;
2015-08-29 03:30:16 +03:00
u16 fw_validpage_count ;
2020-07-17 07:32:23 +03:00
u16 fw_page_size ;
u32 fw_signature_address ;
2014-10-04 00:24:27 +04:00
u8 min_baseline ;
u8 max_baseline ;
bool baseline_ready ;
2017-08-19 02:49:53 +03:00
u8 clickpad ;
2019-05-28 04:44:40 +03:00
bool middle_button ;
2021-03-08 05:23:25 +03:00
u32 quirks ; /* Various quirks */
2014-10-04 00:24:27 +04:00
} ;
2021-03-08 05:23:25 +03:00
static u32 elan_i2c_lookup_quirks ( u16 ic_type , u16 product_id )
{
static const struct {
u16 ic_type ;
u16 product_id ;
u32 quirks ;
} elan_i2c_quirks [ ] = {
{ 0x0D , ETP_PRODUCT_ID_DELBIN , ETP_QUIRK_QUICK_WAKEUP } ,
2021-09-07 07:52:05 +03:00
{ 0x0D , ETP_PRODUCT_ID_WHITEBOX , ETP_QUIRK_QUICK_WAKEUP } ,
2021-03-08 05:23:25 +03:00
{ 0x10 , ETP_PRODUCT_ID_VOXEL , ETP_QUIRK_QUICK_WAKEUP } ,
{ 0x14 , ETP_PRODUCT_ID_MAGPIE , ETP_QUIRK_QUICK_WAKEUP } ,
{ 0x14 , ETP_PRODUCT_ID_BOBBA , ETP_QUIRK_QUICK_WAKEUP } ,
} ;
u32 quirks = 0 ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( elan_i2c_quirks ) ; i + + ) {
if ( elan_i2c_quirks [ i ] . ic_type = = ic_type & &
elan_i2c_quirks [ i ] . product_id = = product_id ) {
quirks = elan_i2c_quirks [ i ] . quirks ;
}
}
if ( ic_type > = 0x0D & & product_id > = 0x123 )
quirks | = ETP_QUIRK_QUICK_WAKEUP ;
return quirks ;
}
2020-07-17 08:49:09 +03:00
static int elan_get_fwinfo ( u16 ic_type , u8 iap_version , u16 * validpage_count ,
2020-07-17 07:32:23 +03:00
u32 * signature_address , u16 * page_size )
2015-06-09 02:39:35 +03:00
{
2016-11-28 07:59:29 +03:00
switch ( ic_type ) {
2015-09-29 03:17:01 +03:00
case 0x00 :
case 0x06 :
2015-08-24 20:49:29 +03:00
case 0x08 :
2015-08-29 03:30:16 +03:00
* validpage_count = 512 ;
2015-08-24 20:49:29 +03:00
break ;
2015-09-21 19:24:46 +03:00
case 0x03 :
2015-09-29 03:17:01 +03:00
case 0x07 :
case 0x09 :
case 0x0A :
case 0x0B :
case 0x0C :
2015-08-29 03:30:16 +03:00
* validpage_count = 768 ;
2015-06-09 02:39:35 +03:00
break ;
case 0x0D :
2015-08-29 03:30:16 +03:00
* validpage_count = 896 ;
2015-06-09 02:39:35 +03:00
break ;
2015-09-29 03:17:01 +03:00
case 0x0E :
* validpage_count = 640 ;
break ;
2016-11-28 07:59:29 +03:00
case 0x10 :
* validpage_count = 1024 ;
break ;
2020-07-17 08:55:57 +03:00
case 0x11 :
* validpage_count = 1280 ;
break ;
case 0x13 :
* validpage_count = 2048 ;
break ;
case 0x14 :
2020-07-30 09:31:33 +03:00
case 0x15 :
2020-07-17 08:55:57 +03:00
* validpage_count = 1024 ;
break ;
2015-06-09 02:39:35 +03:00
default :
/* unknown ic type clear value */
2015-08-29 03:30:16 +03:00
* validpage_count = 0 ;
2015-06-09 02:39:35 +03:00
* signature_address = 0 ;
2020-07-17 07:32:23 +03:00
* page_size = 0 ;
2015-06-09 02:39:35 +03:00
return - ENXIO ;
}
* signature_address =
2015-08-29 03:30:16 +03:00
( * validpage_count * ETP_FW_PAGE_SIZE ) - ETP_FW_SIGNATURE_SIZE ;
2015-06-09 02:39:35 +03:00
2020-07-30 09:31:33 +03:00
if ( ( ic_type = = 0x14 | | ic_type = = 0x15 ) & & iap_version > = 2 ) {
2020-07-17 08:55:57 +03:00
* validpage_count / = 8 ;
* page_size = ETP_FW_PAGE_SIZE_512 ;
} else if ( ic_type > = 0x0D & & iap_version > = 1 ) {
2020-07-17 08:49:09 +03:00
* validpage_count / = 2 ;
* page_size = ETP_FW_PAGE_SIZE_128 ;
} else {
* page_size = ETP_FW_PAGE_SIZE ;
}
2020-07-17 07:32:23 +03:00
2015-06-09 02:39:35 +03:00
return 0 ;
}
2022-03-01 10:39:38 +03:00
static int elan_set_power ( struct elan_tp_data * data , bool on )
2014-10-04 00:24:27 +04:00
{
int repeat = ETP_RETRY_COUNT ;
int error ;
do {
2022-03-01 10:39:38 +03:00
error = data - > ops - > power_control ( data - > client , on ) ;
2014-10-04 00:24:27 +04:00
if ( error > = 0 )
return 0 ;
msleep ( 30 ) ;
} while ( - - repeat > 0 ) ;
2022-03-01 10:39:38 +03:00
dev_err ( & data - > client - > dev , " failed to set power %s: %d \n " ,
on ? " on " : " off " , error ) ;
2014-10-04 00:24:27 +04:00
return error ;
}
static int elan_sleep ( struct elan_tp_data * data )
{
int repeat = ETP_RETRY_COUNT ;
int error ;
do {
error = data - > ops - > sleep_control ( data - > client , true ) ;
if ( ! error )
return 0 ;
msleep ( 30 ) ;
} while ( - - repeat > 0 ) ;
return error ;
}
2016-07-13 21:12:12 +03:00
static int elan_query_product ( struct elan_tp_data * data )
{
int error ;
error = data - > ops - > get_product_id ( data - > client , & data - > product_id ) ;
if ( error )
return error ;
2020-07-21 00:56:54 +03:00
error = data - > ops - > get_pattern ( data - > client , & data - > pattern ) ;
if ( error )
return error ;
error = data - > ops - > get_sm_version ( data - > client , data - > pattern ,
& data - > ic_type , & data - > sm_version ,
& data - > clickpad ) ;
2016-07-13 21:12:12 +03:00
if ( error )
return error ;
return 0 ;
}
static int elan_check_ASUS_special_fw ( struct elan_tp_data * data )
{
2017-03-11 01:33:09 +03:00
if ( data - > ic_type = = 0x0E ) {
switch ( data - > product_id ) {
case 0x05 . . . 0x07 :
case 0x09 :
case 0x13 :
return true ;
}
} else if ( data - > ic_type = = 0x08 & & data - > product_id = = 0x26 ) {
/* ASUS EeeBook X205TA */
2016-07-13 21:12:12 +03:00
return true ;
}
2017-03-11 01:33:09 +03:00
return false ;
2016-07-13 21:12:12 +03:00
}
2021-03-08 05:23:25 +03:00
static int __elan_initialize ( struct elan_tp_data * data , bool skip_reset )
2014-10-04 00:24:27 +04:00
{
struct i2c_client * client = data - > client ;
2016-07-13 21:12:12 +03:00
bool woken_up = false ;
2014-10-04 00:24:27 +04:00
int error ;
2021-03-08 05:23:25 +03:00
if ( ! skip_reset ) {
error = data - > ops - > initialize ( client ) ;
if ( error ) {
dev_err ( & client - > dev , " device initialize failed: %d \n " , error ) ;
return error ;
}
2014-10-04 00:24:27 +04:00
}
2016-07-13 21:12:12 +03:00
error = elan_query_product ( data ) ;
if ( error )
return error ;
/*
* Some ASUS devices were shipped with firmware that requires
* touchpads to be woken up first , before attempting to switch
* them into absolute reporting mode .
*/
if ( elan_check_ASUS_special_fw ( data ) ) {
error = data - > ops - > sleep_control ( client , false ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to wake device up: %d \n " , error ) ;
return error ;
}
msleep ( 200 ) ;
woken_up = true ;
}
2014-10-04 00:24:27 +04:00
data - > mode | = ETP_ENABLE_ABS ;
error = data - > ops - > set_mode ( client , data - > mode ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to switch to absolute mode: %d \n " , error ) ;
return error ;
}
2016-07-13 21:12:12 +03:00
if ( ! woken_up ) {
error = data - > ops - > sleep_control ( client , false ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to wake device up: %d \n " , error ) ;
return error ;
}
2014-10-04 00:24:27 +04:00
}
return 0 ;
}
2021-03-08 05:23:25 +03:00
static int elan_initialize ( struct elan_tp_data * data , bool skip_reset )
2014-10-04 00:24:27 +04:00
{
int repeat = ETP_RETRY_COUNT ;
int error ;
do {
2021-03-08 05:23:25 +03:00
error = __elan_initialize ( data , skip_reset ) ;
2014-10-04 00:24:27 +04:00
if ( ! error )
return 0 ;
2021-03-08 05:23:25 +03:00
skip_reset = false ;
2014-10-04 00:24:27 +04:00
msleep ( 30 ) ;
} while ( - - repeat > 0 ) ;
return error ;
}
static int elan_query_device_info ( struct elan_tp_data * data )
{
int error ;
2020-07-21 00:56:54 +03:00
error = data - > ops - > get_version ( data - > client , data - > pattern , false ,
& data - > fw_version ) ;
2014-10-04 00:24:27 +04:00
if ( error )
return error ;
error = data - > ops - > get_checksum ( data - > client , false ,
& data - > fw_checksum ) ;
if ( error )
return error ;
2020-07-21 00:56:54 +03:00
error = data - > ops - > get_version ( data - > client , data - > pattern ,
true , & data - > iap_version ) ;
2014-10-04 00:24:27 +04:00
if ( error )
return error ;
2015-04-13 02:01:05 +03:00
error = data - > ops - > get_pressure_adjustment ( data - > client ,
& data - > pressure_adjustment ) ;
if ( error )
return error ;
2020-07-20 22:46:10 +03:00
error = data - > ops - > get_report_features ( data - > client , data - > pattern ,
& data - > report_features ,
& data - > report_len ) ;
2016-11-28 07:59:29 +03:00
if ( error )
return error ;
2021-03-08 05:23:25 +03:00
data - > quirks = elan_i2c_lookup_quirks ( data - > ic_type , data - > product_id ) ;
2020-07-17 08:49:09 +03:00
error = elan_get_fwinfo ( data - > ic_type , data - > iap_version ,
& data - > fw_validpage_count ,
2020-07-17 07:32:23 +03:00
& data - > fw_signature_address ,
& data - > fw_page_size ) ;
2015-09-19 19:49:45 +03:00
if ( error )
dev_warn ( & data - > client - > dev ,
" unexpected iap version %#04x (ic type: %#04x), firmware update will not work \n " ,
data - > iap_version , data - > ic_type ) ;
2015-06-09 02:39:35 +03:00
2014-10-04 00:24:27 +04:00
return 0 ;
}
2020-07-20 22:46:10 +03:00
static unsigned int elan_convert_resolution ( u8 val , u8 pattern )
2014-10-04 00:24:27 +04:00
{
/*
2020-07-20 22:46:10 +03:00
* pattern < = 0x01 :
* ( value from firmware ) * 10 + 790 = dpi
* else
* ( ( value from firmware ) + 3 ) * 100 = dpi
*/
int res = pattern < = 0x01 ?
( int ) ( char ) val * 10 + 790 : ( ( int ) ( char ) val + 3 ) * 100 ;
/*
2014-10-04 00:24:27 +04:00
* We also have to convert dpi to dots / mm ( * 10 / 254 to avoid floating
* point ) .
*/
2020-07-20 22:46:10 +03:00
return res * 10 / 254 ;
2014-10-04 00:24:27 +04:00
}
static int elan_query_device_parameters ( struct elan_tp_data * data )
{
2019-05-28 04:42:48 +03:00
struct i2c_client * client = data - > client ;
2014-10-04 00:24:27 +04:00
unsigned int x_traces , y_traces ;
2019-05-28 04:42:48 +03:00
u32 x_mm , y_mm ;
2014-10-04 00:24:27 +04:00
u8 hw_x_res , hw_y_res ;
int error ;
2019-05-28 04:42:48 +03:00
if ( device_property_read_u32 ( & client - > dev ,
" touchscreen-size-x " , & data - > max_x ) | |
device_property_read_u32 ( & client - > dev ,
" touchscreen-size-y " , & data - > max_y ) ) {
error = data - > ops - > get_max ( data - > client ,
& data - > max_x ,
& data - > max_y ) ;
if ( error )
return error ;
} else {
/* size is the maximum + 1 */
- - data - > max_x ;
- - data - > max_y ;
}
2014-10-04 00:24:27 +04:00
2019-05-28 04:42:48 +03:00
if ( device_property_read_u32 ( & client - > dev ,
" elan,x_traces " ,
& x_traces ) | |
device_property_read_u32 ( & client - > dev ,
" elan,y_traces " ,
& y_traces ) ) {
error = data - > ops - > get_num_traces ( data - > client ,
& x_traces , & y_traces ) ;
if ( error )
return error ;
}
2014-10-04 00:24:27 +04:00
data - > width_x = data - > max_x / x_traces ;
data - > width_y = data - > max_y / y_traces ;
2019-05-28 04:42:48 +03:00
if ( device_property_read_u32 ( & client - > dev ,
" touchscreen-x-mm " , & x_mm ) | |
device_property_read_u32 ( & client - > dev ,
" touchscreen-y-mm " , & y_mm ) ) {
error = data - > ops - > get_resolution ( data - > client ,
& hw_x_res , & hw_y_res ) ;
if ( error )
return error ;
2020-07-20 22:46:10 +03:00
data - > x_res = elan_convert_resolution ( hw_x_res , data - > pattern ) ;
data - > y_res = elan_convert_resolution ( hw_y_res , data - > pattern ) ;
2019-05-28 04:42:48 +03:00
} else {
data - > x_res = ( data - > max_x + 1 ) / x_mm ;
data - > y_res = ( data - > max_y + 1 ) / y_mm ;
}
2014-10-04 00:24:27 +04:00
2019-05-28 04:42:48 +03:00
if ( device_property_read_bool ( & client - > dev , " elan,clickpad " ) )
data - > clickpad = 1 ;
2014-10-04 00:24:27 +04:00
2019-05-28 04:44:40 +03:00
if ( device_property_read_bool ( & client - > dev , " elan,middle-button " ) )
data - > middle_button = true ;
2014-10-04 00:24:27 +04:00
return 0 ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* IAP firmware updater related routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2020-07-17 07:32:23 +03:00
static int elan_write_fw_block ( struct elan_tp_data * data , u16 page_size ,
2014-10-04 00:24:27 +04:00
const u8 * page , u16 checksum , int idx )
{
int retry = ETP_RETRY_COUNT ;
int error ;
do {
2020-07-17 07:32:23 +03:00
error = data - > ops - > write_fw_block ( data - > client , page_size ,
2014-10-04 00:24:27 +04:00
page , checksum , idx ) ;
if ( ! error )
return 0 ;
dev_dbg ( & data - > client - > dev ,
" IAP retrying page %d (error: %d) \n " , idx , error ) ;
} while ( - - retry > 0 ) ;
return error ;
}
static int __elan_update_firmware ( struct elan_tp_data * data ,
const struct firmware * fw )
{
struct i2c_client * client = data - > client ;
struct device * dev = & client - > dev ;
int i , j ;
int error ;
u16 iap_start_addr ;
u16 boot_page_count ;
u16 sw_checksum = 0 , fw_checksum = 0 ;
2020-07-17 08:49:09 +03:00
error = data - > ops - > prepare_fw_update ( client , data - > ic_type ,
2020-11-12 07:06:24 +03:00
data - > iap_version ,
data - > fw_page_size ) ;
2014-10-04 00:24:27 +04:00
if ( error )
return error ;
iap_start_addr = get_unaligned_le16 ( & fw - > data [ ETP_IAP_START_ADDR * 2 ] ) ;
2020-07-17 07:32:23 +03:00
boot_page_count = ( iap_start_addr * 2 ) / data - > fw_page_size ;
2015-08-29 03:30:16 +03:00
for ( i = boot_page_count ; i < data - > fw_validpage_count ; i + + ) {
2014-10-04 00:24:27 +04:00
u16 checksum = 0 ;
2020-07-17 07:32:23 +03:00
const u8 * page = & fw - > data [ i * data - > fw_page_size ] ;
2014-10-04 00:24:27 +04:00
2020-07-17 07:32:23 +03:00
for ( j = 0 ; j < data - > fw_page_size ; j + = 2 )
2014-10-04 00:24:27 +04:00
checksum + = ( ( page [ j + 1 ] < < 8 ) | page [ j ] ) ;
2020-07-17 07:32:23 +03:00
error = elan_write_fw_block ( data , data - > fw_page_size ,
page , checksum , i ) ;
2014-10-04 00:24:27 +04:00
if ( error ) {
dev_err ( dev , " write page %d fail: %d \n " , i , error ) ;
return error ;
}
sw_checksum + = checksum ;
}
/* Wait WDT reset and power on reset */
msleep ( 600 ) ;
error = data - > ops - > finish_fw_update ( client , & data - > fw_completion ) ;
if ( error )
return error ;
error = data - > ops - > get_checksum ( client , true , & fw_checksum ) ;
if ( error )
return error ;
if ( sw_checksum ! = fw_checksum ) {
dev_err ( dev , " checksum diff sw=[%04X], fw=[%04X] \n " ,
sw_checksum , fw_checksum ) ;
return - EIO ;
}
return 0 ;
}
static int elan_update_firmware ( struct elan_tp_data * data ,
const struct firmware * fw )
{
struct i2c_client * client = data - > client ;
int retval ;
dev_dbg ( & client - > dev , " Starting firmware update.... \n " ) ;
disable_irq ( client - > irq ) ;
data - > in_fw_update = true ;
retval = __elan_update_firmware ( data , fw ) ;
if ( retval ) {
dev_err ( & client - > dev , " firmware update failed: %d \n " , retval ) ;
data - > ops - > iap_reset ( client ) ;
} else {
/* Reinitialize TP after fw is updated */
2021-03-08 05:23:25 +03:00
elan_initialize ( data , false ) ;
2014-10-04 00:24:27 +04:00
elan_query_device_info ( data ) ;
}
data - > in_fw_update = false ;
enable_irq ( client - > irq ) ;
return retval ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* SYSFS attributes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
static ssize_t elan_sysfs_read_fw_checksum ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " 0x%04x \n " , data - > fw_checksum ) ;
}
static ssize_t elan_sysfs_read_product_id ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
2015-06-09 02:48:23 +03:00
return sprintf ( buf , ETP_PRODUCT_ID_FORMAT_STRING " \n " ,
data - > product_id ) ;
2014-10-04 00:24:27 +04:00
}
static ssize_t elan_sysfs_read_fw_ver ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %d.0 \n " , data - > fw_version ) ;
}
static ssize_t elan_sysfs_read_sm_ver ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %d.0 \n " , data - > sm_version ) ;
}
static ssize_t elan_sysfs_read_iap_ver ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
return sprintf ( buf , " %d.0 \n " , data - > iap_version ) ;
}
static ssize_t elan_sysfs_update_fw ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2015-01-23 20:35:07 +03:00
struct elan_tp_data * data = dev_get_drvdata ( dev ) ;
2014-10-04 00:24:27 +04:00
const struct firmware * fw ;
2015-06-09 02:48:23 +03:00
char * fw_name ;
2014-10-04 00:24:27 +04:00
int error ;
2015-01-23 20:35:07 +03:00
const u8 * fw_signature ;
static const u8 signature [ ] = { 0xAA , 0x55 , 0xCC , 0x33 , 0xFF , 0xFF } ;
2014-10-04 00:24:27 +04:00
2015-09-19 19:49:45 +03:00
if ( data - > fw_validpage_count = = 0 )
return - EINVAL ;
2015-06-09 02:48:23 +03:00
/* Look for a firmware with the product id appended. */
fw_name = kasprintf ( GFP_KERNEL , ETP_FW_NAME , data - > product_id ) ;
if ( ! fw_name ) {
dev_err ( dev , " failed to allocate memory for firmware name \n " ) ;
return - ENOMEM ;
}
dev_info ( dev , " requesting fw '%s' \n " , fw_name ) ;
error = request_firmware ( & fw , fw_name , dev ) ;
kfree ( fw_name ) ;
2014-10-04 00:24:27 +04:00
if ( error ) {
2015-06-09 02:48:23 +03:00
dev_err ( dev , " failed to request firmware: %d \n " , error ) ;
2014-10-04 00:24:27 +04:00
return error ;
}
2015-01-23 20:35:07 +03:00
/* Firmware file must match signature data */
2015-06-09 02:39:35 +03:00
fw_signature = & fw - > data [ data - > fw_signature_address ] ;
2015-01-23 20:35:07 +03:00
if ( memcmp ( fw_signature , signature , sizeof ( signature ) ) ! = 0 ) {
dev_err ( dev , " signature mismatch (expected %*ph, got %*ph) \n " ,
( int ) sizeof ( signature ) , signature ,
( int ) sizeof ( signature ) , fw_signature ) ;
2014-10-04 00:24:27 +04:00
error = - EBADF ;
goto out_release_fw ;
}
error = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( error )
goto out_release_fw ;
error = elan_update_firmware ( data , fw ) ;
mutex_unlock ( & data - > sysfs_mutex ) ;
out_release_fw :
release_firmware ( fw ) ;
return error ? : count ;
}
static ssize_t calibrate_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int tries = 20 ;
int retval ;
int error ;
2018-06-19 21:17:32 +03:00
u8 val [ ETP_CALIBRATE_MAX_LEN ] ;
2014-10-04 00:24:27 +04:00
retval = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( retval )
return retval ;
disable_irq ( client - > irq ) ;
data - > mode | = ETP_ENABLE_CALIBRATE ;
retval = data - > ops - > set_mode ( client , data - > mode ) ;
if ( retval ) {
dev_err ( dev , " failed to enable calibration mode: %d \n " ,
retval ) ;
goto out ;
}
retval = data - > ops - > calibrate ( client ) ;
if ( retval ) {
dev_err ( dev , " failed to start calibration: %d \n " ,
retval ) ;
goto out_disable_calibrate ;
}
val [ 0 ] = 0xff ;
do {
/* Wait 250ms before checking if calibration has completed. */
msleep ( 250 ) ;
retval = data - > ops - > calibrate_result ( client , val ) ;
if ( retval )
dev_err ( dev , " failed to check calibration result: %d \n " ,
retval ) ;
else if ( val [ 0 ] = = 0 )
break ; /* calibration done */
} while ( - - tries ) ;
if ( tries = = 0 ) {
dev_err ( dev , " failed to calibrate. Timeout. \n " ) ;
retval = - ETIMEDOUT ;
}
out_disable_calibrate :
data - > mode & = ~ ETP_ENABLE_CALIBRATE ;
error = data - > ops - > set_mode ( data - > client , data - > mode ) ;
if ( error ) {
dev_err ( dev , " failed to disable calibration mode: %d \n " ,
error ) ;
if ( ! retval )
retval = error ;
}
out :
enable_irq ( client - > irq ) ;
mutex_unlock ( & data - > sysfs_mutex ) ;
return retval ? : count ;
}
static ssize_t elan_sysfs_read_mode ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int error ;
enum tp_mode mode ;
error = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( error )
return error ;
error = data - > ops - > iap_get_mode ( data - > client , & mode ) ;
mutex_unlock ( & data - > sysfs_mutex ) ;
if ( error )
return error ;
return sprintf ( buf , " %d \n " , ( int ) mode ) ;
}
static DEVICE_ATTR ( product_id , S_IRUGO , elan_sysfs_read_product_id , NULL ) ;
static DEVICE_ATTR ( firmware_version , S_IRUGO , elan_sysfs_read_fw_ver , NULL ) ;
static DEVICE_ATTR ( sample_version , S_IRUGO , elan_sysfs_read_sm_ver , NULL ) ;
static DEVICE_ATTR ( iap_version , S_IRUGO , elan_sysfs_read_iap_ver , NULL ) ;
static DEVICE_ATTR ( fw_checksum , S_IRUGO , elan_sysfs_read_fw_checksum , NULL ) ;
static DEVICE_ATTR ( mode , S_IRUGO , elan_sysfs_read_mode , NULL ) ;
static DEVICE_ATTR ( update_fw , S_IWUSR , NULL , elan_sysfs_update_fw ) ;
static DEVICE_ATTR_WO ( calibrate ) ;
static struct attribute * elan_sysfs_entries [ ] = {
& dev_attr_product_id . attr ,
& dev_attr_firmware_version . attr ,
& dev_attr_sample_version . attr ,
& dev_attr_iap_version . attr ,
& dev_attr_fw_checksum . attr ,
& dev_attr_calibrate . attr ,
& dev_attr_mode . attr ,
& dev_attr_update_fw . attr ,
NULL ,
} ;
static const struct attribute_group elan_sysfs_group = {
. attrs = elan_sysfs_entries ,
} ;
static ssize_t acquire_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int error ;
int retval ;
retval = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( retval )
return retval ;
disable_irq ( client - > irq ) ;
data - > baseline_ready = false ;
data - > mode | = ETP_ENABLE_CALIBRATE ;
retval = data - > ops - > set_mode ( data - > client , data - > mode ) ;
if ( retval ) {
dev_err ( dev , " Failed to enable calibration mode to get baseline: %d \n " ,
retval ) ;
goto out ;
}
msleep ( 250 ) ;
retval = data - > ops - > get_baseline_data ( data - > client , true ,
& data - > max_baseline ) ;
if ( retval ) {
dev_err ( dev , " Failed to read max baseline form device: %d \n " ,
retval ) ;
goto out_disable_calibrate ;
}
retval = data - > ops - > get_baseline_data ( data - > client , false ,
& data - > min_baseline ) ;
if ( retval ) {
dev_err ( dev , " Failed to read min baseline form device: %d \n " ,
retval ) ;
goto out_disable_calibrate ;
}
data - > baseline_ready = true ;
out_disable_calibrate :
data - > mode & = ~ ETP_ENABLE_CALIBRATE ;
error = data - > ops - > set_mode ( data - > client , data - > mode ) ;
if ( error ) {
dev_err ( dev , " Failed to disable calibration mode after acquiring baseline: %d \n " ,
error ) ;
if ( ! retval )
retval = error ;
}
out :
enable_irq ( client - > irq ) ;
mutex_unlock ( & data - > sysfs_mutex ) ;
return retval ? : count ;
}
static ssize_t min_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int retval ;
retval = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( retval )
return retval ;
if ( ! data - > baseline_ready ) {
retval = - ENODATA ;
goto out ;
}
retval = snprintf ( buf , PAGE_SIZE , " %d " , data - > min_baseline ) ;
out :
mutex_unlock ( & data - > sysfs_mutex ) ;
return retval ;
}
static ssize_t max_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int retval ;
retval = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( retval )
return retval ;
if ( ! data - > baseline_ready ) {
retval = - ENODATA ;
goto out ;
}
retval = snprintf ( buf , PAGE_SIZE , " %d " , data - > max_baseline ) ;
out :
mutex_unlock ( & data - > sysfs_mutex ) ;
return retval ;
}
static DEVICE_ATTR_WO ( acquire ) ;
static DEVICE_ATTR_RO ( min ) ;
static DEVICE_ATTR_RO ( max ) ;
static struct attribute * elan_baseline_sysfs_entries [ ] = {
& dev_attr_acquire . attr ,
& dev_attr_min . attr ,
& dev_attr_max . attr ,
NULL ,
} ;
static const struct attribute_group elan_baseline_sysfs_group = {
. name = " baseline " ,
. attrs = elan_baseline_sysfs_entries ,
} ;
static const struct attribute_group * elan_sysfs_groups [ ] = {
& elan_sysfs_group ,
& elan_baseline_sysfs_group ,
NULL
} ;
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Elan isr functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2020-07-20 22:46:10 +03:00
static void elan_report_contact ( struct elan_tp_data * data , int contact_num ,
bool contact_valid , bool high_precision ,
u8 * packet , u8 * finger_data )
2014-10-04 00:24:27 +04:00
{
struct input_dev * input = data - > input ;
unsigned int pos_x , pos_y ;
2020-07-20 22:46:10 +03:00
unsigned int pressure , scaled_pressure ;
2014-10-04 00:24:27 +04:00
if ( contact_valid ) {
2020-07-20 22:46:10 +03:00
if ( high_precision ) {
pos_x = get_unaligned_be16 ( & finger_data [ 0 ] ) ;
pos_y = get_unaligned_be16 ( & finger_data [ 2 ] ) ;
} else {
pos_x = ( ( finger_data [ 0 ] & 0xf0 ) < < 4 ) | finger_data [ 1 ] ;
pos_y = ( ( finger_data [ 0 ] & 0x0f ) < < 8 ) | finger_data [ 2 ] ;
}
2014-10-04 00:24:27 +04:00
if ( pos_x > data - > max_x | | pos_y > data - > max_y ) {
dev_dbg ( input - > dev . parent ,
" [%d] x=%d y=%d over max (%d, %d) " ,
contact_num , pos_x , pos_y ,
data - > max_x , data - > max_y ) ;
return ;
}
2020-07-20 22:46:10 +03:00
pressure = finger_data [ 4 ] ;
2015-04-13 02:01:05 +03:00
scaled_pressure = pressure + data - > pressure_adjustment ;
if ( scaled_pressure > ETP_MAX_PRESSURE )
scaled_pressure = ETP_MAX_PRESSURE ;
2014-10-04 00:24:27 +04:00
input_mt_slot ( input , contact_num ) ;
input_mt_report_slot_state ( input , MT_TOOL_FINGER , true ) ;
input_report_abs ( input , ABS_MT_POSITION_X , pos_x ) ;
input_report_abs ( input , ABS_MT_POSITION_Y , data - > max_y - pos_y ) ;
2015-07-07 20:25:39 +03:00
input_report_abs ( input , ABS_MT_PRESSURE , scaled_pressure ) ;
2020-07-20 22:46:10 +03:00
if ( data - > report_features & ETP_FEATURE_REPORT_MK ) {
unsigned int mk_x , mk_y , area_x , area_y ;
u8 mk_data = high_precision ?
packet [ ETP_MK_DATA_OFFSET + contact_num ] :
finger_data [ 3 ] ;
mk_x = mk_data & 0x0f ;
mk_y = mk_data > > 4 ;
/*
* To avoid treating large finger as palm , let ' s reduce
* the width x and y per trace .
*/
area_x = mk_x * ( data - > width_x - ETP_FWIDTH_REDUCE ) ;
area_y = mk_y * ( data - > width_y - ETP_FWIDTH_REDUCE ) ;
input_report_abs ( input , ABS_TOOL_WIDTH , mk_x ) ;
input_report_abs ( input , ABS_MT_TOUCH_MAJOR ,
max ( area_x , area_y ) ) ;
input_report_abs ( input , ABS_MT_TOUCH_MINOR ,
min ( area_x , area_y ) ) ;
}
2014-10-04 00:24:27 +04:00
} else {
input_mt_slot ( input , contact_num ) ;
2020-05-11 23:12:13 +03:00
input_mt_report_slot_inactive ( input ) ;
2014-10-04 00:24:27 +04:00
}
}
2020-07-20 22:46:10 +03:00
static void elan_report_absolute ( struct elan_tp_data * data , u8 * packet ,
bool high_precision )
2014-10-04 00:24:27 +04:00
{
struct input_dev * input = data - > input ;
u8 * finger_data = & packet [ ETP_FINGER_DATA_OFFSET ] ;
int i ;
u8 tp_info = packet [ ETP_TOUCH_INFO_OFFSET ] ;
2015-04-20 19:59:04 +03:00
u8 hover_info = packet [ ETP_HOVER_INFO_OFFSET ] ;
bool contact_valid , hover_event ;
2014-10-04 00:24:27 +04:00
2020-07-07 03:39:41 +03:00
pm_wakeup_event ( & data - > client - > dev , 0 ) ;
2020-07-20 22:46:10 +03:00
hover_event = hover_info & BIT ( 6 ) ;
2014-10-04 00:24:27 +04:00
2020-07-20 22:46:10 +03:00
for ( i = 0 ; i < ETP_MAX_FINGERS ; i + + ) {
contact_valid = tp_info & BIT ( 3 + i ) ;
elan_report_contact ( data , i , contact_valid , high_precision ,
packet , finger_data ) ;
2014-10-04 00:24:27 +04:00
if ( contact_valid )
finger_data + = ETP_FINGER_DATA_LEN ;
}
2019-05-28 04:44:40 +03:00
input_report_key ( input , BTN_LEFT , tp_info & BIT ( 0 ) ) ;
input_report_key ( input , BTN_MIDDLE , tp_info & BIT ( 2 ) ) ;
input_report_key ( input , BTN_RIGHT , tp_info & BIT ( 1 ) ) ;
2015-07-07 20:25:39 +03:00
input_report_abs ( input , ABS_DISTANCE , hover_event ! = 0 ) ;
2014-10-04 00:24:27 +04:00
input_mt_report_pointer_emulation ( input , true ) ;
input_sync ( input ) ;
}
2018-05-23 03:23:04 +03:00
static void elan_report_trackpoint ( struct elan_tp_data * data , u8 * report )
{
struct input_dev * input = data - > tp_input ;
u8 * packet = & report [ ETP_REPORT_ID_OFFSET + 1 ] ;
int x , y ;
2020-07-07 03:39:41 +03:00
pm_wakeup_event ( & data - > client - > dev , 0 ) ;
2018-05-23 03:23:04 +03:00
if ( ! data - > tp_input ) {
dev_warn_once ( & data - > client - > dev ,
" received a trackpoint report while no trackpoint device has been created. Please report upstream. \n " ) ;
return ;
}
input_report_key ( input , BTN_LEFT , packet [ 0 ] & 0x01 ) ;
input_report_key ( input , BTN_RIGHT , packet [ 0 ] & 0x02 ) ;
input_report_key ( input , BTN_MIDDLE , packet [ 0 ] & 0x04 ) ;
if ( ( packet [ 3 ] & 0x0F ) = = 0x06 ) {
x = packet [ 4 ] - ( int ) ( ( packet [ 1 ] ^ 0x80 ) < < 1 ) ;
y = ( int ) ( ( packet [ 2 ] ^ 0x80 ) < < 1 ) - packet [ 5 ] ;
input_report_rel ( input , REL_X , x ) ;
input_report_rel ( input , REL_Y , y ) ;
}
input_sync ( input ) ;
}
2014-10-04 00:24:27 +04:00
static irqreturn_t elan_isr ( int irq , void * dev_id )
{
struct elan_tp_data * data = dev_id ;
int error ;
u8 report [ ETP_MAX_REPORT_LEN ] ;
/*
* When device is connected to i2c bus , when all IAP page writes
* complete , the driver will receive interrupt and must read
* 0000 to confirm that IAP is finished .
*/
if ( data - > in_fw_update ) {
complete ( & data - > fw_completion ) ;
goto out ;
}
2020-07-20 22:46:10 +03:00
error = data - > ops - > get_report ( data - > client , report , data - > report_len ) ;
2014-10-04 00:24:27 +04:00
if ( error )
goto out ;
2018-05-23 03:23:04 +03:00
switch ( report [ ETP_REPORT_ID_OFFSET ] ) {
case ETP_REPORT_ID :
2020-07-20 22:46:10 +03:00
elan_report_absolute ( data , report , false ) ;
break ;
case ETP_REPORT_ID2 :
elan_report_absolute ( data , report , true ) ;
2018-05-23 03:23:04 +03:00
break ;
case ETP_TP_REPORT_ID :
2020-12-11 10:40:09 +03:00
case ETP_TP_REPORT_ID2 :
2018-05-23 03:23:04 +03:00
elan_report_trackpoint ( data , report ) ;
break ;
default :
2020-07-07 03:39:41 +03:00
dev_err ( & data - > client - > dev , " invalid report id data (%x) \n " ,
2014-10-04 00:24:27 +04:00
report [ ETP_REPORT_ID_OFFSET ] ) ;
2018-05-23 03:23:04 +03:00
}
2014-10-04 00:24:27 +04:00
out :
return IRQ_HANDLED ;
}
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Elan initialization functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2018-05-23 03:23:04 +03:00
static int elan_setup_trackpoint_input_device ( struct elan_tp_data * data )
{
struct device * dev = & data - > client - > dev ;
struct input_dev * input ;
input = devm_input_allocate_device ( dev ) ;
if ( ! input )
return - ENOMEM ;
input - > name = " Elan TrackPoint " ;
input - > id . bustype = BUS_I2C ;
input - > id . vendor = ELAN_VENDOR_ID ;
input - > id . product = data - > product_id ;
input_set_drvdata ( input , data ) ;
input_set_capability ( input , EV_REL , REL_X ) ;
input_set_capability ( input , EV_REL , REL_Y ) ;
input_set_capability ( input , EV_KEY , BTN_LEFT ) ;
input_set_capability ( input , EV_KEY , BTN_RIGHT ) ;
input_set_capability ( input , EV_KEY , BTN_MIDDLE ) ;
__set_bit ( INPUT_PROP_POINTER , input - > propbit ) ;
__set_bit ( INPUT_PROP_POINTING_STICK , input - > propbit ) ;
data - > tp_input = input ;
return 0 ;
}
2014-10-04 00:24:27 +04:00
static int elan_setup_input_device ( struct elan_tp_data * data )
{
struct device * dev = & data - > client - > dev ;
struct input_dev * input ;
unsigned int max_width = max ( data - > width_x , data - > width_y ) ;
unsigned int min_width = min ( data - > width_x , data - > width_y ) ;
int error ;
input = devm_input_allocate_device ( dev ) ;
if ( ! input )
return - ENOMEM ;
input - > name = " Elan Touchpad " ;
input - > id . bustype = BUS_I2C ;
2015-12-15 22:32:10 +03:00
input - > id . vendor = ELAN_VENDOR_ID ;
input - > id . product = data - > product_id ;
2014-10-04 00:24:27 +04:00
input_set_drvdata ( input , data ) ;
error = input_mt_init_slots ( input , ETP_MAX_FINGERS ,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED ) ;
if ( error ) {
dev_err ( dev , " failed to initialize MT slots: %d \n " , error ) ;
return error ;
}
__set_bit ( EV_ABS , input - > evbit ) ;
__set_bit ( INPUT_PROP_POINTER , input - > propbit ) ;
2019-05-28 04:44:40 +03:00
if ( data - > clickpad ) {
2017-08-19 02:49:53 +03:00
__set_bit ( INPUT_PROP_BUTTONPAD , input - > propbit ) ;
2019-05-28 04:44:40 +03:00
} else {
2017-08-19 02:49:53 +03:00
__set_bit ( BTN_RIGHT , input - > keybit ) ;
2019-05-28 04:44:40 +03:00
if ( data - > middle_button )
__set_bit ( BTN_MIDDLE , input - > keybit ) ;
}
2014-10-04 00:24:27 +04:00
__set_bit ( BTN_LEFT , input - > keybit ) ;
/* Set up ST parameters */
input_set_abs_params ( input , ABS_X , 0 , data - > max_x , 0 , 0 ) ;
input_set_abs_params ( input , ABS_Y , 0 , data - > max_y , 0 , 0 ) ;
input_abs_set_res ( input , ABS_X , data - > x_res ) ;
input_abs_set_res ( input , ABS_Y , data - > y_res ) ;
input_set_abs_params ( input , ABS_PRESSURE , 0 , ETP_MAX_PRESSURE , 0 , 0 ) ;
2020-07-20 22:46:10 +03:00
if ( data - > report_features & ETP_FEATURE_REPORT_MK )
input_set_abs_params ( input , ABS_TOOL_WIDTH ,
0 , ETP_FINGER_WIDTH , 0 , 0 ) ;
2015-07-07 20:25:39 +03:00
input_set_abs_params ( input , ABS_DISTANCE , 0 , 1 , 0 , 0 ) ;
2014-10-04 00:24:27 +04:00
/* And MT parameters */
input_set_abs_params ( input , ABS_MT_POSITION_X , 0 , data - > max_x , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_POSITION_Y , 0 , data - > max_y , 0 , 0 ) ;
input_abs_set_res ( input , ABS_MT_POSITION_X , data - > x_res ) ;
input_abs_set_res ( input , ABS_MT_POSITION_Y , data - > y_res ) ;
input_set_abs_params ( input , ABS_MT_PRESSURE , 0 ,
ETP_MAX_PRESSURE , 0 , 0 ) ;
2020-07-20 22:46:10 +03:00
if ( data - > report_features & ETP_FEATURE_REPORT_MK ) {
input_set_abs_params ( input , ABS_MT_TOUCH_MAJOR ,
0 , ETP_FINGER_WIDTH * max_width , 0 , 0 ) ;
input_set_abs_params ( input , ABS_MT_TOUCH_MINOR ,
0 , ETP_FINGER_WIDTH * min_width , 0 , 0 ) ;
}
2014-10-04 00:24:27 +04:00
data - > input = input ;
return 0 ;
}
static void elan_disable_regulator ( void * _data )
{
struct elan_tp_data * data = _data ;
regulator_disable ( data - > vcc ) ;
}
2022-11-19 01:39:15 +03:00
static int elan_probe ( struct i2c_client * client )
2014-10-04 00:24:27 +04:00
{
const struct elan_transport_ops * transport_ops ;
struct device * dev = & client - > dev ;
struct elan_tp_data * data ;
unsigned long irqflags ;
int error ;
if ( IS_ENABLED ( CONFIG_MOUSE_ELAN_I2C_I2C ) & &
i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
transport_ops = & elan_i2c_ops ;
} else if ( IS_ENABLED ( CONFIG_MOUSE_ELAN_I2C_SMBUS ) & &
i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK ) ) {
transport_ops = & elan_smbus_ops ;
} else {
dev_err ( dev , " not a supported I2C/SMBus adapter \n " ) ;
return - EIO ;
}
2017-01-22 10:44:46 +03:00
data = devm_kzalloc ( dev , sizeof ( struct elan_tp_data ) , GFP_KERNEL ) ;
2014-10-04 00:24:27 +04:00
if ( ! data )
return - ENOMEM ;
i2c_set_clientdata ( client , data ) ;
data - > ops = transport_ops ;
data - > client = client ;
init_completion ( & data - > fw_completion ) ;
mutex_init ( & data - > sysfs_mutex ) ;
2017-01-22 10:44:46 +03:00
data - > vcc = devm_regulator_get ( dev , " vcc " ) ;
2014-10-04 00:24:27 +04:00
if ( IS_ERR ( data - > vcc ) ) {
error = PTR_ERR ( data - > vcc ) ;
if ( error ! = - EPROBE_DEFER )
2017-01-22 10:44:46 +03:00
dev_err ( dev , " Failed to get 'vcc' regulator: %d \n " ,
2014-10-04 00:24:27 +04:00
error ) ;
return error ;
}
error = regulator_enable ( data - > vcc ) ;
if ( error ) {
2017-01-22 10:44:46 +03:00
dev_err ( dev , " Failed to enable regulator: %d \n " , error ) ;
2014-10-04 00:24:27 +04:00
return error ;
}
2019-07-13 11:18:14 +03:00
error = devm_add_action_or_reset ( dev , elan_disable_regulator , data ) ;
2014-10-04 00:24:27 +04:00
if ( error ) {
2017-01-22 10:44:46 +03:00
dev_err ( dev , " Failed to add disable regulator action: %d \n " ,
2014-10-04 00:24:27 +04:00
error ) ;
return error ;
}
2014-12-19 23:57:49 +03:00
/* Make sure there is something at this address */
error = i2c_smbus_read_byte ( client ) ;
if ( error < 0 ) {
dev_dbg ( & client - > dev , " nothing at this address: %d \n " , error ) ;
return - ENXIO ;
}
2014-10-04 00:24:27 +04:00
/* Initialize the touchpad. */
2021-03-08 05:23:25 +03:00
error = elan_initialize ( data , false ) ;
2014-10-04 00:24:27 +04:00
if ( error )
return error ;
error = elan_query_device_info ( data ) ;
if ( error )
return error ;
error = elan_query_device_parameters ( data ) ;
if ( error )
return error ;
2017-01-22 10:44:46 +03:00
dev_info ( dev ,
2016-10-27 02:26:19 +03:00
" Elan Touchpad: Module ID: 0x%04x, Firmware: 0x%04x, Sample: 0x%04x, IAP: 0x%04x \n " ,
data - > product_id ,
data - > fw_version ,
data - > sm_version ,
data - > iap_version ) ;
2017-01-22 10:44:46 +03:00
dev_dbg ( dev ,
2016-10-27 02:26:19 +03:00
" Elan Touchpad Extra Information: \n "
2014-10-04 00:24:27 +04:00
" Max ABS X,Y: %d,%d \n "
" Width X,Y: %d,%d \n "
2016-11-28 07:59:29 +03:00
" Resolution X,Y: %d,%d (dots/mm) \n "
" ic type: 0x%x \n "
" info pattern: 0x%x \n " ,
2014-10-04 00:24:27 +04:00
data - > max_x , data - > max_y ,
data - > width_x , data - > width_y ,
2016-11-28 07:59:29 +03:00
data - > x_res , data - > y_res ,
data - > ic_type , data - > pattern ) ;
2014-10-04 00:24:27 +04:00
/* Set up input device properties based on queried parameters. */
error = elan_setup_input_device ( data ) ;
if ( error )
return error ;
2018-05-23 03:23:04 +03:00
if ( device_property_read_bool ( & client - > dev , " elan,trackpoint " ) ) {
error = elan_setup_trackpoint_input_device ( data ) ;
if ( error )
return error ;
}
2014-10-04 00:24:27 +04:00
/*
2017-09-28 19:57:34 +03:00
* Platform code ( ACPI , DTS ) should normally set up interrupt
* for us , but in case it did not let ' s fall back to using falling
* edge to be compatible with older Chromebooks .
2014-10-04 00:24:27 +04:00
*/
2017-09-28 19:57:34 +03:00
irqflags = irq_get_trigger_type ( client - > irq ) ;
if ( ! irqflags )
irqflags = IRQF_TRIGGER_FALLING ;
2014-10-04 00:24:27 +04:00
2017-01-22 10:44:46 +03:00
error = devm_request_threaded_irq ( dev , client - > irq , NULL , elan_isr ,
2014-10-04 00:24:27 +04:00
irqflags | IRQF_ONESHOT ,
client - > name , data ) ;
if ( error ) {
2017-01-22 10:44:46 +03:00
dev_err ( dev , " cannot register irq=%d \n " , client - > irq ) ;
2014-10-04 00:24:27 +04:00
return error ;
}
error = input_register_device ( data - > input ) ;
if ( error ) {
2017-01-22 10:44:46 +03:00
dev_err ( dev , " failed to register input device: %d \n " , error ) ;
2014-10-04 00:24:27 +04:00
return error ;
}
2018-05-23 03:23:04 +03:00
if ( data - > tp_input ) {
error = input_register_device ( data - > tp_input ) ;
if ( error ) {
dev_err ( & client - > dev ,
" failed to register TrackPoint input device: %d \n " ,
error ) ;
return error ;
}
}
2014-10-04 00:24:27 +04:00
return 0 ;
}
2023-01-02 21:17:59 +03:00
static int elan_suspend ( struct device * dev )
2014-10-04 00:24:27 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int ret ;
/*
* We are taking the mutex to make sure sysfs operations are
* complete before we attempt to bring the device into low [ er ]
* power mode .
*/
ret = mutex_lock_interruptible ( & data - > sysfs_mutex ) ;
if ( ret )
return ret ;
disable_irq ( client - > irq ) ;
if ( device_may_wakeup ( dev ) ) {
ret = elan_sleep ( data ) ;
} else {
2022-03-01 10:39:38 +03:00
ret = elan_set_power ( data , false ) ;
if ( ret )
goto err ;
ret = regulator_disable ( data - > vcc ) ;
if ( ret ) {
dev_err ( dev , " error %d disabling regulator \n " , ret ) ;
/* Attempt to power the chip back up */
elan_set_power ( data , true ) ;
}
2014-10-04 00:24:27 +04:00
}
2022-03-01 10:39:38 +03:00
err :
2014-10-04 00:24:27 +04:00
mutex_unlock ( & data - > sysfs_mutex ) ;
return ret ;
}
2023-01-02 21:17:59 +03:00
static int elan_resume ( struct device * dev )
2014-10-04 00:24:27 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct elan_tp_data * data = i2c_get_clientdata ( client ) ;
int error ;
2022-03-01 10:39:50 +03:00
if ( ! device_may_wakeup ( dev ) ) {
error = regulator_enable ( data - > vcc ) ;
if ( error ) {
dev_err ( dev , " error %d enabling regulator \n " , error ) ;
goto err ;
}
2014-10-04 00:24:27 +04:00
}
2022-03-01 10:39:38 +03:00
error = elan_set_power ( data , true ) ;
2015-03-08 07:57:54 +03:00
if ( error ) {
2014-10-04 00:24:27 +04:00
dev_err ( dev , " power up when resuming failed: %d \n " , error ) ;
2015-03-08 07:57:54 +03:00
goto err ;
}
2014-10-04 00:24:27 +04:00
2021-03-08 05:23:25 +03:00
error = elan_initialize ( data , data - > quirks & ETP_QUIRK_QUICK_WAKEUP ) ;
2014-10-04 00:24:27 +04:00
if ( error )
dev_err ( dev , " initialize when resuming failed: %d \n " , error ) ;
2015-03-08 07:57:54 +03:00
err :
2014-10-04 00:24:27 +04:00
enable_irq ( data - > client - > irq ) ;
2015-03-08 07:57:54 +03:00
return error ;
2014-10-04 00:24:27 +04:00
}
2023-01-02 21:17:59 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( elan_pm_ops , elan_suspend , elan_resume ) ;
2014-10-04 00:24:27 +04:00
static const struct i2c_device_id elan_id [ ] = {
{ DRIVER_NAME , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , elan_id ) ;
# ifdef CONFIG_ACPI
2020-11-18 00:46:18 +03:00
# include <linux/input/elan-i2c-ids.h>
2014-10-04 00:24:27 +04:00
MODULE_DEVICE_TABLE ( acpi , elan_acpi_id ) ;
# endif
# ifdef CONFIG_OF
static const struct of_device_id elan_of_match [ ] = {
{ . compatible = " elan,ekth3000 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , elan_of_match ) ;
# endif
static struct i2c_driver elan_driver = {
. driver = {
. name = DRIVER_NAME ,
2023-01-02 21:17:59 +03:00
. pm = pm_sleep_ptr ( & elan_pm_ops ) ,
2014-10-04 00:24:27 +04:00
. acpi_match_table = ACPI_PTR ( elan_acpi_id ) ,
. of_match_table = of_match_ptr ( elan_of_match ) ,
2015-08-05 22:01:30 +03:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2022-08-02 20:16:14 +03:00
. dev_groups = elan_sysfs_groups ,
2014-10-04 00:24:27 +04:00
} ,
2023-05-17 19:55:42 +03:00
. probe = elan_probe ,
2014-10-04 00:24:27 +04:00
. id_table = elan_id ,
} ;
module_i2c_driver ( elan_driver ) ;
MODULE_AUTHOR ( " Duson Lin <dusonlin@emc.com.tw> " ) ;
MODULE_DESCRIPTION ( " Elan I2C/SMBus Touchpad driver " ) ;
MODULE_LICENSE ( " GPL " ) ;