summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/ist30xx/ist30xx_update.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/touchscreen/ist30xx/ist30xx_update.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/input/touchscreen/ist30xx/ist30xx_update.c')
-rw-r--r--drivers/input/touchscreen/ist30xx/ist30xx_update.c1449
1 files changed, 1449 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ist30xx/ist30xx_update.c b/drivers/input/touchscreen/ist30xx/ist30xx_update.c
new file mode 100644
index 00000000..17379061
--- /dev/null
+++ b/drivers/input/touchscreen/ist30xx/ist30xx_update.c
@@ -0,0 +1,1449 @@
+/*
+ * Copyright (C) 2010,Imagis Technology Co. Ltd. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+
+/* Including the definition of S_IRWXUGO */
+// Can use S_IRUGO for better security. Verify the same.
+#include <linux/stat.h>
+
+#include "ist30xx.h"
+#include "ist30xx_update.h"
+#include "ist30xx_tracking.h"
+
+#if IST30XX_DEBUG
+#include "ist30xx_misc.h"
+#endif
+
+#if IST30XX_INTERNAL_BIN
+#if defined(CONFIG_MACH_KANAS)
+#include "./firmware/IST3038_FW_KANAS.h"
+#elif defined(CONFIG_MACH_TSHARKWSAMSUNG)
+#include "./firmware/ist3038_fw_tshark_ver0007.h"
+#elif defined(CONFIG_MACH_CORE3)
+#include "./firmware/ist3038_fw_kanas_ver0006_20140401.h"
+#else
+#include "./firmware/ist3038_fw_kanas_ver0006_20140401.h"
+#endif
+# if IST30XX_MULTIPLE_TSP
+# include "./firmware/ist3038_fw_kanas_ver0001_20140225.h" //Not proper file
+# include "./firmware/ist3038_fw_kanas_ver0001_20140225.h" //Not proper file
+# endif
+#endif // IST30XX_INTERNAL_BIN
+
+struct ist30xx_tags *ts_tags;
+
+u32 ist30xx_fw_ver = 0;
+u32 ist30xx_param_ver = 0;
+u32 ist30xx_sub_ver = 0;
+
+extern struct ist30xx_data *ts_data;
+
+extern void ist30xx_disable_irq(struct ist30xx_data *data);
+extern void ist30xx_enable_irq(struct ist30xx_data *data);
+
+
+int ist30xx_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int msg_num, u8 *cmd_buf)
+{
+ int ret = 0;
+ int idx = msg_num - 1;
+ int size = msgs[idx].len;
+ u8 *msg_buf = NULL;
+ u8 *pbuf = NULL;
+ int max_size = 0;
+
+ if (msg_num == WRITE_CMD_MSG_LEN)
+ max_size = I2C_MAX_WRITE_SIZE;
+ else if (msg_num == READ_CMD_MSG_LEN)
+ max_size = I2C_MAX_READ_SIZE;
+
+ if (unlikely(max_size == 0)) {
+ tsp_err("%s() : transaction size(%d)\n", __func__, max_size);
+ return -EINVAL;
+ }
+
+ if (msg_num == WRITE_CMD_MSG_LEN) {
+ msg_buf = kmalloc(max_size + IST30XX_ADDR_LEN, GFP_KERNEL);
+ if (!msg_buf)
+ return -ENOMEM;
+ memcpy(msg_buf, cmd_buf, IST30XX_ADDR_LEN);
+ pbuf = msgs[idx].buf;
+ }
+
+ while (size > 0) {
+ int trans_size = (size >= max_size ? max_size : size);
+
+ msgs[idx].len = trans_size;
+ if (msg_num == WRITE_CMD_MSG_LEN) {
+ memcpy(&msg_buf[IST30XX_ADDR_LEN], pbuf, trans_size);
+ msgs[idx].buf = msg_buf;
+ msgs[idx].len += IST30XX_ADDR_LEN;
+ }
+ ret = i2c_transfer(adap, msgs, msg_num);
+ if (unlikely(ret != msg_num)) {
+ tsp_err("%s() : i2c_transfer failed(%d), num=%d\n",
+ __func__, ret, msg_num);
+ break;
+ }
+
+ if (msg_num == WRITE_CMD_MSG_LEN)
+ pbuf += trans_size;
+ else
+ msgs[idx].buf += trans_size;
+
+ size -= trans_size;
+ }
+
+ if (msg_num == WRITE_CMD_MSG_LEN)
+ kfree(msg_buf);
+
+ return ret;
+}
+
+int ist30xx_read_buf(struct i2c_client *client, u32 cmd, u32 *buf, u16 len)
+{
+ int ret, i;
+ u32 le_reg = cpu_to_be32(cmd);
+
+ struct i2c_msg msg[READ_CMD_MSG_LEN] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = IST30XX_ADDR_LEN,
+ .buf = (u8 *)&le_reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len * IST30XX_DATA_LEN,
+ .buf = (u8 *)buf,
+ },
+ };
+
+ ret = ist30xx_i2c_transfer(client->adapter, msg, READ_CMD_MSG_LEN, NULL);
+ if (unlikely(ret != READ_CMD_MSG_LEN))
+ return -EIO;
+
+ for (i = 0; i < len; i++)
+ buf[i] = cpu_to_be32(buf[i]);
+
+ return 0;
+}
+
+int ist30xx_write_buf(struct i2c_client *client, u32 cmd, u32 *buf, u16 len)
+{
+ int ret;
+ struct i2c_msg msg;
+ u8 cmd_buf[IST30XX_ADDR_LEN];
+ u8 msg_buf[IST30XX_DATA_LEN * (len + 1)];
+
+ put_unaligned_be32(cmd, cmd_buf);
+
+ if (likely(len > 0)) {
+ int i;
+ for (i = 0; i < len; i++)
+ put_unaligned_be32(buf[i], msg_buf + (i * IST30XX_DATA_LEN));
+ } else {
+ /* then add dummy data(4byte) */
+ put_unaligned_be32(0, msg_buf);
+ len = 1;
+ }
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = IST30XX_DATA_LEN * len;
+ msg.buf = msg_buf;
+
+ ret = ist30xx_i2c_transfer(client->adapter, &msg, WRITE_CMD_MSG_LEN, cmd_buf);
+ if (unlikely(ret != WRITE_CMD_MSG_LEN))
+ return -EIO;
+
+ return 0;
+}
+
+int ist30xxb_burst_read(struct i2c_client *client, u32 addr,
+ u32 *buf32, int len)
+{
+ int i;
+ int max_len = I2C_MAX_READ_SIZE / IST30XX_DATA_LEN;
+
+ addr |= IST30XXB_BURST_ACCESS;
+
+ for (i = 0; i < len; i += max_len) {
+ int ret = 0;
+ if (len < max_len) max_len = len;
+
+ ret = ist30xx_read_buf(client, addr, buf32, max_len);
+ if (unlikely(ret)) {
+ tsp_err("Burst fail, addr: %x\n", __func__, addr);
+ return ret;
+ }
+
+ addr += max_len * IST30XX_DATA_LEN;
+ buf32 += max_len;
+ }
+
+ return 0;
+}
+
+#define IST30XXB_ISP_READ (1)
+#define IST30XXB_ISP_WRITE (2)
+#define IST30XXB_ISP_ERASE_ALL (3)
+#define IST30XXB_ISP_ERASE_PAGE (4)
+
+int ist30xxb_isp_reset(void)
+{
+ int ret = 0;
+
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_EEPPWRCTRL, 0xB200);
+ if (unlikely(ret)) return ret;
+
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_EEPPWRCTRL, 0x9200);
+ if (unlikely(ret)) return ret;
+
+ msleep(1);
+
+ return ret;
+}
+
+int ist30xxb_isp_enable(bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_EEPISPEN, 0x3);
+ if (unlikely(ret)) return ret;
+
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_LDOOSC, 0xF4C8);
+ if (unlikely(ret)) return ret;
+
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_CLKDIV, 0x3);
+ if (unlikely(ret)) return ret;
+
+ msleep(5);
+ } else {
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_EEPISPEN, 0x2);
+ if (unlikely(ret)) return ret;
+
+ msleep(1);
+ }
+
+ return 0;
+}
+
+int ist30xxb_isp_mode(int mode)
+{
+ int ret = 0;
+ u32 val = 0;
+
+ switch (mode) {
+ case IST30XXB_ISP_READ:
+ val = 0x1C0;
+ break;
+ case IST30XXB_ISP_WRITE:
+ val = 0x1A8;
+ break;
+ case IST30XXB_ISP_ERASE_ALL:
+ val = 0x1A7;
+ break;
+ case IST30XXB_ISP_ERASE_PAGE:
+ val = 0x1A3;
+ break;
+ default:
+ tsp_err("ISP fail, unknown mode\n");
+ return -EINVAL;
+ }
+
+ ret = ist30xx_write_cmd(ts_data->client, IST30XXB_REG_EEPMODE, val);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPMODE\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int ist30xxb_isp_erase(struct i2c_client *client, int mode, u32 addr)
+{
+ int ret = 0;
+ u32 val = 0x1A0;
+ u8 buf[EEPROM_PAGE_SIZE] = { 0, };
+
+ ret = ist30xxb_isp_mode(mode);
+ if (unlikely(ret)) return ret;
+
+ ret = ist30xx_write_cmd(client, IST30XXB_REG_EEPADDR, addr);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPADDR\n");
+ return ret;
+ }
+
+ val = (EEPROM_PAGE_SIZE / IST30XX_DATA_LEN);
+ ret = ist30xx_write_buf(client, IST30XXB_REG_EEPWDAT, (u32 *)buf, val);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPWDAT\n");
+ return ret;
+ }
+
+ msleep(30);
+
+ ist30xxb_isp_reset();
+
+ return 0;
+}
+
+int ist30xxb_isp_write(struct i2c_client *client, u32 addr,
+ const u32 *buf32, int len)
+{
+ int ret = 0;
+
+ ret = ist30xx_write_cmd(client, IST30XXB_REG_EEPADDR, addr);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPADDR\n");
+ return ret;
+ }
+
+ ret = ist30xx_write_buf(client, IST30XXB_REG_EEPWDAT, (u32 *)buf32, len);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPWDAT\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int ist30xxb_isp_read(struct i2c_client *client, u32 addr,
+ u32 *buf32, int len)
+{
+ int i;
+ int max_len = I2C_MAX_READ_SIZE / IST30XX_DATA_LEN;
+
+ for (i = 0; i < len; i += max_len) {
+ int ret = 0;
+ if (len < max_len) max_len = len;
+
+ /* IST30xxB ISP read mode */
+ ret = ist30xxb_isp_mode(IST30XXB_ISP_READ);
+ if (unlikely(ret)) return ret;
+
+ ret = ist30xx_write_cmd(client, IST30XXB_REG_EEPADDR, addr);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPADDR\n");
+ return ret;
+ }
+
+ ret = ist30xx_read_buf(client, IST30XXB_REG_EEPRDAT, buf32, max_len);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_EEPWDAT\n");
+ return ret;
+ }
+
+ addr += max_len * IST30XX_DATA_LEN;
+ buf32 += max_len;
+ }
+
+ return 0;
+}
+
+int ist30xxb_cmd_read_chksum(struct i2c_client *client,
+ u32 start_addr, u32 end_addr, u32 *chksum)
+{
+ int ret = 0;
+ u32 val = (1 << 31); // Chkecksum enable
+
+ val |= start_addr;
+ val |= (end_addr / IST30XX_DATA_LEN - 1) << 16;
+
+ ret = ist30xx_write_cmd(client, IST30XXB_REG_CHKSMOD, val);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_CHKSMOD (%x)\n", val);
+ return ret;
+ }
+
+ msleep(10);
+
+ ret = ist30xx_read_cmd(client, IST30XXB_REG_CHKSDAT, chksum);
+ if (unlikely(ret)) {
+ tsp_err("ISP fail, IST30XXB_REG_CHKSDAT (%x)\n", chksum);
+ return ret;
+ }
+
+ tsp_verb("chksum: %x (%x~%x)\n", *chksum, start_addr, end_addr);
+
+ return 0;
+}
+
+int ist30xxb_read_chksum(struct i2c_client *client, u32 *chksum)
+{
+ int ret = 0;
+ u32 start_addr, end_addr;
+ u32 chksum1 = 0, chksum2 = 0;
+
+ start_addr = 0;
+ end_addr = ts_data->tags.flag_addr;
+ ret = ist30xxb_cmd_read_chksum(client, start_addr, end_addr, &chksum1);
+ if (unlikely(ret)) return ret;
+
+ start_addr = ts_data->tags.flag_addr + ts_data->tags.flag_size;
+ end_addr = ts_data->tags.sensor2_addr + ts_data->tags.sensor2_size;
+ ret = ist30xxb_cmd_read_chksum(client, start_addr, end_addr, &chksum2);
+ if (unlikely(ret)) return ret;
+
+ *chksum = chksum1 | (chksum2 << 16);
+
+ tsp_info("Chksum: %x(%x~%x, %x~%x)\n", *chksum, 0, ts_data->tags.flag_addr,
+ start_addr, end_addr);
+
+ return 0;
+}
+
+
+int ist30xxb_isp_fw_read(struct i2c_client *client, u32 *buf32)
+{
+ int ret = 0;
+ int i;
+
+ u16 addr = EEPROM_BASE_ADDR;
+ int len = EEPROM_PAGE_SIZE / IST30XX_DATA_LEN;
+
+ ist30xx_reset();
+
+ /* IST30xxB ISP enable */
+ ret = ist30xxb_isp_enable(true);
+ if (unlikely(ret)) return ret;
+
+ for (i = 0; i < IST30XX_EEPROM_SIZE; i += EEPROM_PAGE_SIZE) {
+ ret = ist30xxb_isp_read(client, addr, buf32, len);
+ if (unlikely(ret)) goto isp_fw_read_end;
+
+ addr += EEPROM_PAGE_SIZE;
+ buf32 += len;
+ }
+
+isp_fw_read_end:
+ /* IST30xxB ISP disable */
+ ist30xxb_isp_enable(false);
+ return ret;
+}
+
+int ist30xxb_isp_fw_update(struct i2c_client *client, const u8 *buf, u32 *chksum)
+{
+ int ret = 0;
+ int i;
+ u32 page_cnt = IST30XX_EEPROM_SIZE / EEPROM_PAGE_SIZE;
+ u16 addr = EEPROM_BASE_ADDR;
+ int len = EEPROM_PAGE_SIZE / IST30XX_DATA_LEN;
+
+ ist30xx_tracking(TRACK_CMD_FWUPDATE);
+
+ /* IST30xxB ISP enable */
+ ret = ist30xxb_isp_enable(true);
+ if (unlikely(ret)) goto isp_fw_update_end;
+
+ ret = ist30xxb_isp_erase(client, IST30XXB_ISP_ERASE_ALL, 0);
+ if (unlikely(ret)) goto isp_fw_update_end;
+
+ for (i = 0; i < page_cnt; i++) {
+ /* IST30xxB ISP write mode */
+ ret = ist30xxb_isp_mode(IST30XXB_ISP_WRITE);
+ if (unlikely(ret)) goto isp_fw_update_end;
+
+ ret = ist30xxb_isp_write(client, addr, (u32 *)buf, len);
+ if (unlikely(ret)) goto isp_fw_update_end;
+
+ addr += EEPROM_PAGE_SIZE;
+ buf += EEPROM_PAGE_SIZE;
+
+ msleep(5);
+
+ ist30xxb_isp_reset();
+ }
+
+isp_fw_update_end:
+ /* IST30xxB ISP disable */
+ ist30xxb_isp_enable(false);
+ return ret;
+}
+
+int ist30xxb_isp_bootloader(struct i2c_client *client, const u8 *buf)
+{
+ int ret = 0;
+ int i;
+ u16 addr = EEPROM_BASE_ADDR;
+ u32 page_cnt = (ts_data->fw.index) / EEPROM_PAGE_SIZE;
+ int buf_cnt = EEPROM_PAGE_SIZE / IST30XX_DATA_LEN;
+
+ tsp_info("*** Bootloader update (0x%x~0x%x) ***\n",
+ EEPROM_BASE_ADDR, ts_data->fw.index);
+
+ /* IST30xxB ISP enable */
+ ret = ist30xxb_isp_enable(true);
+ if (unlikely(ret)) goto isp_bootldr_end;
+
+ ret = ist30xxb_isp_erase(client, IST30XXB_ISP_ERASE_ALL, 0);
+ if (unlikely(ret)) goto isp_bootldr_end;
+
+ for (i = 0; i < page_cnt; i++) {
+ /* IST30xxB ISP write mode */
+ ret = ist30xxb_isp_mode(IST30XXB_ISP_WRITE);
+ if (unlikely(ret)) goto isp_bootldr_end;
+
+ ret = ist30xxb_isp_write(client, addr, (u32 *)buf, buf_cnt);
+ if (unlikely(ret)) goto isp_bootldr_end;
+
+ addr += EEPROM_PAGE_SIZE;
+ buf += EEPROM_PAGE_SIZE;
+
+ msleep(5);
+
+ ist30xxb_isp_reset();
+ }
+
+isp_bootldr_end:
+ /* IST30xxB ISP disable */
+ ist30xxb_isp_enable(false);
+ return ret;
+}
+
+
+int ist30xx_fw_write(struct i2c_client *client, const u8 *buf)
+{
+ u32 *buf32 = (u32 *)(buf + ts_data->fw.index);
+ u32 size = ts_data->fw.size;
+
+ if (unlikely(size == 0 || size > IST30XX_EEPROM_SIZE))
+ return -ENOEXEC;
+
+ while (size > 0) {
+ int len = (size >= EEPROM_PAGE_SIZE ? EEPROM_PAGE_SIZE : size);
+
+ int ret = ist30xx_write_buf(client, CMD_ENTER_FW_UPDATE, buf32, (len >> 2));
+ if (unlikely(ret))
+ return ret;
+
+ buf32 += (len >> 2);
+ size -= len;
+
+ msleep(5);
+ }
+ return 0;
+}
+
+
+u32 ist30xx_parse_ver(int flag, const u8 *buf)
+{
+ u32 ver = 0;
+ u32 *buf32 = (u32 *)buf;
+
+ if (flag == FLAG_FW)
+ ver = (u32)buf32[(ts_data->tags.flag_addr + 4) >> 2];
+ else if (flag == FLAG_SUB)
+ ver = (u32)buf32[(ts_data->tags.flag_addr + 8) >> 2];
+ else if (flag == FLAG_PARAM)
+ ver = (u32)(buf32[(ts_data->tags.cfg_addr + 4) >> 2] & 0xFFFF);
+ else
+ tsp_warn("Parsing ver's flag is not corrent!\n");
+
+ return ver;
+}
+
+
+int calib_ms_delay = WAIT_CALIB_CNT;
+int ist30xx_calib_wait(void)
+{
+ int cnt = calib_ms_delay;
+
+ ts_data->status.calib_msg = 0;
+ while (cnt-- > 0) {
+ msleep(100);
+
+ if (ts_data->status.calib_msg) {
+ tsp_info("Calibration status : %d, Max raw gap : %d - (%08x)\n",
+ CALIB_TO_STATUS(ts_data->status.calib_msg),
+ CALIB_TO_GAP(ts_data->status.calib_msg),
+ ts_data->status.calib_msg);
+
+ if (CALIB_TO_OS_VALUE(ts_data->status.calib_msg) == 0xFFFF)
+ return 1;
+ else if (CALIB_TO_STATUS(ts_data->status.calib_msg) == 0)
+ return 0; // Calibrate success
+ else
+ return -EAGAIN;
+ }
+ }
+ tsp_warn("Calibration time out\n");
+
+ return -EPERM;
+}
+
+int ist30xx_calibrate(int wait_cnt)
+{
+ int ret = -ENOEXEC;
+
+ tsp_info("*** Calibrate %ds ***\n", calib_ms_delay / 10);
+
+ ts_data->status.update = 1;
+ while (wait_cnt--) {
+ ist30xx_disable_irq(ts_data);
+ ret = ist30xx_cmd_run_device(ts_data->client, true);
+ if (unlikely(ret)) continue;
+
+ ret = ist30xx_cmd_calibrate(ts_data->client);
+ if (unlikely(ret)) continue;
+
+ ist30xx_enable_irq(ts_data);
+ ret = ist30xx_calib_wait();
+ if (likely(ret == 0)) break;
+ }
+
+ ist30xx_disable_irq(ts_data);
+ ist30xx_cmd_run_device(ts_data->client, true);
+
+ ts_data->status.update = 2;
+
+ ist30xx_enable_irq(ts_data);
+
+ return ret;
+}
+
+
+int ist30xx_parse_tags(struct ist30xx_data *data, const u8 *buf, const u32 size)
+{
+ int ret = -EPERM;
+
+ ts_tags = (struct ist30xx_tags *)(&buf[size - sizeof(struct ist30xx_tags)]);
+
+ if (!strncmp(ts_tags->magic1, IST30XX_TAG_MAGIC, sizeof(ts_tags->magic1))
+ && !strncmp(ts_tags->magic2, IST30XX_TAG_MAGIC, sizeof(ts_tags->magic2))) {
+ data->fw.index = ts_tags->fw_addr;
+ data->fw.size = ts_tags->flag_addr - ts_tags->fw_addr +
+ ts_tags->flag_size;
+ data->fw.chksum = ts_tags->chksum;
+ data->tags = *ts_tags;
+
+ ret = 0;
+ }
+
+ tsp_verb("Tagts magic1: %s, magic2: %s\n",
+ ts_tags->magic1, ts_tags->magic2);
+ tsp_verb(" fw: %x(%x)\n", ts_tags->fw_addr, ts_tags->fw_size);
+ tsp_verb(" flag: %x(%x)\n", ts_tags->flag_addr, ts_tags->flag_size);
+ tsp_verb(" cfg: %x(%x)\n", ts_tags->cfg_addr, ts_tags->cfg_size);
+ tsp_verb(" sensor1: %x(%x)\n", ts_tags->sensor1_addr, ts_tags->sensor1_size);
+ tsp_verb(" sensor2: %x(%x)\n", ts_tags->sensor2_addr, ts_tags->sensor2_size);
+ tsp_verb(" sensor3: %x(%x)\n", ts_tags->sensor3_addr, ts_tags->sensor3_size);
+ tsp_verb(" chksum: %x\n", ts_tags->chksum);
+
+ return ret;
+}
+
+void ist30xx_get_update_info(struct ist30xx_data *data, const u8 *buf, const u32 size)
+{
+ int ret;
+
+ ret = ist30xx_parse_tags(data, buf, size);
+ if (unlikely(ret != TAGS_PARSE_OK))
+ tsp_warn("Cannot find tags of F/W, make a tags by 'tagts.exe'\n");
+}
+
+#if (IST30XX_DEBUG) && (IST30XX_INTERNAL_BIN)
+extern TSP_INFO ist30xx_tsp_info;
+extern TKEY_INFO ist30xx_tkey_info;
+int ist30xx_get_tkey_info(struct ist30xx_data *data)
+{
+ int ret = 0;
+ TSP_INFO *tsp = &ist30xx_tsp_info;
+ TKEY_INFO *tkey = &ist30xx_tkey_info;
+ u8 *cfg_buf;
+
+ ist30xx_get_update_info(data, data->fw.buf, data->fw.buf_size);
+ cfg_buf = (u8 *)&data->fw.buf[data->tags.cfg_addr];
+
+ tkey->enable = (bool)(cfg_buf[0x14] & 1);
+ tkey->axis_rx = (bool)((cfg_buf[0x14] >> 1) & 1);
+ tkey->key_num = (u8)cfg_buf[0x15];
+ tkey->ch_num[0] = (u8)cfg_buf[0x16];
+ tkey->ch_num[1] = (u8)cfg_buf[0x17];
+ tkey->ch_num[2] = (u8)cfg_buf[0x18];
+ tkey->ch_num[3] = (u8)cfg_buf[0x19];
+ tkey->ch_num[4] = (u8)cfg_buf[0x1A];
+
+ if (tkey->axis_rx) {
+ if (tsp->dir.swap_xy) tsp->height -= 1;
+ else tsp->width -= 1;
+ } else {
+ if (tsp->dir.swap_xy) tsp->width -= 1;
+ else tsp->height -= 1;
+ }
+
+ return ret;
+}
+
+#define TSP_INFO_SWAP_XY (1 << 0)
+#define TSP_INFO_FLIP_X (1 << 1)
+#define TSP_INFO_FLIP_Y (1 << 2)
+int ist30xx_get_tsp_info(struct ist30xx_data *data)
+{
+ int ret = 0;
+ TSP_INFO *tsp = &ist30xx_tsp_info;
+ u8 *cfg_buf, *sensor_buf;
+
+ ist30xx_get_update_info(data, data->fw.buf, data->fw.buf_size);
+ cfg_buf = (u8 *)&data->fw.buf[data->tags.cfg_addr];
+ sensor_buf = (u8 *)&data->fw.buf[data->tags.sensor1_addr];
+
+ tsp->finger_num = (u8)cfg_buf[0x0E];
+ tsp->dir.swap_xy = (bool)(cfg_buf[0xF] & TSP_INFO_SWAP_XY ? true : false);
+ tsp->dir.flip_x = (bool)(cfg_buf[0xF] & TSP_INFO_FLIP_X ? true : false);
+ tsp->dir.flip_y = (bool)(cfg_buf[0xF] & TSP_INFO_FLIP_Y ? true : false);
+
+ tsp->ch_num.tx = (u8)sensor_buf[0x40];
+ tsp->ch_num.rx = (u8)sensor_buf[0x41];
+
+ tsp->node.len = tsp->ch_num.tx * tsp->ch_num.rx;
+ tsp->height = (tsp->dir.swap_xy ? tsp->ch_num.rx : tsp->ch_num.tx);
+ tsp->width = (tsp->dir.swap_xy ? tsp->ch_num.tx : tsp->ch_num.rx);
+
+ return ret;
+}
+#endif // (IST30XX_DEBUG) && (IST30XX_INTERNAL_BIN)
+
+
+#define update_next_step(ret) { if (unlikely(ret)) goto end; }
+int ist30xx_fw_update(struct i2c_client *client, const u8 *buf, int size, bool mode)
+{
+ int ret = 0;
+ u32 chksum = 0;
+ struct ist30xx_fw *fw = &ts_data->fw;
+
+ tsp_info("*** Firmware update ***\n");
+ tsp_info(" core: %x, param: %x, sub: %x (addr: 0x%x ~ 0x%x)\n",
+ ist30xx_fw_ver, ist30xx_param_ver, ist30xx_sub_ver,
+ fw->index, (fw->index + fw->size));
+
+ ts_data->status.update = 1;
+
+ ist30xx_disable_irq(ts_data);
+
+ ist30xx_reset();
+
+ if (mode) { /* ISP Mode */
+ ret = ist30xxb_isp_fw_update(client, buf, &chksum);
+ update_next_step(ret);
+ } else { /* I2C SW Mode */
+ ret = ist30xx_cmd_update(client, CMD_ENTER_FW_UPDATE);
+ update_next_step(ret);
+
+ ret = ist30xx_fw_write(client, buf);
+ update_next_step(ret);
+ }
+ msleep(50);
+
+ buf += IST30XX_EEPROM_SIZE;
+ size -= IST30XX_EEPROM_SIZE;
+
+ ret = ist30xx_cmd_run_device(client, true);
+ update_next_step(ret);
+
+ ret = ist30xx_read_cmd(client, CMD_GET_CHECKSUM, &chksum);
+ if (unlikely((ret) || (chksum != fw->chksum)))
+ goto end;
+
+ ret = ist30xx_get_ver_info(ts_data);
+ update_next_step(ret);
+
+end:
+ if (unlikely(ret)) {
+ tsp_warn("Firmware update Fail!, ret=%d\n", ret);
+ } else if (unlikely(chksum != fw->chksum)) {
+ tsp_warn("Error CheckSum: %x(%x)\n", chksum, fw->chksum);
+ ret = -ENOEXEC;
+ }
+
+ ist30xx_enable_irq(ts_data);
+
+ ts_data->status.update = 2;
+
+ return ret;
+}
+
+int ist30xx_fw_recovery(struct ist30xx_data *data)
+{
+ int ret;
+ u8 *fw = data->fw.buf;
+ int fw_size = data->fw.buf_size;
+
+ ist30xx_get_update_info(data, fw, fw_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_fw_update(data->client, fw, fw_size, true);
+ mutex_unlock(&ist30xx_mutex);
+
+ ist30xx_calibrate(1);
+
+ ist30xx_init_touch_driver(data);
+
+ return ret;
+}
+
+
+#define fw_update_next_step(ret) { if (unlikely(ret)) \
+ { step = 1; goto end; } }
+#define param_update_next_step(ret) { if (unlikely(ret)) \
+ { step = 2; goto end; } }
+int ist30xx_fw_param_update(struct i2c_client *client, const u8 *buf)
+{
+ int ret = 0;
+ int step = 0;
+ u16 len = 0;
+ int size = IST30XX_EEPROM_SIZE;
+ u32 chksum = 0, ver = 0;
+ u32 *param = (u32 *)buf;
+ struct ist30xx_fw *fw = &ts_data->fw;
+
+ tsp_info("*** FW param update ***\n");
+ tsp_info(" core: %x, param: %x, sub: %x (addr: 0x%x ~ 0x%x)\n",
+ ist30xx_fw_ver, ist30xx_param_ver, ist30xx_sub_ver,
+ fw->index, (fw->index + fw->size));
+
+ ts_data->status.update = 1;
+
+ ist30xx_disable_irq(ts_data);
+ ist30xx_reset();
+
+ /* FW update by SW */
+ ret = ist30xx_cmd_update(client, CMD_ENTER_FW_UPDATE);
+ fw_update_next_step(ret);
+
+ ret = ist30xx_fw_write(client, buf);
+ fw_update_next_step(ret);
+
+ msleep(50);
+
+ param = (u32 *)(buf + (fw->index + fw->size));
+ size -= fw->size;
+
+ ret = ist30xx_cmd_run_device(client, true);
+ fw_update_next_step(ret);
+
+ ret = ist30xx_read_cmd(client, CMD_GET_CHECKSUM, &chksum);
+ if (unlikely((ret) || (chksum != fw->chksum)))
+ goto end;
+
+ ret = ist30xx_read_cmd(client, CMD_GET_FW_VER, &ver);
+ update_next_step(ret);
+ fw->prev_core_ver = fw->core_ver;
+ fw->core_ver = ver;
+ tsp_info("F/W core version : %x\n", fw->core_ver);
+
+ /* update parameters */
+ ret = ist30xx_cmd_update(client, CMD_ENTER_UPDATE);
+ param_update_next_step(ret);
+
+ /* config */
+ len = (ts_data->tags.cfg_size >> 2);
+ ret = ist30xx_write_buf(client, CMD_UPDATE_CONFIG, param, len);
+ param_update_next_step(ret);
+ msleep(10);
+
+ param += len;
+ size -= (len << 2);
+
+ /* sensor */
+ len = ((ts_data->tags.sensor1_size + ts_data->tags.sensor2_size) >> 2);
+ ret = ist30xx_write_buf(client, CMD_UPDATE_SENSOR, param, len);
+ param_update_next_step(ret);
+ msleep(10);
+
+ param += len;
+ size -= (len << 2);
+
+ ret = ist30xx_cmd_update(client, CMD_EXIT_UPDATE);
+ param_update_next_step(ret);
+ msleep(120);
+
+ ret = ist30xx_cmd_run_device(client, true);
+ update_next_step(ret);
+
+ ret = ist30xx_read_cmd(client, CMD_GET_PARAM_VER, &ver);
+ update_next_step(ret);
+ fw->prev_param_ver = fw->param_ver;
+ fw->param_ver = ver;
+
+ ret = ist30xx_read_cmd(client, CMD_GET_SUB_VER, &ver);
+ update_next_step(ret);
+ fw->sub_ver = ver;
+
+ tsp_info("Param version : %x, (sub: %x)\n", fw->param_ver, fw->sub_ver);
+
+end:
+ if (unlikely(ret)) {
+ if (unlikely(step == 1))
+ tsp_warn("Firmware update Fail!, ret=%d", ret);
+ else if (unlikely(step == 2))
+ tsp_warn("Parameters update Fail!, ret=%d", ret);
+ } else if (unlikely(chksum != fw->chksum)) {
+ tsp_warn("Error CheckSum: %08x(%08x)\n", chksum, fw->chksum);
+ ret = -ENOEXEC;
+ }
+
+ ist30xx_enable_irq(ts_data);
+ ts_data->status.update = 2;
+
+ return ret;
+}
+
+
+#if IST30XX_INTERNAL_BIN
+int ist30xx_check_fw(struct ist30xx_data *data, const u8 *buf)
+{
+ int ret;
+ u32 chksum;
+
+ ret = ist30xx_read_cmd(data->client, CMD_GET_CHECKSUM, &chksum);
+ if (unlikely(ret)) return ret;
+
+ if (unlikely(chksum != data->fw.chksum)) {
+ tsp_warn("Checksum compare error, (IC: %08x, Bin: %08x)\n",
+ chksum, data->fw.chksum);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+bool ist30xx_check_valid_vendor(u32 tsp_vendor)
+{
+ switch (tsp_vendor) {
+ case TSP_TYPE_ALPS:
+ case TSP_TYPE_EELY:
+ case TSP_TYPE_TOP:
+ case TSP_TYPE_MELFAS:
+ case TSP_TYPE_ILJIN:
+ case TSP_TYPE_SYNOPEX:
+ case TSP_TYPE_SMAC:
+ case TSP_TYPE_OTHERS:
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+#if IST30XX_MULTIPLE_TSP
+void ist30xx_set_tsp_fw(struct ist30xx_data *data)
+{
+ char *str;
+ struct ist30xx_fw *fw = &data->fw;
+
+ switch (data->tsp_type) {
+ case TSP_TYPE_ALPS:
+ case TSP_TYPE_ILJIN:
+ str = "ALPS/ILJIN";
+ fw->buf = (u8 *)ist30xxb_fw;
+ fw->buf_size = sizeof(ist30xxb_fw);
+ break;
+ case TSP_TYPE_EELY:
+ str = "EELY";
+ fw->buf = (u8 *)ist30xxb_fw2;
+ fw->buf_size = sizeof(ist30xxb_fw2);
+ break;
+ case TSP_TYPE_TOP:
+ str = "TopTouch";
+ fw->buf = (u8 *)ist30xxb_fw1;
+ fw->buf_size = sizeof(ist30xxb_fw1);
+ break;
+ case TSP_TYPE_UNKNOWN:
+ default:
+ str = "Unknown";
+ tsp_warn("Unknown TSP vendor(0x%x)\n", data->tsp_type);
+ break;
+ }
+ tsp_info("TSP vendor : %s(%x)\n", str, data->tsp_type);
+
+ ist30xx_get_update_info(ts_data, fw->buf, fw->buf_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw->buf);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw->buf);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw->buf);
+}
+#endif // IST30XX_MULTIPLE_TSP
+
+
+int ist30xx_check_auto_update(struct ist30xx_data *data)
+{
+ int ret = 0;
+ int retry = IST30XX_FW_UPDATE_RETRY;
+ u32 tsp_type = TSP_TYPE_UNKNOWN;
+ u32 chksum;
+ bool tsp_check = false;
+ struct ist30xx_fw *fw = &data->fw;
+
+ while (retry--) {
+ ret = ist30xx_read_cmd(data->client,
+ CMD_GET_TSP_PANNEL_TYPE, &tsp_type);
+ if (likely(ret == 0)) {
+ if (likely(ist30xx_check_valid_vendor(tsp_type) == true))
+ tsp_check = true;
+ break;
+ }
+ }
+
+ retry = IST30XX_FW_UPDATE_RETRY;
+
+ if (unlikely(!tsp_check)) {
+ while (retry--) {
+ /* FW recovery */
+ tsp_info("tsp type: %x\n", tsp_type);
+ ret = ist30xxb_isp_bootloader(data->client, fw->buf);
+ if (likely(ret == 0)) {
+ ist30xx_reset();
+ ret = ist30xx_read_cmd(data->client,
+ CMD_GET_TSP_PANNEL_TYPE, &tsp_type);
+ tsp_info("tsp type: %x\n", tsp_type);
+ if (likely(ret == 0)) // recovery OK
+ if (ist30xx_check_valid_vendor(tsp_type) == true) {
+ data->tsp_type = tsp_type;
+#if IST30XX_MULTIPLE_TSP
+ ist30xx_set_tsp_fw(data);
+#endif
+ break;
+ }
+ }
+
+ if (retry == 0) return 1; /* TSP is not connected */
+ }
+ }
+
+ ist30xx_cmd_run_device(data->client, false);
+
+ ist30xx_get_ver_info(data);
+
+ if (likely((fw->param_ver > 0) && (fw->param_ver < 0xFFFFFFFF))) {
+ if (unlikely(((fw->core_ver & MASK_FW_VER) != IST30XX_FW_VER3) &&
+ ((fw->core_ver & MASK_FW_VER) != IST30XX_FW_VER4)))
+ goto fw_check_end;
+
+ tsp_info("Version compare IC: %x(%x), BIN: %x(%x)\n",
+ fw->param_ver, fw->core_ver, ist30xx_param_ver, ist30xx_fw_ver);
+
+ /* If FW version is same, check FW checksum */
+ if (likely((fw->core_ver == ist30xx_fw_ver) &&
+ (fw->param_ver == ist30xx_param_ver))) {
+ ret = ist30xx_read_cmd(data->client, CMD_GET_CHECKSUM, &chksum);
+ if (unlikely((ret) || (chksum != fw->chksum))) {
+ tsp_warn("Checksum error, IC: %x, Bin: %x (ret: %d)\n",
+ chksum, fw->chksum, ret);
+ goto fw_check_end;
+ }
+ }
+
+ /*
+ * fw->core_ver : FW core version in TSP IC
+ * fw->param_ver : FW version if TSP IC
+ * ist30xx_fw_ver : FW core version in FW Binary
+ * ist30xx_fw_ver : FW version in FW Binary
+ */
+ /* If the ver of binary is higher than ver of IC, FW update operate. */
+
+ if (likely((fw->core_ver >= ist30xx_fw_ver) &&
+ (fw->param_ver >= ist30xx_param_ver)))
+ return 0;
+ }
+
+fw_check_end:
+ return -EAGAIN;
+}
+
+int ist30xx_auto_bin_update(struct ist30xx_data *data)
+{
+ int ret = 0;
+ int retry = IST30XX_FW_UPDATE_RETRY;
+ struct ist30xx_fw *fw = &data->fw;
+
+ fw->buf = (u8 *)ist30xxb_fw;
+ fw->buf_size = sizeof(ist30xxb_fw);
+
+#if IST30XX_MULTIPLE_TSP
+ ist30xx_set_tsp_fw(data);
+#else
+ ist30xx_get_update_info(data, fw->buf, fw->buf_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw->buf);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw->buf);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw->buf);
+#endif
+
+ tsp_info("IC: %x, Binary ver core: %x, param: %x, sub: %x\n",
+ data->chip_id, ist30xx_fw_ver, ist30xx_param_ver, ist30xx_sub_ver);
+
+ mutex_lock(&ist30xx_mutex);
+ ret = ist30xx_check_auto_update(data);
+ mutex_unlock(&ist30xx_mutex);
+ if (likely(ret >= 0))
+ return ret;
+
+update_bin: // TSP is not ready / FW update
+ tsp_info("Update version. param(core): %x(%x, %x) -> %x(%x, %x)\n",
+ fw->param_ver, fw->core_ver, fw->sub_ver,
+ ist30xx_param_ver, ist30xx_fw_ver, ist30xx_sub_ver);
+
+ mutex_lock(&ist30xx_mutex);
+ while (retry--) {
+ ret = ist30xx_fw_update(data->client, fw->buf, fw->buf_size, true);
+ if (unlikely(!ret)) break;
+ }
+ mutex_unlock(&ist30xx_mutex);
+
+ if (unlikely(ret))
+ return ret;
+
+ if (unlikely(retry > 0 && ist30xx_check_fw(data, fw->buf)))
+ goto update_bin;
+
+ ist30xx_calibrate(IST30XX_FW_UPDATE_RETRY);
+
+ return ret;
+}
+#endif // IST30XX_INTERNAL_BIN
+
+
+/* sysfs: /sys/class/touch/firmware/boot */
+ssize_t ist30xx_boot_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int fw_size = 0;
+ u8 *fw = NULL;
+ char *tmp;
+ const struct firmware *request_fw = NULL;
+
+ unsigned long mode = simple_strtoul(buf, &tmp, 10);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW) {
+ int ret = request_firmware(&request_fw, IST30XXB_FW_NAME, &ts_data->client->dev);
+ if (ret) {
+ tsp_warn("File not found, %s\n", IST30XXB_FW_NAME);
+ return size;
+ }
+ fw = (u8 *)request_fw->data;
+ fw_size = (u32)request_fw->size;
+ } else {
+#if IST30XX_INTERNAL_BIN
+ fw = ts_data->fw.buf;
+ fw_size = ts_data->fw.buf_size;
+#else
+ return size;
+#endif // IST30XX_INTERNAL_BIN
+ }
+
+ if (mode == 3) {
+ tsp_info("EEPROM all erase test\n");
+ ist30xx_disable_irq(ts_data);
+ mutex_lock(&ist30xx_mutex);
+
+ ist30xxb_isp_enable(true);
+ ist30xxb_isp_erase(ts_data->client, IST30XXB_ISP_ERASE_ALL, 0);
+ ist30xxb_isp_enable(false);
+
+ mutex_unlock(&ist30xx_mutex);
+ ist30xx_enable_irq(ts_data);
+ } else {
+ ist30xx_get_update_info(ts_data, fw, fw_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw);
+
+ ist30xx_disable_irq(ts_data);
+
+ mutex_lock(&ist30xx_mutex);
+ ist30xxb_isp_bootloader(ts_data->client, fw);
+ mutex_unlock(&ist30xx_mutex);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW)
+ release_firmware(request_fw);
+ }
+
+ return size;
+}
+
+u32 buf32_eeprom[IST30XX_EEPROM_SIZE / IST30XX_DATA_LEN];
+ssize_t ist30xx_fw_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ u8 *buf8 = (u8 *)buf32_eeprom;;
+
+ mutex_lock(&ist30xx_mutex);
+ ist30xx_disable_irq(ts_data);
+
+ ist30xxb_isp_fw_read(ts_data->client, buf32_eeprom);
+ for (i = 0; i < IST30XX_EEPROM_SIZE; i += 16) {
+ tsp_debug("%07x: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x\n", i,
+ buf8[i], buf8[i + 1], buf8[i + 2], buf8[i + 3],
+ buf8[i + 4], buf8[i + 5], buf8[i + 6], buf8[i + 7],
+ buf8[i + 8], buf8[i + 9], buf8[i + 10], buf8[i + 11],
+ buf8[i + 12], buf8[i + 13], buf8[i + 14], buf8[i + 15]);
+ }
+
+ ist30xx_enable_irq(ts_data);
+ mutex_unlock(&ist30xx_mutex);
+
+ return 0;
+}
+
+/* sysfs: /sys/class/touch/firmware/fw_only */
+ssize_t ist30xx_fw_only_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret;
+ bool isp_mode = false;
+ int fw_size = 0;
+ u8 *fw = NULL;
+ char *tmp;
+ const struct firmware *request_fw = NULL;
+
+ unsigned long mode = simple_strtoul(buf, &tmp, 10);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW) {
+ ret = request_firmware(&request_fw, IST30XXB_FW_NAME, &ts_data->client->dev);
+ if (ret) {
+ tsp_warn("File not found, %s\n", IST30XXB_FW_NAME);
+ return size;
+ }
+ fw = (u8 *)request_fw->data;
+ fw_size = (u32)request_fw->size;
+ isp_mode = (mode == MASK_UPDATE_ISP ? true : false);
+ } else {
+#if IST30XX_INTERNAL_BIN
+ fw = ts_data->fw.buf;
+ fw_size = ts_data->fw.buf_size;
+#else
+ return size;
+#endif // IST30XX_INTERNAL_BIN
+ }
+
+ ist30xx_get_update_info(ts_data, fw, fw_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw);
+
+ mutex_lock(&ist30xx_mutex);
+ ist30xx_fw_update(ts_data->client, fw, fw_size, isp_mode);
+ mutex_unlock(&ist30xx_mutex);
+
+ ist30xx_init_touch_driver(ts_data);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW)
+ release_firmware(request_fw);
+
+ return size;
+}
+
+/* sysfs: /sys/class/touch/firmware/firmware */
+ssize_t ist30xx_fw_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ bool isp_mode = false;
+ int fw_size = 0;
+ u8 *fw = NULL;
+ char *tmp;
+ const struct firmware *request_fw = NULL;
+
+ unsigned long mode = simple_strtoul(buf, &tmp, 10);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW) {
+ int ret = request_firmware(&request_fw, IST30XXB_FW_NAME, &ts_data->client->dev);
+ if (ret) {
+ tsp_warn("File not found, %s\n", IST30XXB_FW_NAME);
+ return size;
+ }
+ fw = (u8 *)request_fw->data;
+ fw_size = (u32)request_fw->size;
+ isp_mode = (mode == MASK_UPDATE_ISP ? true : false);
+ } else {
+#if IST30XX_INTERNAL_BIN
+ fw = ts_data->fw.buf;
+ fw_size = ts_data->fw.buf_size;
+#else
+ return size;
+#endif // IST30XX_INTERNAL_BIN
+ }
+
+ ist30xx_get_update_info(ts_data, fw, fw_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw);
+
+ mutex_lock(&ist30xx_mutex);
+ ist30xx_fw_update(ts_data->client, fw, fw_size, isp_mode);
+ mutex_unlock(&ist30xx_mutex);
+
+ ist30xx_calibrate(1);
+
+ ist30xx_init_touch_driver(ts_data);
+
+ if (mode == MASK_UPDATE_ISP || mode == MASK_UPDATE_FW)
+ release_firmware(request_fw);
+
+ return size;
+}
+
+ssize_t ist30xx_fw_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int count;
+
+ switch (ts_data->status.update) {
+ case 1:
+ count = sprintf(buf, "Downloading\n");
+ break;
+ case 2:
+ count = sprintf(buf, "Update success, ver %x(%x) -> %x(%x, %x), status : %d, gap : %d\n",
+ ts_data->fw.prev_param_ver, ts_data->fw.prev_core_ver,
+ ts_data->fw.param_ver, ts_data->fw.core_ver, ts_data->fw.sub_ver,
+ CALIB_TO_STATUS(ts_data->status.calib_msg),
+ CALIB_TO_GAP(ts_data->status.calib_msg));
+ break;
+ default:
+ count = sprintf(buf, "Pass\n");
+ }
+
+ return count;
+}
+
+
+/* sysfs: /sys/class/touch/firmware/fwparam */
+ssize_t ist30xx_fwparam_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int fw_size = 0;
+ u8 *fw = NULL;
+ char *tmp;
+ const struct firmware *request_fw = NULL;
+
+ unsigned long mode = simple_strtoul(buf, &tmp, 10);
+
+ if (mode == MASK_UPDATE_FW) {
+ int ret = request_firmware(&request_fw, IST30XXB_FW_NAME, &ts_data->client->dev);
+ if (unlikely(ret)) {
+ tsp_warn("File not found, %s\n", IST30XXB_FW_NAME);
+ return size;
+ }
+ fw = (u8 *)request_fw->data;
+ fw_size = (u32)request_fw->size;
+ } else {
+#if IST30XX_INTERNAL_BIN
+ fw = ts_data->fw.buf;
+ fw_size = ts_data->fw.buf_size;
+#else
+ return size;
+#endif // IST30XX_INTERNAL_BIN
+ }
+
+ ist30xx_get_update_info(ts_data, fw, fw_size);
+ ist30xx_fw_ver = ist30xx_parse_ver(FLAG_FW, fw);
+ ist30xx_param_ver = ist30xx_parse_ver(FLAG_PARAM, fw);
+ ist30xx_sub_ver = ist30xx_parse_ver(FLAG_SUB, fw);
+
+ mutex_lock(&ist30xx_mutex);
+ ist30xx_fw_param_update(ts_data->client, fw);
+ mutex_unlock(&ist30xx_mutex);
+
+ ist30xx_calibrate(1);
+
+ ist30xx_init_touch_driver(ts_data);
+
+ if (mode == MASK_UPDATE_FW)
+ release_firmware(request_fw);
+
+ return size;
+}
+
+
+/* sysfs: /sys/class/touch/firmware/viersion */
+ssize_t ist30xx_fw_version(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int count;
+
+ count = sprintf(buf, "ID: %x, f/w core: %x, param: %x, sub: %x\n",
+ ts_data->chip_id, ts_data->fw.core_ver,
+ ts_data->fw.param_ver, ts_data->fw.sub_ver);
+
+#if IST30XX_INTERNAL_BIN
+ {
+ char msg[128];
+
+ ist30xx_get_update_info(ts_data, ts_data->fw.buf, ts_data->fw.buf_size);
+
+ count += snprintf(msg, sizeof(msg),
+ " Header - f/w ver: %x, param: %x, sub: %x\r\n",
+ ist30xx_parse_ver(FLAG_FW, ts_data->fw.buf),
+ ist30xx_parse_ver(FLAG_PARAM, ts_data->fw.buf),
+ ist30xx_parse_ver(FLAG_SUB, ts_data->fw.buf));
+ strncat(buf, msg, sizeof(msg));
+ }
+#endif
+
+ return count;
+}
+
+
+/* sysfs */
+static DEVICE_ATTR(fw_only, S_IWUSR | S_IWGRP | S_IRUGO, ist30xx_fw_status, ist30xx_fw_only_store);
+static DEVICE_ATTR(firmware, S_IWUSR | S_IWGRP | S_IRUGO, ist30xx_fw_status, ist30xx_fw_store);
+static DEVICE_ATTR(fwparam, S_IWUSR | S_IWGRP, NULL, ist30xx_fwparam_store);
+static DEVICE_ATTR(boot, S_IWUSR | S_IWGRP | S_IRUGO, ist30xx_fw_show, ist30xx_boot_store);
+static DEVICE_ATTR(version, S_IRUGO, ist30xx_fw_version, NULL);
+
+struct class *ist30xx_class;
+struct device *ist30xx_fw_dev;
+
+static struct attribute *fw_attributes[] = {
+ &dev_attr_fw_only.attr,
+ &dev_attr_firmware.attr,
+ &dev_attr_fwparam.attr,
+ &dev_attr_boot.attr,
+ &dev_attr_version.attr,
+ NULL,
+};
+
+static struct attribute_group fw_attr_group = {
+ .attrs = fw_attributes,
+};
+
+int ist30xx_init_update_sysfs(void)
+{
+ /* /sys/class/touch */
+ ist30xx_class = class_create(THIS_MODULE, "touch");
+
+ /* /sys/class/touch/firmware */
+ ist30xx_fw_dev = device_create(ist30xx_class, NULL, 0, NULL, "firmware");
+
+ /* /sys/class/touch/firmware/... */
+ if (unlikely(sysfs_create_group(&ist30xx_fw_dev->kobj,
+ &fw_attr_group)))
+ tsp_err("Failed to create sysfs group(%s)!\n", "firmware");
+
+ ts_data->status.update = 0;
+ ts_data->status.calib = 0;
+
+ return 0;
+}