OSDN Git Service

input: ft5x06: Add support for firmware upgrade
authorMohan Pallaka <mpallaka@codeaurora.org>
Tue, 4 Jun 2013 12:06:20 +0000 (17:36 +0530)
committerGerrit - the friendly Code Review server <code-review@localhost>
Thu, 11 Aug 2016 10:21:01 +0000 (03:21 -0700)
Add sysfs entries to upgrade firmware from userspace.
Firmware upgrade is triggered when the device's firmware
version mismatches with userspace's firmware file.
Force firmware upgrade sysfs entry is added to force
the firmware upgrade even when the firmware versions
match.

Change-Id: I48189abe60b4bd9d54a1bbc4657707213d8eb166
Signed-off-by: Mohan Pallaka <mpallaka@codeaurora.org>
Signed-off-by: Shantanu Jain <shjain@codeaurora.org>
drivers/input/touchscreen/ft5x06_ts.c

index 6cf3be2..45ce036 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
 #include <linux/input/ft5x06_ts.h>
 
 #if defined(CONFIG_FB)
@@ -35,7 +36,7 @@
 #elif defined(CONFIG_HAS_EARLYSUSPEND)
 #include <linux/earlysuspend.h>
 /* Early-suspend level */
-#define FT5X06_SUSPEND_LEVEL 1
+#define FT_SUSPEND_LEVEL 1
 #endif
 
 #define CFG_MAX_TOUCH_POINTS   5
 
 #define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
 
-/*register address*/
-#define FT5X06_REG_ID          0xA3
-#define FT5X06_REG_PMODE       0xA5
-#define FT5X06_REG_FW_VER      0xA6
-#define FT5X06_REG_POINT_RATE  0x88
-#define FT5X06_REG_THGROUP     0x80
-
-/* power register bits*/
-#define FT5X06_PMODE_ACTIVE            0x00
-#define FT5X06_PMODE_MONITOR           0x01
-#define FT5X06_PMODE_STANDBY           0x02
-#define FT5X06_PMODE_HIBERNATE         0x03
-
-#define FT5X06_VTG_MIN_UV      2600000
-#define FT5X06_VTG_MAX_UV      3300000
-#define FT5X06_I2C_VTG_MIN_UV  1800000
-#define FT5X06_I2C_VTG_MAX_UV  1800000
-
-#define FT5X06_COORDS_ARR_SIZE 4
+/* register address*/
+#define FT_REG_DEV_MODE                0x00
+#define FT_DEV_MODE_REG_CAL    0x02
+#define FT_REG_ID              0xA3
+#define FT_REG_PMODE           0xA5
+#define FT_REG_FW_VER          0xA6
+#define FT_REG_POINT_RATE      0x88
+#define FT_REG_THGROUP         0x80
+#define FT_REG_ECC             0xCC
+#define FT_REG_RESET_FW                0x07
+
+/* power register bits */
+#define FT_PMODE_ACTIVE                0x00
+#define FT_PMODE_MONITOR       0x01
+#define FT_PMODE_STANDBY       0x02
+#define FT_PMODE_HIBERNATE     0x03
+#define FT_FACTORYMODE_VALUE   0x40
+#define FT_WORKMODE_VALUE      0x00
+#define FT_RST_CMD_REG         0xFC
+#define FT_READ_ID_REG         0x90
+#define FT_ERASE_APP_REG       0x61
+#define FT_ERASE_PANEL_REG     0x63
+#define FT_FW_START_REG                0xBF
+
+
+#define FT_VTG_MIN_UV          2600000
+#define FT_VTG_MAX_UV          3300000
+#define FT_I2C_VTG_MIN_UV      1800000
+#define FT_I2C_VTG_MAX_UV      1800000
+
+#define FT_COORDS_ARR_SIZE     4
 #define MAX_BUTTONS            4
 
