From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dmitry Torokhov Subject: Re: [PATCH] Input: add i2c/smbus driver for elan touchpad Date: Mon, 16 Dec 2013 13:00:11 -0800 Message-ID: <20131216210011.GC8884@core.coreip.homeip.net> References: <1385099816-2873-1-git-send-email-dusonlin@emc.com.tw> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: Received: from mail-pd0-f170.google.com ([209.85.192.170]:44575 "EHLO mail-pd0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752505Ab3LQBvS (ORCPT ); Mon, 16 Dec 2013 20:51:18 -0500 Content-Disposition: inline In-Reply-To: <1385099816-2873-1-git-send-email-dusonlin@emc.com.tw> Sender: linux-input-owner@vger.kernel.org List-Id: linux-input@vger.kernel.org To: Duson Lin Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, agnescheng@google.com, phoenix@emc.com.tw Hi Duson, On Fri, Nov 22, 2013 at 01:56:55PM +0800, Duson Lin wrote: > This driver adds support for elan i2c/smbus touchpad found on some la= ptops PC > --- > drivers/input/mouse/Kconfig | 10 + > drivers/input/mouse/Makefile | 1 + > drivers/input/mouse/elan_i2c.c | 1846 ++++++++++++++++++++++++++++++= ++++++++++ > 3 files changed, 1857 insertions(+) > create mode 100644 drivers/input/mouse/elan_i2c.c >=20 > diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfi= g > index effa9c5..8ad4b38 100644 > --- a/drivers/input/mouse/Kconfig > +++ b/drivers/input/mouse/Kconfig > @@ -215,6 +215,16 @@ config MOUSE_CYAPA > To compile this driver as a module, choose M here: the module wil= l be > called cyapa. > =20 > +config MOUSE_ELAN_I2C > + tristate "ELAN I2C Touchpad support" > + depends on I2C > + help > + This driver adds support for Elan I2C Trackpads. > + Say y here if you have a ELAN I2C Touchpad. > + > + To compile this driver as a module, choose M here: the module wil= l be > + called elan_i2c. > + > config MOUSE_INPORT > tristate "InPort/MS/ATIXL busmouse" > depends on ISA > diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makef= ile > index c25efdb..24a12a6 100644 > --- a/drivers/input/mouse/Makefile > +++ b/drivers/input/mouse/Makefile > @@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH) +=3D appletouch.o > obj-$(CONFIG_MOUSE_ATARI) +=3D atarimouse.o > obj-$(CONFIG_MOUSE_BCM5974) +=3D bcm5974.o > obj-$(CONFIG_MOUSE_CYAPA) +=3D cyapa.o > +obj-$(CONFIG_MOUSE_ELAN_I2C) +=3D elan_i2c.o > obj-$(CONFIG_MOUSE_GPIO) +=3D gpio_mouse.o > obj-$(CONFIG_MOUSE_INPORT) +=3D inport.o > obj-$(CONFIG_MOUSE_LOGIBM) +=3D logibm.o > diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/ela= n_i2c.c > new file mode 100644 > index 0000000..9892ee1 > --- /dev/null > +++ b/drivers/input/mouse/elan_i2c.c > @@ -0,0 +1,1846 @@ > +=EF=BB=BF/* > + * Elan I2C/SMBus Touchpad driver > + * > + * Copyright (c) 2013 ELAN Microelectronics Corp. > + * > + * Author: =E6=9E=97=E6=94=BF=E7=B6=AD (Duson Lin) > + * Version: 1.4.6 > + * > + * This program is free software; you can redistribute it and/or mod= ify it > + * under the terms of the GNU General Public License version 2 as pu= blished > + * by the Free Software Foundation. > + * > + * Trademarks are the property of their respective owners. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define DRIVER_NAME "elan_i2c" > +#define ELAN_DRIVER_VERSION "1.4.6" > +#define ETP_PRESSURE_OFFSET 25 > +#define ETP_MAX_PRESSURE 255 > +#define ETP_FWIDTH_REDUCE 90 > +#define ETP_FINGER_WIDTH 15 > + > +#define ELAN_ADAPTER_FUNC_NONE 0 > +#define ELAN_ADAPTER_FUNC_I2C 1 > +#define ELAN_ADAPTER_FUNC_SMBUS 2 > +#define ELAN_ADAPTER_FUNC_BOTH 3 > + > +/* Length of Elan touchpad information */ > +#define ETP_INF_LENGTH 2 > +#define ETP_MAX_FINGERS 5 > +#define ETP_FINGER_DATA_LEN 5 > +#define ETP_REPORT_ID 0x5D > +#define ETP_MAX_REPORT_LEN 34 > +#define ETP_ENABLE_ABS 0x0001 > +#define ETP_ENABLE_CALIBRATE 0x0002 > +#define ETP_DISABLE_CALIBRATE 0x0000 > + > +/* Elan smbus command */ > +#define ETP_SMBUS_IAP_CMD 0x00 > +#define ETP_SMBUS_ENABLE_TP 0x20 > +#define ETP_SMBUS_DISABLE_TP 0x21 > +#define ETP_SMBUS_IAP_PASSWORD_WRITE 0x29 > +#define ETP_SMBUS_IAP_PASSWORD_READ 0x80 > +#define ETP_SMBUS_WRITE_FW_BLOCK 0x2A > +#define ETP_SMBUS_IAP_RESET_CMD 0x2B > +#define ETP_SMBUS_RANGE_CMD 0xA0 > +#define ETP_SMBUS_FW_VERSION_CMD 0xA1 > +#define ETP_SMBUS_XY_TRACENUM_CMD 0xA2 > +#define ETP_SMBUS_SM_VERSION_CMD 0xA3 > +#define ETP_SMBUS_UNIQUEID_CMD 0xA3 > +#define ETP_SMBUS_RESOLUTION_CMD 0xA4 > +#define ETP_SMBUS_HELLOPACKET_CMD 0xA7 > +#define ETP_SMBUS_PACKET_QUERY 0xA8 > +#define ETP_SMBUS_IAP_VERSION_CMD 0xAC > +#define ETP_SMBUS_IAP_CTRL_CMD 0xAD > +#define ETP_SMBUS_IAP_CHECKSUM_CMD 0xAE > +#define ETP_SMBUS_FW_CHECKSUM_CMD 0xAF > +#define ETP_SMBUS_MAX_BASELINE_CMD 0xC3 > +#define ETP_SMBUS_MIN_BASELINE_CMD 0xC4 > +#define ETP_SMBUS_CALIBRATE_QUERY 0xC5 > +#define ETP_SMBUS_REPORT_LEN 32 > +#define ETP_SMBUS_FINGER_DATA_OFFSET 2 > +#define ETP_SMBUS_HELLOPACKET_LEN 5 > +#define ETP_SMBUS_IAP_PASSWORD 0x1234 > +#define ETP_SMBUS_IAP_MODE_ON (1<<6) > + > +/* Elan i2c command */ > +#define ETP_I2C_RESET 0x0100 > +#define ETP_I2C_WAKE_UP 0x0800 > +#define ETP_I2C_SLEEP 0x0801 > +#define ETP_I2C_DESC_CMD 0x0001 > +#define ETP_I2C_REPORT_DESC_CMD 0x0002 > +#define ETP_I2C_STAND_CMD 0x0005 > +#define ETP_I2C_UNIQUEID_CMD 0x0101 > +#define ETP_I2C_FW_VERSION_CMD 0x0102 > +#define ETP_I2C_SM_VERSION_CMD 0x0103 > +#define ETP_I2C_XY_TRACENUM_CMD 0x0105 > +#define ETP_I2C_MAX_X_AXIS_CMD 0x0106 > +#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107 > +#define ETP_I2C_RESOLUTION_CMD 0x0108 > +#define ETP_I2C_IAP_VERSION_CMD 0x0110 > +#define ETP_I2C_SET_CMD 0x0300 > +#define ETP_I2C_MAX_BASELINE_CMD 0x0306 > +#define ETP_I2C_MIN_BASELINE_CMD 0x0307 > +#define ETP_I2C_FW_CHECKSUM_CMD 0x030F > +#define ETP_I2C_IAP_CTRL_CMD 0x0310 > +#define ETP_I2C_IAP_CMD 0x0311 > +#define ETP_I2C_IAP_RESET_CMD 0x0314 > +#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315 > +#define ETP_I2C_CALIBRATE_CMD 0x0316 > +#define ETP_I2C_REPORT_LEN 34 > +#define ETP_I2C_FINGER_DATA_OFFSET 4 > +#define ETP_I2C_REPORT_ID_OFFSET 2 > +#define ETP_I2C_DESC_LENGTH 30 > +#define ETP_I2C_REPORT_DESC_LENGTH 158 > +#define ETP_I2C_IAP_PASSWORD 0x1EA5 > +#define ETP_I2C_IAP_RESET 0xF0F0 > +#define ETP_I2C_MAIN_MODE_ON (1<<9) > +#define ETP_I2C_IAP_REG_L 0x01 > +#define ETP_I2C_IAP_REG_H 0x06 > + > +/* IAP F/W updater */ > +#define ETP_FW_NAME "elan_i2c.bin" > +#define ETP_IAP_VERSION_ADDR 0x0082 > +#define ETP_IAP_START_ADDR 0x0083 > +#define ETP_FW_IAP_PAGE_ERR (1<<5) > +#define ETP_FW_IAP_INTERFACE_ERR (1<<4) > +#define ETP_FW_PAGE_SIZE 64 > +#define ETP_FW_PAGE_COUNT 768 > +#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT) > +enum {UNKNOWN_MODE, IAP_MODE, MAIN_MODE}; > + > +struct dbfs_data { > + bool bfetch; > + u8 buffer[ETP_MAX_REPORT_LEN]; > +}; > + > +/* The main device structure */ > +struct elan_tp_data { > + struct i2c_client *client; > + struct input_dev *input; > + unsigned int max_x; > + unsigned int max_y; > + unsigned int width_x; > + unsigned int width_y; > + unsigned int irq; > + > + /* fields required for IAP firmware updater */ > + u16 unique_id; > + u16 fw_version; > + u16 sm_version; > + u16 iap_version; > + bool updated_fw; > + u16 iap_start_addr; > + > + /* irq wake is enabled */ > + bool irq_wake; > + bool smbus; > + bool enable_detail_info; > + > + /* fields required for debug fs */ > + struct mutex dbfs_mutex; > + struct dentry *dbfs_root; > + struct dbfs_data dbfs_buffer; > +}; > + > +u8 val[256]; > +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 = *val); > +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u1= 6 cmd); > +static int elan_initialize(struct elan_tp_data *data); > + > +/* > + ************************************************************** > + * debugfs interface > + ************************************************************** > +*/ > +static int elan_dbfs_open(struct inode *inode, struct file *file) > +{ > + int retval; > + struct elan_tp_data *data =3D inode->i_private; > + > + if (!data) > + return -ENODEV; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return retval; > + > + if (!kobject_get(&data->client->dev.kobj)) { Why are you pinning I2C device kobject? It will not prevent unbinding the driver if that's what you are aiming for. > + retval =3D -ENODEV; > + goto dbfs_out; > + } > + > + file->private_data =3D data; > +dbfs_out: > + mutex_unlock(&data->dbfs_mutex); > + return 0; > +} > + > +static int elan_dbfs_release(struct inode *inode, struct file *file) > +{ > + struct elan_tp_data *data =3D file->private_data; > + int retval; > + if (!data) > + return -ENODEV; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return retval; > + file->private_data =3D NULL; > + kobject_put(&data->client->dev.kobj); > + mutex_unlock(&data->dbfs_mutex); > + return 0; > +} > + > + > +static ssize_t elan_dbfs_read(struct file *file, > + char __user *buffer, size_t count, loff_t *ppos) > +{ > + struct elan_tp_data *data =3D file->private_data; > + int retval; > + if (!data) > + return -ENODEV; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return -EFAULT; > + if (data->dbfs_buffer.bfetch =3D=3D false) { > + if (!copy_to_user(buffer, data->dbfs_buffer.buffer, count)) { > + data->dbfs_buffer.bfetch =3D true; > + retval =3D count; > + } else { > + retval =3D -2; -ENOENT? Why? > + } > + } else { > + retval =3D -4; -EINTR??? > + } > + mutex_unlock(&data->dbfs_mutex); > + return retval; > +} > + > +static ssize_t elan_dbfs_write(struct file *file, > + const char __user *buffer, size_t count, loff_t *ppos) > +{ > + struct elan_tp_data *data =3D file->private_data; > + int retval; > + if (!data) > + return -ENODEV; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return -EFAULT; > + retval =3D count; > + mutex_unlock(&data->dbfs_mutex); > + return retval; > +} This looks like an unneeded stub. > + > +static long elan_dbfs_ioctrl(struct file *file, > + unsigned int cmd, unsigned long arg) > +{ > + int retval =3D 0; > + struct elan_tp_data *data =3D file->private_data; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return retval; > + mutex_unlock(&data->dbfs_mutex); > + return retval; > +} Same here. > + > +static const struct file_operations elan_debug_fops =3D { > + .open =3D elan_dbfs_open, > + .release =3D elan_dbfs_release, > + .read =3D elan_dbfs_read, > + .write =3D elan_dbfs_write, > + .unlocked_ioctl =3D elan_dbfs_ioctrl > +}; > + > +static int elan_dbfs_init(struct elan_tp_data *data) > +{ > + /* Create a global debugfs root for all elan devices */ > + /* sys/kernel/debug/elan */ > + data->dbfs_root =3D debugfs_create_dir("elan", NULL); > + if (!data->dbfs_root) { > + dev_err(&data->client->dev, "cannot create dbfs_root.\n"); > + return -ENODEV; > + } That should not be done in per-device code. That said, do you really need to dump your packets - looks like you are done with the driver development? > + mutex_init(&data->dbfs_mutex); > + > + debugfs_create_file(DRIVER_NAME, 0777, > + data->dbfs_root, data, &elan_debug_fops); > + data->dbfs_buffer.bfetch =3D false; > + return 0; > +} > + > +/********************************************************** > + * IAP firmware updater related routines * > + ********************************************************** > +*/ > + > +static int elan_iap_getmode(struct elan_tp_data *data) > +{ > + u16 constant; > + int retval; > + struct i2c_client *client =3D data->client; > + > + if (data->smbus) { > + retval =3D i2c_smbus_read_block_data(client, > + ETP_SMBUS_IAP_CTRL_CMD, val); > + if (retval < 0) { > + dev_err(&client->dev, "read iap ctrol fail.\n"); > + return UNKNOWN_MODE; > + } > + constant =3D be16_to_cpup((__be16 *)val); > + dev_dbg(&client->dev, "smbus iap control reg: 0x%04x.\n", > + constant); > + if ((constant & ETP_SMBUS_IAP_MODE_ON) =3D=3D 0x00) > + return MAIN_MODE; > + } else { > + retval =3D elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val); > + if (retval < 0) { > + dev_err(&client->dev, "read iap ctrol fail.\n"); > + return UNKNOWN_MODE; > + } > + constant =3D le16_to_cpup((__le16 *)val); > + dev_dbg(&client->dev, "i2c iap control reg: 0x%04x.\n", > + constant); > + if (constant & ETP_I2C_MAIN_MODE_ON) > + return MAIN_MODE; > + } Instead of doing if (smbus) do_this(); else do_that(); everywhere maybe you could define transport functions and invoke them through a pointer to transport ops? > + > + return IAP_MODE; > +} > + > +static int elan_iap_checksum(struct elan_tp_data *data) > +{ > + int retval =3D 0; > + u16 checksum =3D -1; > + struct i2c_client *client =3D data->client; > + > + if (data->smbus) { > + retval =3D i2c_smbus_read_block_data(client, > + ETP_SMBUS_IAP_CHECKSUM_CMD, val); > + if (retval < 0) { > + dev_err(&client->dev, "Read checksum fail, %d\n", > + retval); > + return -1; > + } > + checksum =3D be16_to_cpup((__be16 *)val); > + } else { > + retval =3D elan_i2c_read_cmd(client, > + ETP_I2C_IAP_CHECKSUM_CMD, val); > + if (retval < 0) { > + dev_err(&client->dev, "Read checksum fail, %d\n", > + retval); > + return -1; > + } > + checksum =3D le16_to_cpup((__le16 *)val); > + } > + return checksum; > +} > + > +static bool elan_iap_reset(struct elan_tp_data *data) > +{ > + int retval =3D 0; > + struct i2c_client *client =3D data->client; > + > + if (data->smbus) > + retval =3D i2c_smbus_write_byte(client, > + ETP_SMBUS_IAP_RESET_CMD); > + else > + retval =3D elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD, > + ETP_I2C_IAP_RESET); > + if (retval < 0) { > + dev_err(&client->dev, "cannot reset IC, %d\n", retval); > + return false; > + } > + return true; > +} > + > +static bool elan_iap_setflashkey(struct elan_tp_data *data) > +{ > + int retval =3D 0; > + struct i2c_client *client =3D data->client; > + u8 smbus_cmd[4] =3D {0x00, 0x0B, 0x00, 0x5A}; > + > + if (data->smbus) > + retval =3D i2c_smbus_write_block_data(client, > + ETP_SMBUS_IAP_CMD, 4, smbus_cmd); > + else > + retval =3D elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD, > + ETP_I2C_IAP_PASSWORD); > + if (retval < 0) { > + dev_err(&client->dev, "cannot set flash key, %d\n", retval); > + return false; > + } > + > + return true; > +} > + > +static int elan_check_fw(struct elan_tp_data *data, > + const struct firmware *fw) > +{ > + struct device *dev =3D &data->client->dev; > + > + /* Firmware must match exact PAGE_NUM * PAGE_SIZE bytes */ > + if (fw->size !=3D ETP_FW_SIZE) { > + dev_err(dev, "invalid firmware size =3D %zu, expected %d.\n", > + fw->size, ETP_FW_SIZE); > + return -EBADF; > + } > + > + /* Get IAP Start Address*/ > + memcpy(val, &fw->data[ETP_IAP_START_ADDR * 2], 2); > + data->iap_start_addr =3D le16_to_cpup((__le16 *)val); > + return 0; > +} > + > + > +static int elan_smbus_prepare_fw_update(struct elan_tp_data *data) > +{ > + struct i2c_client *client =3D data->client; > + struct device *dev =3D &data->client->dev; > + u16 password; > + u8 cmd[4] =3D {0x0F, 0x78, 0x00, 0x06}; > + > + /* Get FW in which mode (IAP_MODE/MAIN_MODE) */ > + int mode =3D elan_iap_getmode(data); > + if (mode =3D=3D UNKNOWN_MODE) > + return -1; > + > + if (mode =3D=3D MAIN_MODE) { > + > + /* set flash key*/ > + if (elan_iap_setflashkey(data) =3D=3D false) { > + dev_err(dev, "cannot set flash key\n"); > + return -1; > + } > + > + /* write iap password */ > + if (i2c_smbus_write_byte(client, > + ETP_SMBUS_IAP_PASSWORD_WRITE) < 0) { > + dev_err(dev, "cannot write iap password\n"); > + return -1; Would be nice using appropriate error codes. EIO? > + } > + > + if (i2c_smbus_write_block_data(client, > + ETP_SMBUS_IAP_CMD, 4, cmd) < 0) { > + dev_err(dev, "cannot write cmd\n"); > + return -1; > + } > + > + /* read password to check we enabled successfully. */ > + if (i2c_smbus_read_block_data(client, > + ETP_SMBUS_IAP_PASSWORD_READ, val) < 0) { > + dev_err(dev, "cannot get iap password\n"); > + return -1; > + } > + password =3D be16_to_cpup((__be16 *)val); > + > + if (password !=3D ETP_SMBUS_IAP_PASSWORD) { > + dev_err(dev, "wrong iap password =3D 0x%X\n", password); > + return -1; > + } > + /* wait 30ms, from MAIN_MODE change to IAP_MODE*/ > + msleep(30); > + } > + > + /* set flash key*/ > + if (elan_iap_setflashkey(data) =3D=3D false) { > + dev_err(dev, "cannot set flash key\n"); > + return -1; > + } > + > + /* Reset IC */ > + if (elan_iap_reset(data) =3D=3D false) { > + dev_err(dev, "iap reset fail.\n"); > + return -1; > + } > + > + return 0; > +} > + > +static int elan_i2c_prepare_fw_update(struct elan_tp_data *data) > +{ > + struct i2c_client *client =3D data->client; > + struct device *dev =3D &data->client->dev; > + u16 password; > + > + /* Get FW in which mode (IAP_MODE/MAIN_MODE) */ > + int mode =3D elan_iap_getmode(data); > + if (mode =3D=3D UNKNOWN_MODE) > + return -1; > + > + if (mode =3D=3D IAP_MODE) { > + /* Reset IC */ > + if (elan_iap_reset(data) =3D=3D false) > + return -1; > + msleep(30); > + } > + > + /* set flash key*/ > + if (elan_iap_setflashkey(data) =3D=3D false) { > + dev_err(dev, "cannot set flash key\n"); > + return -1; > + } > + > + /* Wait for F/W IAP initialization */ > + if (mode =3D=3D MAIN_MODE) > + msleep(100); > + else > + msleep(30); > + > + /* check is in iap mode or not*/ > + if (elan_iap_getmode(data) =3D=3D MAIN_MODE) { > + dev_err(dev, "status wrong.\n"); > + return -1; > + } > + > + /* set flash key again */ > + if (elan_iap_setflashkey(data) =3D=3D false) { > + dev_err(dev, "cannot set flash key\n"); > + return -1; > + } > + > + /* Wait for F/W IAP initialization */ > + if (mode =3D=3D MAIN_MODE) > + msleep(100); > + else > + msleep(30); > + > + /* read back to check we actually enabled successfully. */ > + if (elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val) < 0) { > + dev_err(dev, "cannot get iap register\n"); > + return -1; > + } > + password =3D le16_to_cpup((__le16 *)val); > + > + if (password !=3D ETP_I2C_IAP_PASSWORD) { > + dev_err(dev, "wrong iap password =3D 0x%X\n", password); > + return -1; > + } > + return 0; > +} > + > +static bool elan_iap_page_write_ok(struct elan_tp_data *data) > +{ > + u16 constant; > + int retval =3D 0; > + struct i2c_client *client =3D data->client; > + > + > + if (data->smbus) { > + retval =3D i2c_smbus_read_block_data(client, > + ETP_SMBUS_IAP_CTRL_CMD, val); > + if (retval < 0) > + return false; > + constant =3D be16_to_cpup((__be16 *)val); > + } else { > + retval =3D elan_i2c_read_cmd(client, > + ETP_I2C_IAP_CTRL_CMD, val); > + if (retval < 0) > + return false; > + constant =3D le16_to_cpup((__le16 *)val); > + } > + > + if (constant & ETP_FW_IAP_PAGE_ERR) > + return false; > + > + if (constant & ETP_FW_IAP_INTERFACE_ERR) > + return false; > + return true; > +} > + > +static int elan_smbus_write_fw_block(struct elan_tp_data *data, > + const u8 *page, u16 checksum, int idx) > +{ > + struct device *dev =3D &data->client->dev; > + int half_page_size =3D ETP_FW_PAGE_SIZE / 2; > + int repeat =3D 3; > + > + do { > + /* due to smbus can write 32 bytes one time, > + so, we must write data 2 times. > + */ > + i2c_smbus_write_block_data(data->client, > + ETP_SMBUS_WRITE_FW_BLOCK, > + half_page_size, > + page); > + i2c_smbus_write_block_data(data->client, > + ETP_SMBUS_WRITE_FW_BLOCK, > + half_page_size, > + (page + half_page_size)); > + /* Wait for F/W to update one page ROM data. */ > + usleep_range(8000, 10000); > + if (elan_iap_page_write_ok(data)) > + break; > + dev_info(dev, "IAP retry this page! [%d]\n", idx); > + repeat--; > + } while (repeat =3D=3D 0); > + > + if (repeat > 0) > + return 0; > + return -1; > + > +} > + > +static int elan_i2c_write_fw_block(struct elan_tp_data *data, > + const u8 *page, u16 checksum, int idx) > +{ > + struct device *dev =3D &data->client->dev; > + int ret; > + int repeat =3D 3; > + u8 page_store[ETP_FW_PAGE_SIZE + 4]; > + > + page_store[0] =3D ETP_I2C_IAP_REG_L; > + page_store[1] =3D ETP_I2C_IAP_REG_H; > + memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE); > + > + /* recode checksum at last two bytes */ > + page_store[ETP_FW_PAGE_SIZE+2] =3D (u8)(checksum & 0xFF); > + page_store[ETP_FW_PAGE_SIZE+3] =3D (u8)((checksum >> 8)&0xFF); > + > + do { > + ret =3D i2c_master_send(data->client, page_store, > + ETP_FW_PAGE_SIZE + 4); > + > + /* Wait for F/W to update one page ROM data. */ > + msleep(20); > + > + if (ret =3D=3D (ETP_FW_PAGE_SIZE + 4)) { > + if (elan_iap_page_write_ok(data)) > + break; > + } > + dev_dbg(dev, "IAP retry this page! [%d]\n", idx); > + repeat--; > + } while (repeat =3D=3D 0); > + > + if (repeat > 0) > + return 0; > + return -1; > +} > + > +static int elan_write_fw_block(struct elan_tp_data *data, > + const u8 *page, u16 checksum, int idx) > +{ > + int ret; > + if (data->smbus) > + ret =3D elan_smbus_write_fw_block(data, page, checksum, idx); > + else > + ret =3D elan_i2c_write_fw_block(data, page, checksum, idx); > + return ret; > +} > + > +static int elan_prepare_fw_update(struct elan_tp_data *data) > +{ > + int ret =3D 0; > + if (data->smbus) > + ret =3D elan_smbus_prepare_fw_update(data); > + else > + ret =3D elan_i2c_prepare_fw_update(data); > + return ret; > +} > + > +static int elan_firmware(struct elan_tp_data *data) > +{ > + struct device *dev =3D &data->client->dev; > + const struct firmware *fw; > + const char *fw_name =3D ETP_FW_NAME; > + int i, j, ret; > + u16 boot_page_count; > + u16 sw_checksum, fw_checksum; > + data->updated_fw =3D true; > + > + dev_info(dev, "Start firmware update....\n"); > + > + ret =3D request_firmware(&fw, ETP_FW_NAME, dev); > + if (ret) { > + dev_err(dev, "cannot load firmware from %s, %d\n", > + fw_name, ret); > + goto done; > + } > + /* check fw data match current iap version */ > + ret =3D elan_check_fw(data, fw); > + if (ret) { > + dev_err(dev, "Invalid Elan firmware from %s, %d\n", > + fw_name, ret); > + goto done; > + } > + /* setup IAP status */ > + ret =3D elan_prepare_fw_update(data); > + if (ret) > + goto done; > + sw_checksum =3D 0; > + fw_checksum =3D 0; > + boot_page_count =3D (data->iap_start_addr * 2) / ETP_FW_PAGE_SIZE; > + for (i =3D boot_page_count; i < ETP_FW_PAGE_COUNT; i++) { > + u16 checksum =3D 0; > + const u8 *page =3D &fw->data[i * ETP_FW_PAGE_SIZE]; > + > + for (j =3D 0; j < ETP_FW_PAGE_SIZE; j +=3D 2) > + checksum +=3D ((page[j + 1] << 8) | page[j]); > + > + ret =3D elan_write_fw_block(data, page, checksum, i); > + if (ret) { > + dev_err(dev, "write page %d fail\n", i); > + goto done; > + } > + sw_checksum +=3D checksum; > + } > + > + /* Wait WDT reset and power on reset */ > + msleep(600); > + > + /* check checksum */ > + fw_checksum =3D elan_iap_checksum(data); > + if (sw_checksum !=3D fw_checksum) { > + dev_err(dev, "checksum diff sw=3D[%04X], fw=3D[%04X]\n", > + sw_checksum, fw_checksum); > + ret =3D -1; > + goto done; > + } > + ret =3D 0; > +done: > + if (ret !=3D 0) { > + elan_iap_reset(data); > + data->updated_fw =3D false; > + } else { > + if (data->smbus) { > + data->updated_fw =3D false; > + elan_initialize(data); > + } > + } > + release_firmware(fw); > + return ret; > +} > + > +/****************************************************************** > +* Elan smbus interface > +******************************************************************* > +*/ > +static int elan_smbus_initialize(struct i2c_client *client) > +{ > + u8 check[ETP_SMBUS_HELLOPACKET_LEN] =3D {0x55, 0x55, 0x55, 0x55, 0x= 55}; > + u8 values[ETP_SMBUS_HELLOPACKET_LEN] =3D {0, 0, 0, 0, 0}; > + int ret; > + > + /* Get hello packet */ > + ret =3D i2c_smbus_read_block_data(client, > + ETP_SMBUS_HELLOPACKET_CMD, values); > + if (ret !=3D ETP_SMBUS_HELLOPACKET_LEN) { > + dev_err(&client->dev, "hello packet length fail\n"); > + return -1; > + } > + > + /* compare hello packet */ > + if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) { > + dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n", > + values[0], values[1], values[2], values[3], values[4]); > + return -1; > + } > + > + /* enable tp */ > + ret =3D i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP); > + return ret; > +} > + > +static int elan_smbus_enable_calibrate(struct i2c_client *client) > +{ > + u8 cmd[4] =3D {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_ENABLE_CALIBRAT= E}; > + > + return i2c_smbus_write_block_data(client, > + ETP_SMBUS_IAP_CMD, 4, cmd); > +} > + > +static int elan_smbus_disable_calibrate(struct i2c_client *client) > +{ > + u8 cmd[4] =3D {0x00, 0x07, 0x00, ETP_ENABLE_ABS|ETP_DISABLE_CALIBRA= TE}; > + > + return i2c_smbus_write_block_data(client, > + ETP_SMBUS_IAP_CMD, 4, cmd); > +} > + > +static int elan_smbus_enable_absolute_mode(struct i2c_client *client= ) > +{ > + u8 cmd[4] =3D {0x00, 0x07, 0x00, ETP_ENABLE_ABS}; > + > + return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd= ); > +} > + > +/***************************************************************** > +* Elan i2c interface > +****************************************************************** > +*/ > +static int elan_i2c_read_block(struct i2c_client *client, > + u16 reg, u8 *val, u16 len) > +{ > + struct i2c_msg msgs[2]; > + u8 buf[2]; > + int ret; > + > + buf[0] =3D reg & 0xff; > + buf[1] =3D (reg >> 8) & 0xff; > + > + msgs[0].addr =3D client->addr; > + msgs[0].flags =3D client->flags & I2C_M_TEN; > + msgs[0].len =3D 2; > + msgs[0].buf =3D buf; > + > + msgs[1].addr =3D client->addr; > + msgs[1].flags =3D client->flags & I2C_M_TEN; > + msgs[1].flags |=3D I2C_M_RD; > + msgs[1].len =3D len; > + msgs[1].buf =3D val; > + > + ret =3D i2c_transfer(client->adapter, msgs, 2); > + return ret !=3D 2 ? -EIO : 0; > +} > + > +static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 = *val) > +{ > + int retval; > + > + retval =3D elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH); > + if (retval < 0) { > + dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg); > + return retval; > + } > + return 0; > +} > + > +static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u1= 6 cmd) > +{ > + struct i2c_msg msg; > + u8 buf[4]; > + int ret; > + > + buf[0] =3D reg & 0xff; > + buf[1] =3D (reg >> 8) & 0xff; > + buf[2] =3D cmd & 0xff; > + buf[3] =3D (cmd >> 8) & 0xff; > + > + msg.addr =3D client->addr; > + msg.flags =3D client->flags & I2C_M_TEN; > + msg.len =3D 4; > + msg.buf =3D buf; > + > + ret =3D i2c_transfer(client->adapter, &msg, 1); > + return ret !=3D 1 ? -EIO : 0; > +} > + > +static int elan_i2c_reset(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, > + ETP_I2C_RESET); > +} > + > +static int elan_i2c_wake_up(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, > + ETP_I2C_WAKE_UP); > +} > + > +static int elan_i2c_sleep(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD, > + ETP_I2C_SLEEP); > +} > + > +static int elan_i2c_enable_absolute_mode(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD, > + ETP_ENABLE_ABS); > +} > + > +static int elan_i2c_enable_calibrate(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD, > + ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE); > +} > + > +static int elan_i2c_disable_calibrate(struct i2c_client *client) > +{ > + return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD, > + ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE); > +} > + > +static int elan_i2c_get_desc(struct i2c_client *client, u8 *val) > +{ > + return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val, > + ETP_I2C_DESC_LENGTH); > +} > + > +static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *v= al) > +{ > + return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD, > + val, ETP_I2C_REPORT_DESC_LENGTH); > +} > + > +static int elan_i2c_initialize(struct i2c_client *client) > +{ > + struct device *dev =3D &client->dev; > + int rc; > + > + rc =3D elan_i2c_reset(client); > + if (rc < 0) { > + dev_err(dev, "device reset failed.\n"); > + return -1; > + } > + > + /* wait for get reset return flag */ > + msleep(100); > + /* get reset return flag 0000 */ > + rc =3D i2c_master_recv(client, val, ETP_INF_LENGTH); > + if (rc < 0) { > + dev_err(dev, "get device reset return value failed.\n"); > + return -1; > + } > + > + rc =3D elan_i2c_get_desc(client, val); > + if (rc < 0) { > + dev_err(dev, "cannot get device descriptor.\n"); > + return -1; > + } > + > + rc =3D elan_i2c_get_report_desc(client, val); > + if (rc < 0) { > + dev_err(dev, "fetching report descriptor failed.\n"); > + return -1; > + } > + > + return 0; > +} > + > +/*******************************************************************= ******* > +* Genernal functions > +********************************************************************= ******* > +*/ > + > +/* > + * (value from firmware) * 10 + 790 =3D dpi > + * we also have to convert dpi to dots/mm (*10/254 to avoid floating= point) > + */ > +static unsigned int elan_convert_res(char val) > +{ > + int res; > + if (val & 0x80) { > + val =3D ~val + 1; > + res =3D (790 - val * 10) * 10 / 254; > + } else > + res =3D (val * 10 + 790) * 10 / 254; > + return res; > +} > + > +static int elan_get_iap_version(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_IAP_VERSION_CMD, val); > + ret =3D val[2]; > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_IAP_VERSION_CMD, val); > + ret =3D val[0]; > + } > + return ret; > +} > + > +static int elan_get_x_max(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_RANGE_CMD, val); > + ret =3D (0x0f & val[0]) << 8 | val[1]; > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_MAX_X_AXIS_CMD, val); > + ret =3D (0x0f & val[1]) << 8 | val[0]; > + } > + return ret; > +} > + > +static int elan_get_y_max(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_RANGE_CMD, val); > + ret =3D (0xf0 & val[0]) << 4 | val[2]; > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_MAX_Y_AXIS_CMD, val); > + ret =3D (0x0f & val[1]) << 8 | val[0]; > + } > + return ret; > +} > + > +static int elan_get_x_tracenum(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_XY_TRACENUM_CMD, val); > + ret =3D (val[1] - 1); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_XY_TRACENUM_CMD, val); > + ret =3D (val[0] - 1); > + } > + return ret; > +} > + > +static int elan_get_y_tracenum(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_XY_TRACENUM_CMD, val); > + ret =3D (val[2] - 1); > + } else { > + ret =3D elan_i2c_read_cmd(data->client, > + ETP_I2C_XY_TRACENUM_CMD, val); > + ret =3D (val[1] - 1); > + } > + return ret; > +} > + > +static int elan_get_fw_version(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_FW_VERSION_CMD, val); > + ret =3D val[2]; > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_FW_VERSION_CMD, val); > + ret =3D val[0]; > + } > + return ret; > +} > + > +static int elan_get_sm_version(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_SM_VERSION_CMD, val); > + else > + elan_i2c_read_block(data->client, > + ETP_I2C_SM_VERSION_CMD, val, 1); > + ret =3D val[0]; > + return ret; > +} > + > +static int elan_get_unique_id(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_UNIQUEID_CMD, val); > + ret =3D val[1]; > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_UNIQUEID_CMD, val); > + ret =3D val[0]; > + } > + return ret; > +} > + > +static int elan_get_x_resolution(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_RESOLUTION_CMD, val); > + ret =3D elan_convert_res(val[1] & 0x0F); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_RESOLUTION_CMD, val); > + ret =3D elan_convert_res(val[0]); > + } > + return ret; > +} > + > +static int elan_get_y_resolution(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_RESOLUTION_CMD, val); > + ret =3D elan_convert_res((val[1] & 0xF0) >> 4); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_RESOLUTION_CMD, val); > + ret =3D elan_convert_res(val[1]); > + } > + return ret; > +} > + > +static int elan_get_fw_checksum(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_FW_CHECKSUM_CMD, val); > + ret =3D be16_to_cpup((__be16 *)val); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_FW_CHECKSUM_CMD, val); > + ret =3D le16_to_cpup((__le16 *)val); > + } > + return ret; > +} > + > +static int elan_get_max_baseline(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_MAX_BASELINE_CMD, val); > + ret =3D be16_to_cpup((__be16 *)val); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_MAX_BASELINE_CMD, val); > + ret =3D le16_to_cpup((__le16 *)val); > + } > + return ret; > +} > + > +static int elan_get_min_baseline(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_MIN_BASELINE_CMD, val); > + ret =3D be16_to_cpup((__be16 *)val); > + } else { > + elan_i2c_read_cmd(data->client, > + ETP_I2C_MIN_BASELINE_CMD, val); > + ret =3D le16_to_cpup((__le16 *)val); > + } > + return ret; > +} > + > +static int elan_enable_calibrate(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) > + ret =3D elan_smbus_enable_calibrate(data->client); > + else > + ret =3D elan_i2c_enable_calibrate(data->client); > + return ret; > +} > + > +static int elan_disable_calibrate(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) > + ret =3D elan_smbus_disable_calibrate(data->client); > + else > + ret =3D elan_i2c_disable_calibrate(data->client); > + return ret; > +} > + > +static int elan_initialize(struct elan_tp_data *data) > +{ > + int ret; > + if (data->smbus) { > + ret =3D elan_smbus_initialize(data->client); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "device initialize failed.\n"); > + goto err_initialize; > + } > + > + ret =3D elan_smbus_enable_absolute_mode(data->client); > + if (ret < 0) > + dev_err(&data->client->dev, > + "cannot switch to absolute mode.\n"); > + } else { > + ret =3D elan_i2c_initialize(data->client); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "device initialize failed.\n"); > + goto err_initialize; > + } > + > + ret =3D elan_i2c_enable_absolute_mode(data->client); > + if (ret < 0) { > + dev_err(&data->client->dev, > + "cannot switch to absolute mode.\n"); > + goto err_initialize; > + } > + > + ret =3D elan_i2c_wake_up(data->client); > + if (ret < 0) > + dev_err(&data->client->dev, > + "device wake up failed.\n"); > + } > +err_initialize: > + return ret; > +} > + > +/*******************************************************************= * > + * below routines export interfaces to sysfs file system. > + * so user can get firmware/driver/hardware information using cat co= mmand. > + * e.g.: use below command to get firmware version > + * cat /sys/bus/i2c/drivers/elan_i2c/1-0015/firmware_version > + ******************************************************************* > + */ > +static ssize_t elan_sysfs_enable_detailinfo(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + data->enable_detail_info =3D true; What is this for? > + return sprintf(buf, "enable\n"); > +} > + > +static ssize_t elan_sysfs_read_fw_checksum(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + unsigned int checksum =3D 0; > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + if (data->enable_detail_info =3D=3D true) { > + checksum =3D elan_get_fw_checksum(data); > + data->enable_detail_info =3D false; > + } > + return sprintf(buf, "0x%04x\n", checksum); > +} > + > +static ssize_t elan_sysfs_read_unique_id(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + data->unique_id =3D elan_get_unique_id(data); > + return sprintf(buf, "0x%04x\n", data->unique_id); > +} > + > +static ssize_t elan_sysfs_read_driver_ver(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%s\n", ELAN_DRIVER_VERSION); This should be reported in MODULE_VERSION if you need it. > +} > + > +static ssize_t elan_sysfs_read_fw_ver(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + data->fw_version =3D elan_get_fw_version(data); > + return sprintf(buf, "0x%04x\n", data->fw_version); > +} > + > +static ssize_t elan_sysfs_read_sm_ver(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + data->sm_version =3D elan_get_sm_version(data); > + return sprintf(buf, "0x%04x\n", data->sm_version); > +} > + > +static ssize_t elan_sysfs_read_iap_ver(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + data->iap_version =3D elan_get_iap_version(data); > + return sprintf(buf, "0x%04x\n", data->iap_version); > +} > + > + > +static ssize_t elan_sysfs_update_fw(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + int ret; > + ret =3D elan_firmware(data); > + if (ret) > + dev_err(dev, "firmware update failed.\n"); > + else > + dev_info(dev, "firmware update succeeded.\n"); > + return ret ? ret : count; > +} > + > +static ssize_t elan_sysfs_calibrate(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + /* start calibarate cmd */ > + u8 smbus_cmd[4] =3D {0x00, 0x08, 0x00, 0x01}; > + int tries =3D 20; > + int ret =3D 0; > + val[0] =3D 0; > + > + disable_irq(data->irq); > + elan_enable_calibrate(data); > + if (data->smbus) > + i2c_smbus_write_block_data(data->client, > + ETP_SMBUS_IAP_CMD, 4, smbus_cmd); > + else > + elan_i2c_write_cmd(data->client, > + ETP_I2C_CALIBRATE_CMD, 1); > + > + do { > + /* wait 250ms and check finish or not */ > + msleep(250); > + > + if (data->smbus) > + i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_CALIBRATE_QUERY, val); > + else > + elan_i2c_read_block(data->client, > + ETP_I2C_CALIBRATE_CMD, val, 1); > + > + /* calibrate finish */ > + if (val[0] =3D=3D 0) > + break; > + } while (--tries); > + > + elan_disable_calibrate(data); > + enable_irq(data->irq); > + > + if (tries =3D=3D 0) { > + dev_err(dev, "Failed to calibrate. Timeout.\n"); > + ret =3D -ETIMEDOUT; > + } > + return sprintf(buf, "calibration finish\n"); > +} > + > + > +static ssize_t elan_sysfs_read_baseline(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + int max_baseline, min_baseline; > + > + disable_irq(data->irq); > + elan_enable_calibrate(data); > + msleep(250); > + max_baseline =3D elan_get_max_baseline(data); > + min_baseline =3D elan_get_min_baseline(data); > + elan_disable_calibrate(data); > + enable_irq(data->irq); > + return sprintf(buf, "max:%d min:%d\n", max_baseline, min_baseline); > +} > + > +static ssize_t elan_sysfs_reinitialize(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + int ret; > + > + disable_irq(data->irq); > + ret =3D elan_initialize(data); > + enable_irq(data->irq); > + > + if (ret < 0) > + return sprintf(buf, "reinitialize fail\n"); > + > + return sprintf(buf, "reinitialize success\n"); This actually should be a write method and instead of returning strings you should use return code to indicate success/failure. > +} > + > +static DEVICE_ATTR(unique_id, S_IRUGO, elan_sysfs_read_unique_id, NU= LL); > +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(driver_version, S_IRUGO, elan_sysfs_read_driver_v= er, NULL); > +static DEVICE_ATTR(iap_version, S_IRUGO, elan_sysfs_read_iap_ver, NU= LL); > +static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_sysfs_read_fw_checksum= , NULL); > +static DEVICE_ATTR(open_info, S_IRUGO, elan_sysfs_enable_detailinfo,= NULL); > +static DEVICE_ATTR(baseline, S_IRUGO, elan_sysfs_read_baseline, NULL= ); > +static DEVICE_ATTR(reinitialize, S_IRUGO, elan_sysfs_reinitialize, N= ULL); > +static DEVICE_ATTR(calibrate, S_IRUGO, elan_sysfs_calibrate, NULL); > +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_sysfs_update_fw); > + > +static struct attribute *elan_sysfs_entries[] =3D { > + &dev_attr_unique_id.attr, > + &dev_attr_firmware_version.attr, > + &dev_attr_sample_version.attr, > + &dev_attr_driver_version.attr, > + &dev_attr_iap_version.attr, > + &dev_attr_fw_checksum.attr, > + &dev_attr_open_info.attr, > + &dev_attr_baseline.attr, > + &dev_attr_reinitialize.attr, > + &dev_attr_calibrate.attr, > + &dev_attr_update_fw.attr, > + NULL, Please add documentation for sysfs entries to Documentation/...=20 > +}; > + > +static const struct attribute_group elan_sysfs_group =3D { > + .attrs =3D elan_sysfs_entries, > +}; > + > +/***************************************************************** > +* Elan isr functions > +****************************************************************** > +*/ > + > +static int elan_check_packet(struct elan_tp_data *data, u8 *packet) > +{ > + u8 rid; > + > + if (data->smbus) > + rid =3D packet[0]; > + else > + rid =3D packet[ETP_I2C_REPORT_ID_OFFSET]; > + > + /* check report id */ > + if (rid !=3D ETP_REPORT_ID) { > + dev_err(&data->client->dev, "report id [%x] fail.\n", rid); > + return -1; > + } > + return 0; > +} > + > +static void elan_report_absolute(struct elan_tp_data *data, u8 *pack= et) > +{ > + struct input_dev *input =3D data->input; > + u8 *finger_data; > + bool finger_on; > + int pos_x, pos_y; > + int pressure, mk_x, mk_y; > + int i, area_x, area_y, major, minor, new_pressure; > + int finger_count =3D 0; > + int btn_click; > + u8 tp_info; > + > + if (data->smbus) { > + finger_data =3D &packet[ETP_SMBUS_FINGER_DATA_OFFSET]; > + tp_info =3D packet[1]; > + } else { > + finger_data =3D &packet[ETP_I2C_FINGER_DATA_OFFSET]; > + tp_info =3D packet[3]; > + } > + > + btn_click =3D (tp_info & 0x01); > + for (i =3D 0; i < ETP_MAX_FINGERS; i++) { > + finger_on =3D (tp_info >> (3 + i)) & 0x01; > + > + /* analyze touched finger raw data*/ > + if (finger_on) { > + pos_x =3D ((finger_data[0] & 0xf0) << 4) | > + finger_data[1]; > + pos_y =3D ((finger_data[0] & 0x0f) << 8) | > + finger_data[2]; > + pos_y =3D data->max_y - pos_y; > + mk_x =3D (finger_data[3] & 0x0f); > + mk_y =3D (finger_data[3] >> 4); > + pressure =3D finger_data[4]; > + > + /* > + to avoid fat finger be as palm, so reduce the > + width x and y per trace > + */ > + area_x =3D mk_x * (data->width_x - ETP_FWIDTH_REDUCE); > + area_y =3D mk_y * (data->width_y - ETP_FWIDTH_REDUCE); > + > + major =3D max(area_x, area_y); > + minor =3D min(area_x, area_y); > + > + new_pressure =3D pressure + ETP_PRESSURE_OFFSET; > + if (new_pressure > ETP_MAX_PRESSURE) > + new_pressure =3D ETP_MAX_PRESSURE; > + > + input_mt_slot(input, i); > + 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, pos_y); > + input_report_abs(input, ABS_MT_PRESSURE, new_pressure); > + input_report_abs(input, ABS_TOOL_WIDTH, mk_x); > + input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); > + input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); > + finger_data +=3D ETP_FINGER_DATA_LEN; > + finger_count++; > + } else { > + input_mt_slot(input, i); > + input_mt_report_slot_state(input, > + MT_TOOL_FINGER, false); > + } > + } > + > + input_report_key(input, BTN_LEFT, (btn_click =3D=3D 1)); > + input_mt_report_pointer_emulation(input, true); > + input_sync(input); > +} > + > +static irqreturn_t elan_isr(int irq, void *dev_id) > +{ > + struct elan_tp_data *data =3D dev_id; > + u8 raw[ETP_MAX_REPORT_LEN]; > + int retval; > + int report_len; > + > + retval =3D mutex_lock_interruptible(&data->dbfs_mutex); > + if (retval) > + return IRQ_HANDLED; > + > + /* > + Only in I2C protocol, when IAP all page wrote finish, driver will > + get one INT signal from high to low, and driver must get 0000 > + to confirm IAP is finished. > + */ > + if (data->updated_fw) { > + retval =3D i2c_master_recv(data->client, raw, > + ETP_INF_LENGTH); > + if (retval =3D=3D 2 && !le16_to_cpup((__le16 *)raw)) { > + dev_info(&data->client->dev, > + "reinitializing after F/W update..."); > + elan_initialize(data); > + } > + data->updated_fw =3D false; > + goto elan_isr_end; > + } > + > + if (data->smbus) { > + report_len =3D ETP_SMBUS_REPORT_LEN; > + retval =3D i2c_smbus_read_block_data(data->client, > + ETP_SMBUS_PACKET_QUERY, raw); > + } else { > + report_len =3D ETP_I2C_REPORT_LEN; > + retval =3D i2c_master_recv(data->client, raw, report_len); > + } > + > + if (retval !=3D report_len) { > + dev_err(&data->client->dev, "wrong packet len(%d)", retval); > + goto elan_isr_end; > + } > + > + if (elan_check_packet(data, raw) < 0) { > + dev_err(&data->client->dev, "wrong packet format."); > + goto elan_isr_end; > + } > + elan_report_absolute(data, raw); > + data->dbfs_buffer.bfetch =3D false; > + memcpy(data->dbfs_buffer.buffer, raw, report_len); > + > +elan_isr_end: > + mutex_unlock(&data->dbfs_mutex); > + return IRQ_HANDLED; > +} > + > + > +static int elan_input_dev_create(struct elan_tp_data *data) > +{ > + struct i2c_client *client =3D data->client; > + struct input_dev *input; > + unsigned int x_res, y_res; > + int ret; > + > + data->input =3D input =3D input_allocate_device(); > + if (!input) > + return -ENOMEM; > + input->name =3D "Elan Touchpad"; > + input->id.bustype =3D BUS_I2C; > + input->dev.parent =3D &data->client->dev; > + > + __set_bit(INPUT_PROP_POINTER, input->propbit); > + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); > + __set_bit(EV_KEY, input->evbit); > + __set_bit(EV_ABS, input->evbit); > + > + __set_bit(BTN_LEFT, input->keybit); > + __set_bit(BTN_TOUCH, input->keybit); > + __set_bit(BTN_TOOL_FINGER, input->keybit); > + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); > + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); > + __set_bit(BTN_TOOL_QUADTAP, input->keybit); > + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); > + > + __set_bit(ABS_MT_TOUCH_MAJOR, input->absbit); > + __set_bit(ABS_MT_TOUCH_MINOR, input->absbit); > + __set_bit(ABS_MT_POSITION_X, input->absbit); > + __set_bit(ABS_MT_POSITION_Y, input->absbit); > + > + data->unique_id =3D elan_get_unique_id(data); > + data->fw_version =3D elan_get_fw_version(data); > + data->sm_version =3D elan_get_sm_version(data); > + data->iap_version =3D elan_get_iap_version(data); > + data->max_x =3D elan_get_x_max(data); > + data->max_y =3D elan_get_y_max(data); > + data->width_x =3D data->max_x / elan_get_x_tracenum(data); > + data->width_y =3D data->max_y / elan_get_y_tracenum(data); > + x_res =3D elan_get_x_resolution(data); > + y_res =3D elan_get_y_resolution(data); > + > + dev_info(&client->dev, > + "Elan Touchpad Information:\n" > + " Module unique ID: 0x%04x\n" > + " Firmware Version: 0x%04x\n" > + " Sample Version: 0x%04x\n" > + " IAP Version: 0x%04x\n" > + " Max ABS X,Y: %d,%d\n" > + " Width X,Y: %d,%d\n" > + " Resolution X,Y: %d,%d (dots/mm)\n", > + data->unique_id, > + data->fw_version, > + data->sm_version, > + data->iap_version, > + data->max_x, data->max_y, > + data->width_x, data->width_y, > + (char)x_res, (char)y_res); Why cast to char? > + > + 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, x_res); > + input_abs_set_res(input, ABS_Y, y_res); > + input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0= ); > + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0,= 0); > + > + /* handle pointer emulation and unused slots in core */ > + ret =3D input_mt_init_slots(input, ETP_MAX_FINGERS, > + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); > + if (ret) { > + dev_info(&client->dev, "allocate MT slots failed, %d\n", ret); > + goto err_free_device; > + } > + 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, x_res); > + input_abs_set_res(input, ABS_MT_POSITION_Y, y_res); > + input_set_abs_params(input, ABS_MT_PRESSURE, 0, > + ETP_MAX_PRESSURE, 0, 0); > + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, > + ETP_FINGER_WIDTH * max(data->width_x, data->width_y), 0, 0); > + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, > + ETP_FINGER_WIDTH * min(data->width_x, data->width_y), 0, 0); > + > + /* Register the device in input subsystem */ > + ret =3D input_register_device(input); > + if (ret) { > + dev_err(&client->dev, "input device register failed, %d\n", > + ret); > + goto err_free_device; > + } > + > + return 0; > + > +err_free_device: > + input_free_device(input); > + return ret; > +} > + > +static u8 elan_check_adapter_functionality(struct i2c_client *client= ) > +{ > + u8 ret =3D ELAN_ADAPTER_FUNC_NONE; > + > + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) > + ret |=3D ELAN_ADAPTER_FUNC_I2C; > + if (i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_BYTE_DATA | > + I2C_FUNC_SMBUS_BLOCK_DATA | > + I2C_FUNC_SMBUS_I2C_BLOCK)) > + ret |=3D ELAN_ADAPTER_FUNC_SMBUS; > + return ret; > +} > + > +static int elan_probe(struct i2c_client *client, > + const struct i2c_device_id *dev_id) > +{ > + struct elan_tp_data *data; > + int ret; > + u8 adapter_func; > + union i2c_smbus_data dummy; > + struct device *dev =3D &client->dev; > + > + adapter_func =3D elan_check_adapter_functionality(client); > + if (adapter_func =3D=3D ELAN_ADAPTER_FUNC_NONE) { > + dev_err(dev, "not a supported I2C/SMBus adapter\n"); > + return -EIO; > + } > + > + /* Make sure there is something at this address */ > + if (dev->of_node && i2c_smbus_xfer(client->adapter, client->addr, 0= , > + I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) > + return -ENODEV; > + > + data =3D kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + /* check protocol type */ > + if (adapter_func =3D=3D ELAN_ADAPTER_FUNC_SMBUS) > + data->smbus =3D true; > + else > + data->smbus =3D false; > + data->client =3D client; > + data->updated_fw =3D false; > + data->enable_detail_info =3D false; > + data->irq =3D client->irq; > + > + ret =3D elan_dbfs_init(data); > + if (ret < 0) { > + dev_err(&client->dev, "error create elan debugfs.\n"); > + goto err_dbfs_init; > + } > + ret =3D request_threaded_irq(client->irq, NULL, elan_isr, > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, > + client->name, data); > + if (ret < 0) { > + dev_err(&client->dev, "cannot register irq=3D%d\n", > + client->irq); > + goto err_irq; > + } > + > + /* initial elan touch pad */ > + ret =3D elan_initialize(data); > + if (ret < 0) > + goto err_init; > + > + /* create input device */ > + ret =3D elan_input_dev_create(data); > + if (ret < 0) > + goto err_input_dev; > + > + device_init_wakeup(&client->dev, 1); > + ret =3D sysfs_create_group(&client->dev.kobj, &elan_sysfs_group); > + if (ret < 0) { > + dev_err(&client->dev, "cannot register dev attribute %d", ret); > + goto err_create_group; > + } > + i2c_set_clientdata(client, data); > + return 0; > + > +err_create_group: > + input_free_device(data->input); The device is registered here so you need to call unregister, not free. > +err_input_dev: > +err_init: > + free_irq(data->irq, data); > +err_irq: > + debugfs_remove_recursive(data->dbfs_root); > + mutex_destroy(&data->dbfs_mutex); > +err_dbfs_init: > + kfree(data); > + dev_err(&client->dev, "Elan Trackpad probe fail!\n"); > + return ret; > +} > + > +static int elan_remove(struct i2c_client *client) > +{ > + struct elan_tp_data *data =3D i2c_get_clientdata(client); > + > + free_irq(data->irq, data); > + debugfs_remove_recursive(data->dbfs_root); > + mutex_destroy(&data->dbfs_mutex); > + > + input_free_device(data->input); Not needed (and harmful here). > + input_unregister_device(data->input); > + kfree(data); > + sysfs_remove_group(&client->dev.kobj, &elan_sysfs_group); I'd start with removing sysfs attributes as first thing in remove(). > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int elan_suspend(struct device *dev) > +{ > + int ret; > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + > + disable_irq(data->irq); > + if (data->smbus) > + ret =3D i2c_smbus_write_byte(data->client, > + ETP_SMBUS_DISABLE_TP); > + else > + ret =3D elan_i2c_sleep(data->client); > + > + if (ret < 0) { > + dev_err(dev, "suspend mode failed, %d\n", ret); > + } else { > + if (device_may_wakeup(dev)) > + data->irq_wake =3D (enable_irq_wake(data->irq) =3D=3D 0); > + } > + return 0; > +} > + > +static int elan_resume(struct device *dev) > +{ > + int ret =3D 0; > + struct elan_tp_data *data =3D dev_get_drvdata(dev); > + > + if (device_may_wakeup(dev) && data->irq_wake) > + disable_irq_wake(data->irq); > + > + ret =3D elan_initialize(data); > + if (ret < 0) > + dev_err(dev, "resume active power failed, %d\n", ret); > + > + enable_irq(data->irq); > + return 0; > +} > +#endif > + > +static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume); > + > +static const struct i2c_device_id elan_id[] =3D { > + { DRIVER_NAME, 0 }, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, elan_id); > + > +static struct i2c_driver elan_driver =3D { > + .driver =3D { > + .name =3D DRIVER_NAME, > + .owner =3D THIS_MODULE, > + .pm =3D &elan_pm_ops, > + }, > + .probe =3D elan_probe, > + .remove =3D elan_remove, > + .id_table =3D elan_id, > +}; > + > + > +static int __init elan_init(void) > +{ > + int ret; > + ret =3D i2c_add_driver(&elan_driver); > + if (ret) { > + pr_err("elan driver register FAILED.\n"); > + return ret; > + } > + > + return ret; > +} > + > +static void __exit elan_exit(void) > +{ > + i2c_del_driver(&elan_driver); > +} > + > +module_init(elan_init); > +module_exit(elan_exit); module_i2c_driver(). > + > +MODULE_AUTHOR("Duson Lin "); > +MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver"); > +MODULE_LICENSE("GPL"); > --=20 > 1.7.10.4 >=20 Thanks. --=20 Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-input" = in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html