+#define FT_8BIT_SHIFT          8
+#define FT_4BIT_SHIFT          4
+#define FT_FW_NAME_MAX_LEN     50
+
+#define FT5316_ID              0x0A
+#define FT5306I_ID             0x55
+
+#define FT_UPGRADE_AA          0xAA
+#define FT_UPGRADE_55          0x55
+
+/* upgrade config of FT5606 */
+#define FT5606_UPGRADE_AA_DELAY                50
+#define FT5606_UPGRADE_55_DELAY                10
+#define FT5606_UPGRADE_ID_1            0x79
+#define FT5606_UPGRADE_ID_2            0x06
+#define FT5606_UPGRADE_READID_DELAY    100
+#define FT5606_UPGRADE_EARSE_DELAY     2000
+
+/* upgrade config of FT5316 */
+#define FT5316_UPGRADE_AA_DELAY                50
+#define FT5316_UPGRADE_55_DELAY                30
+#define FT5316_UPGRADE_ID_1            0x79
+#define FT5316_UPGRADE_ID_2            0x07
+#define FT5316_UPGRADE_READID_DELAY    1
+#define FT5316_UPGRADE_EARSE_DELAY     1500
+
+/* upgrade config of FT5x06(x=2,3,4) */
+#define FT5X06_UPGRADE_AA_DELAY                50
+#define FT5X06_UPGRADE_55_DELAY                30
+#define FT5X06_UPGRADE_ID_1            0x79
+#define FT5X06_UPGRADE_ID_2            0x03
+#define FT5X06_UPGRADE_READID_DELAY    1
+#define FT5X06_UPGRADE_EARSE_DELAY     2000
+
+/* upgrade config of FT6208 */
+#define FT6208_UPGRADE_AA_DELAY                60
+#define FT6208_UPGRADE_55_DELAY                10
+#define FT6208_UPGRADE_ID_1            0x79
+#define FT6208_UPGRADE_ID_2            0x05
+#define FT6208_UPGRADE_READID_DELAY    10
+#define FT6208_UPGRADE_EARSE_DELAY     2000
+
+#define FT_UPGRADE_INFO(x, y)  do { \
+               x->delay_55 = y##_UPGRADE_55_DELAY; \
+               x->delay_aa = y##_UPGRADE_AA_DELAY; \
+               x->upgrade_id_1 = y##_UPGRADE_ID_1; \
+               x->upgrade_id_2 = y##_UPGRADE_ID_2; \
+               x->delay_readid = y##_UPGRADE_READID_DELAY; \
+               x->delay_earse_flash = y##_UPGRADE_EARSE_DELAY; \
+               } while (0)
+
+#define FT_FW_MIN_SIZE         8
+#define FT_FW_MAX_SIZE         32768
+#define FT_FW_FILE_VER(x)      ((x)->data[(x)->size - 2])
+#define FT_FW_CHECK(x)         \
+       (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \
+       && (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \
+       && (((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)))
+
+#define FT_MAX_TRIES           5
+#define FT_RETRY_DLY           20
+
+#define FT_MAX_WR_BUF          10
+#define FT_MAX_RD_BUF          2
+#define FT_FW_PKT_LEN          128
+#define FT_FW_PKT_META_LEN     6
+#define FT_FW_PKT_DLY_MS       20
+#define FT_FW_LAST_PKT         0x6ffa
+#define FT_EARSE_DLY_MS                100
+
+#define FT_UPGRADE_LOOP                3
+#define FT_CAL_START           0x04
+#define FT_CAL_FIN             0x00
+#define FT_CAL_STORE           0x05
+#define FT_CAL_RETRY           100
+#define FT_REG_CAL             0x00
+#define FT_CAL_MASK            0x70
+
 struct ts_event {
-       u16 x[CFG_MAX_TOUCH_POINTS];    /*x coordinate */
-       u16 y[CFG_MAX_TOUCH_POINTS];    /*y coordinate */
+       u16 x[CFG_MAX_TOUCH_POINTS];    /* x coordinate */
+       u16 y[CFG_MAX_TOUCH_POINTS];    /* y coordinate */
        /* touch event: 0 -- down; 1-- contact; 2 -- contact */
        u8 touch_event[CFG_MAX_TOUCH_POINTS];
-       u8 finger_id[CFG_MAX_TOUCH_POINTS];     /*touch ID */
+       u8 finger_id[CFG_MAX_TOUCH_POINTS];     /* touch ID */
        u16 pressure;
        u8 touch_point;
 };
 
+struct upgrade_info {
+       u16 delay_aa;           /* delay of write FT_UPGRADE_AA */
+       u16 delay_55;           /* delay of write FT_UPGRADE_55 */
+       u8 upgrade_id_1;        /* upgrade id 1 */
+       u8 upgrade_id_2;        /* upgrade id 2 */
+       u16 delay_readid;       /* delay of read id */
+       u16 delay_earse_flash;  /* delay of earse flash */
+};
+
 struct ft5x06_ts_data {
        struct i2c_client *client;
        struct input_dev *input_dev;
@@ -93,6 +193,9 @@ struct ft5x06_ts_data {
        const struct ft5x06_ts_platform_data *pdata;
        struct regulator *vdd;
        struct regulator *vcc_i2c;
+       char fw_name[FT_FW_NAME_MAX_LEN];
+       bool loading_fw;
+       u8 family_id;
 #if defined(CONFIG_FB)
        struct notifier_block fb_notif;
 #elif defined(CONFIG_HAS_EARLYSUSPEND)
@@ -160,6 +263,21 @@ static int ft5x06_i2c_write(struct i2c_client *client, char *writebuf,
        return ret;
 }
 
+static int ft5x0x_write_reg(struct i2c_client *client, u8 addr, const u8 val)
+{
+       u8 buf[2] = {0};
+
+       buf[0] = addr;
+       buf[1] = val;
+
+       return ft5x06_i2c_write(client, buf, sizeof(buf));
+}
+
+static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val)
+{
+       return ft5x06_i2c_read(client, &addr, 1, val, 1);
+}
+
 static void ft5x06_report_value(struct ft5x06_ts_data *data)
 {
        struct ts_event *event = &data->event;
@@ -300,8 +418,8 @@ static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on)
        }
 
        if (regulator_count_voltages(data->vdd) > 0) {
-               rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
-                                          FT5X06_VTG_MAX_UV);
+               rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV,
+                                          FT_VTG_MAX_UV);
                if (rc) {
                        dev_err(&data->client->dev,
                                "Regulator set_vtg failed vdd rc=%d\n", rc);
@@ -318,8 +436,8 @@ static int ft5x06_power_init(struct ft5x06_ts_data *data, bool on)
        }
 
        if (regulator_count_voltages(data->vcc_i2c) > 0) {
-               rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
-                                          FT5X06_I2C_VTG_MAX_UV);
+               rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV,
+                                          FT_I2C_VTG_MAX_UV);
                if (rc) {
                        dev_err(&data->client->dev,
                        "Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
@@ -333,19 +451,19 @@ reg_vcc_i2c_put:
        regulator_put(data->vcc_i2c);
 reg_vdd_set_vtg:
        if (regulator_count_voltages(data->vdd) > 0)
-               regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+               regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
 reg_vdd_put:
        regulator_put(data->vdd);
        return rc;
 
 pwr_deinit:
        if (regulator_count_voltages(data->vdd) > 0)
-               regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+               regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
 
        regulator_put(data->vdd);
 
        if (regulator_count_voltages(data->vcc_i2c) > 0)
-               regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+               regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV);
 
        regulator_put(data->vcc_i2c);
        return 0;
@@ -360,8 +478,8 @@ static int ft5x06_ts_suspend(struct device *dev)
        disable_irq(data->client->irq);
 
        if (gpio_is_valid(data->pdata->reset_gpio)) {
-               txbuf[0] = FT5X06_REG_PMODE;
-               txbuf[1] = FT5X06_PMODE_HIBERNATE;
+               txbuf[0] = FT_REG_PMODE;
+               txbuf[1] = FT_PMODE_HIBERNATE;
                ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
        }
 
@@ -430,11 +548,345 @@ static const struct dev_pm_ops ft5x06_ts_pm_ops = {
 };
 #endif
 
+static int ft5x06_auto_cal(struct i2c_client *client)
+{
+       u8 temp = 0, i;
+
+       /* set to factory mode */
+       msleep(2 * FT_STARTUP_DLY);
+       ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+       msleep(FT_STARTUP_DLY);
+
+       /* start calibration */
+       ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START);
+       msleep(2 * FT_STARTUP_DLY);
+       for (i = 0; i < FT_CAL_RETRY; i++) {
+               ft5x0x_read_reg(client, FT_REG_CAL, &temp);
+               /* return to normal mode, calibration finish */
+               if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN)
+                       break;
+       }
+
+       /* calibration OK */
+       msleep(2 * FT_STARTUP_DLY);
+       ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+       msleep(FT_STARTUP_DLY);
+
+       /* store calibration data */
+       ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE);
+       msleep(2 * FT_STARTUP_DLY);
+
+       /* set to normal mode */
+       ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE);
+       msleep(2 * FT_STARTUP_DLY);
+
+       return 0;
+}
+
+static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info)
+{
+       switch (family_id) {
+       case FT5306I_ID:
+               FT_UPGRADE_INFO(info, FT5X06);
+               break;
+       case FT5316_ID:
+               FT_UPGRADE_INFO(info, FT5316);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ft5x06_fw_upgrade_start(struct i2c_client *client,
+                       const u8 *data, u32 data_len)
+{
+       struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client);
+       struct upgrade_info info;
+       u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0};
+       u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN];
+       int rc, i, j, temp;
+       u32 pkt_num, pkt_len;
+       u8 fw_ecc;
+
+       rc = ft5x06_get_upgrade_info(ts_data->family_id, &info);
+       if (rc < 0) {
+               dev_err(&client->dev, "Cannot get upgrade information!\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < FT_UPGRADE_LOOP; i++) {
+               /* reset - write 0xaa and 0x55 to register 0xfc */
+               ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_AA);
+               msleep(info.delay_aa);
+
+               ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55);
+               msleep(info.delay_55);
+
+               /* Enter upgrade mode */
+               w_buf[0] = FT_UPGRADE_55;
+               w_buf[1] = FT_UPGRADE_AA;
+               do {
+                       i++;
+                       rc = ft5x06_i2c_write(client, w_buf, 2);
+                       msleep(FT_RETRY_DLY);
+               } while (rc <= 0 && i < FT_MAX_TRIES);
+
+               /* check READ_ID */
+               msleep(info.delay_readid);
+               w_buf[0] = FT_READ_ID_REG;
+               w_buf[1] = 0x00;
+               w_buf[2] = 0x00;
+               w_buf[3] = 0x00;
+
+               ft5x06_i2c_read(client, w_buf, 4, r_buf, 2);
+
+               if (r_buf[0] != info.upgrade_id_1
+                       || r_buf[1] != info.upgrade_id_2) {
+                       dev_err(&client->dev, "Upgrade ID mismatch(%d)\n", i);
+               } else
+                       break;
+       }
+
+       if (i >= FT_UPGRADE_LOOP) {
+               dev_err(&client->dev, "Abort upgrade\n");
+               return -EIO;
+       }
+
+       /* erase app and panel paramenter area */
+       w_buf[0] = FT_ERASE_APP_REG;
+       ft5x06_i2c_write(client, w_buf, 1);
+       msleep(info.delay_earse_flash);
+
+       w_buf[0] = FT_ERASE_PANEL_REG;
+       ft5x06_i2c_write(client, w_buf, 1);
+       msleep(FT_EARSE_DLY_MS);
+
+       /* program firmware */
+       data_len = data_len - 8;
+       pkt_num = (data_len) / FT_FW_PKT_LEN;
+       pkt_len = FT_FW_PKT_LEN;
+       pkt_buf[0] = FT_FW_START_REG;
+       pkt_buf[1] = 0x00;
+       fw_ecc = 0;
+
+       for (i = 0; i < pkt_num; i++) {
+               temp = i * FT_FW_PKT_LEN;
+               pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+               pkt_buf[3] = (u8) temp;
+               pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT);
+               pkt_buf[5] = (u8) pkt_len;
+
+               for (j = 0; j < FT_FW_PKT_LEN; j++) {
+                       pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j];
+                       fw_ecc ^= pkt_buf[6 + j];
+               }
+
+               ft5x06_i2c_write(client, pkt_buf,
+                               FT_FW_PKT_LEN + FT_FW_PKT_META_LEN);
+               msleep(FT_FW_PKT_DLY_MS);
+       }
+
+       /* send remaining bytes */
+       if ((data_len) % FT_FW_PKT_LEN > 0) {
+               temp = pkt_num * FT_FW_PKT_LEN;
+               pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+               pkt_buf[3] = (u8) temp;
+               temp = (data_len) % FT_FW_PKT_LEN;
+               pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT);
+               pkt_buf[5] = (u8) temp;
+
+               for (i = 0; i < temp; i++) {
+                       pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i];
+                       fw_ecc ^= pkt_buf[6 + i];
+               }
+
+               ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN);
+               msleep(FT_FW_PKT_DLY_MS);
+       }
+
+       /* send the finishing packet */
+       for (i = 0; i < 6; i++) {
+               temp = FT_FW_LAST_PKT + i;
+               pkt_buf[2] = (u8) (temp >> 8);
+               pkt_buf[3] = (u8) temp;
+               temp = 1;
+               pkt_buf[4] = (u8) (temp >> 8);
+               pkt_buf[5] = (u8) temp;
+               pkt_buf[6] = data[data_len + i];
+               fw_ecc ^= pkt_buf[6];
+               ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN);
+               msleep(FT_FW_PKT_DLY_MS);
+       }
+
+       /* verify checksum */
+       w_buf[0] = FT_REG_ECC;
+       ft5x06_i2c_read(client, w_buf, 1, r_buf, 1);
+       if (r_buf[0] != fw_ecc) {
+               dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n",
+                                       r_buf[0], fw_ecc);
+               return -EIO;
+       }
+
+       /* reset */
+       w_buf[0] = FT_REG_RESET_FW;
+       ft5x06_i2c_write(client, w_buf, 1);
+       msleep(FT_STARTUP_DLY);
+
+       return 0;
+}
+
+static int ft5x06_fw_upgrade(struct device *dev, bool force)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+       const struct firmware *fw = NULL;
+       int rc;
+       u8 val = 0;
+
+       rc = request_firmware(&fw, data->fw_name, dev);
+       if (rc < 0) {
+               dev_err(dev, "Request firmware failed - %s (%d)\n",
+                                               data->fw_name, rc);
+               return rc;
+       }
+
+       if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) {
+               dev_err(dev, "Invalid firmware size (%d)\n", fw->size);
+               rc = -EIO;
+               goto rel_fw;
+       }
+
+       /* check firmware version */
+       rc = ft5x0x_read_reg(data->client, FT_REG_FW_VER, &val);
+       if (rc < 0) {
+               dev_err(dev, "Get firmware version failed\n");
+               goto rel_fw;
+       }
+
+       if (val == FT_FW_FILE_VER(fw) && !force) {
+               dev_err(dev, "No need to update (0x%x)\n", val);
+               rc = -EFAULT;
+               goto rel_fw;
+       }
+
+       dev_info(dev, "upgrade to fw ver 0x%x from 0x%x\n",
+                                       FT_FW_FILE_VER(fw), val);
+
+       /* start firmware upgrade */
+       if (FT_FW_CHECK(fw)) {
+               rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size);
+               if (rc < 0)
+                       dev_err(dev, "update failed (%d)\n", rc);
+               else
+                       ft5x06_auto_cal(data->client);
+       } else {
+               dev_err(dev, "FW format error\n");
+               rc = -EIO;
+       }
+
+rel_fw:
+       release_firmware(fw);
+       return rc;
+}
+
+static ssize_t ft5x06_update_fw_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+       return snprintf(buf, 2, "%d\n", data->loading_fw);
+}
+
+static ssize_t ft5x06_update_fw_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+       unsigned long val;
+       int rc;
+
+       if (size > 2)
+               return -EINVAL;
+
+       rc = kstrtoul(buf, 10, &val);
+       if (rc != 0)
+               return rc;
+
+       mutex_lock(&data->input_dev->mutex);
+       if (!data->loading_fw  && val) {
+               data->loading_fw = true;
+               ft5x06_fw_upgrade(dev, false);
+               data->loading_fw = false;
+       }
+       mutex_unlock(&data->input_dev->mutex);
+
+       return size;
+}
+
+static DEVICE_ATTR(update_fw, 0664, ft5x06_update_fw_show,
+                               ft5x06_update_fw_store);
+
+static ssize_t ft5x06_force_update_fw_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+       unsigned long val;
+       int rc;
+
+       if (size > 2)
+               return -EINVAL;
+
+       rc = kstrtoul(buf, 10, &val);
+       if (rc != 0)
+               return rc;
+
+       mutex_lock(&data->input_dev->mutex);
+       if (!data->loading_fw  && val) {
+               data->loading_fw = true;
+               ft5x06_fw_upgrade(dev, true);
+               data->loading_fw = false;
+       }
+       mutex_unlock(&data->input_dev->mutex);
+
+       return size;
+}
+
+static DEVICE_ATTR(force_update_fw, 0664, ft5x06_update_fw_show,
+                               ft5x06_force_update_fw_store);
+
+static ssize_t ft5x06_fw_name_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+       return snprintf(buf, FT_FW_NAME_MAX_LEN - 1, "%s\n", data->fw_name);
+}
+
+static ssize_t ft5x06_fw_name_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+       if (size > FT_FW_NAME_MAX_LEN - 1)
+               return -EINVAL;
+
+       strlcpy(data->fw_name, buf, size);
+       if (data->fw_name[size-1] == '\n')
+               data->fw_name[size-1] = 0;
+
+       return size;
+}
+
+static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store);
+
 #ifdef CONFIG_OF
 static int ft5x06_get_dt_coords(struct device *dev, char *name,
                                struct ft5x06_ts_platform_data *pdata)
 {
-       u32 coords[FT5X06_COORDS_ARR_SIZE];
+       u32 coords[FT_COORDS_ARR_SIZE];
        struct property *prop;
        struct device_node *np = dev->of_node;
        int coords_size, rc;
@@ -446,7 +898,7 @@ static int ft5x06_get_dt_coords(struct device *dev, char *name,
                return -ENODATA;
 
        coords_size = prop->length / sizeof(u32);
-       if (coords_size != FT5X06_COORDS_ARR_SIZE) {
+       if (coords_size != FT_COORDS_ARR_SIZE) {
                dev_err(dev, "invalid %s\n", name);
                return -EINVAL;
        }
@@ -676,37 +1128,43 @@ static int ft5x06_ts_probe(struct i2c_client *client,
        msleep(FT_STARTUP_DLY);
 
        /* check the controller id */
-       reg_addr = FT5X06_REG_ID;
+       reg_addr = FT_REG_ID;
        err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
        if (err < 0) {
                dev_err(&client->dev, "version read failed");
                return err;
        }
 
+       dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
+
        if (pdata->family_id != reg_value) {
                dev_err(&client->dev, "%s:Unsupported controller\n", __func__);
                goto free_reset_gpio;
        }
 
-       /*get some register information */
-       reg_addr = FT5X06_REG_FW_VER;
+       data->family_id = reg_value;
+
+       /* get some register information */
+       reg_addr = FT_REG_FW_VER;
        err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-       if (err)
+       if (err < 0)
                dev_err(&client->dev, "version read failed");
 
-       dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
+       dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value);
 
-       reg_addr = FT5X06_REG_POINT_RATE;
+       reg_addr = FT_REG_POINT_RATE;
        ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-       if (err)
+       if (err < 0)
                dev_err(&client->dev, "report rate read failed");
-       dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
 
-       reg_addr = FT5X06_REG_THGROUP;
+       dev_dbg(&client->dev, "report rate = %dHz\n", reg_value * 10);
+
+       reg_addr = FT_REG_THGROUP;
        err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-       if (err)
+       if (err < 0)
                dev_err(&client->dev, "threshold read failed");
-       dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+
+       dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
 
        err = request_threaded_irq(client->irq, NULL,
                                   ft5x06_ts_interrupt, pdata->irqflags,
@@ -715,6 +1173,26 @@ static int ft5x06_ts_probe(struct i2c_client *client,
                dev_err(&client->dev, "request irq failed\n");
                goto free_reset_gpio;
        }
+
+       err = device_create_file(&client->dev, &dev_attr_fw_name);
+       if (err) {
+               dev_err(&client->dev, "sys file creation failed\n");
+               goto irq_free;
+       }
+
+       err = device_create_file(&client->dev, &dev_attr_update_fw);
+       if (err) {
+               dev_err(&client->dev, "sys file creation failed\n");
+               goto free_fw_name_sys;
+       }
+
+       err = device_create_file(&client->dev, &dev_attr_force_update_fw);
+       if (err) {
+               dev_err(&client->dev, "sys file creation failed\n");
+               goto free_update_fw_sys;
+       }
+
+
 #if defined(CONFIG_FB)
        data->fb_notif.notifier_call = fb_notifier_callback;
 
@@ -725,7 +1203,7 @@ static int ft5x06_ts_probe(struct i2c_client *client,
                        err);
 #elif defined(CONFIG_HAS_EARLYSUSPEND)
        data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
-           FT5X06_SUSPEND_LEVEL;
+                                                   FT_SUSPEND_LEVEL;
        data->early_suspend.suspend = ft5x06_ts_early_suspend;
        data->early_suspend.resume = ft5x06_ts_late_resume;
        register_early_suspend(&data->early_suspend);
@@ -733,12 +1211,18 @@ static int ft5x06_ts_probe(struct i2c_client *client,
 
        return 0;
 
+free_update_fw_sys:
+       device_remove_file(&client->dev, &dev_attr_update_fw);
+free_fw_name_sys:
+       device_remove_file(&client->dev, &dev_attr_fw_name);
+irq_free:
+       free_irq(client->irq, data);
 free_reset_gpio:
        if (gpio_is_valid(pdata->reset_gpio))
                gpio_free(pdata->reset_gpio);
 free_irq_gpio:
        if (gpio_is_valid(pdata->irq_gpio))
-               gpio_free(pdata->reset_gpio);
+               gpio_free(pdata->irq_gpio);
 pwr_off:
        if (pdata->power_on)
                pdata->power_on(false);
@@ -763,6 +1247,10 @@ static int ft5x06_ts_remove(struct i2c_client *client)
 {
        struct ft5x06_ts_data *data = i2c_get_clientdata(client);
 
+       device_remove_file(&client->dev, &dev_attr_force_update_fw);
+       device_remove_file(&client->dev, &dev_attr_update_fw);
+       device_remove_file(&client->dev, &dev_attr_fw_name);
+
 #if defined(CONFIG_FB)
        if (fb_unregister_client(&data->fb_notif))
                dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
@@ -775,7 +1263,7 @@ static int ft5x06_ts_remove(struct i2c_client *client)
                gpio_free(data->pdata->reset_gpio);
 
        if (gpio_is_valid(data->pdata->irq_gpio))
-               gpio_free(data->pdata->reset_gpio);
+               gpio_free(data->pdata->irq_gpio);
 
        if (data->pdata->power_on)
                data->pdata->power_on(false);