diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/touchscreen/ist30xxc/ist30xxc_update.c | |
Diffstat (limited to 'drivers/input/touchscreen/ist30xxc/ist30xxc_update.c')
| -rwxr-xr-x | drivers/input/touchscreen/ist30xxc/ist30xxc_update.c | 1474 |
1 files changed, 1474 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ist30xxc/ist30xxc_update.c b/drivers/input/touchscreen/ist30xxc/ist30xxc_update.c new file mode 100755 index 00000000..92073196 --- /dev/null +++ b/drivers/input/touchscreen/ist30xxc/ist30xxc_update.c @@ -0,0 +1,1474 @@ +/* + * 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 <linux/stat.h> +#include <asm/unaligned.h> +#include <linux/uaccess.h> +#include <linux/fcntl.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/err.h> + +#include "ist30xxc.h" +#include "ist30xxc_update.h" +#include "ist30xxc_tracking.h" + +#if IST30XX_INTERNAL_BIN +#if defined(CONFIG_MACH_SS_SHARKLT8) || defined(CONFIG_MACH_SS_SHARKLT8LIGHTSLEEP) +#include "./firmware/ss_sharklt8_fw.h" +#else +#include "./firmware/coreprimeve3g_fw.h" +#endif +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) +#if IST30XX_MULTIPLE_TSP +//#include "./firmware/ist30xxc_fw_tsp_Ver0000_porting.h" +#endif +#endif +#endif // IST30XX_INTERNAL_BIN + +#define TAGS_PARSE_OK (0) + +// ISP burst r/w +int ist30xx_isp_read_burst(struct i2c_client *client, u32 addr, + u32 *buf32, u16 len) +{ + int ret = 0; + int i; + u16 max_len = I2C_MAX_READ_SIZE / IST30XX_DATA_LEN; + u16 remain_len = len; + + for (i = 0; i < len; i += max_len) { + if (remain_len < max_len) max_len = remain_len; + ret = ist30xx_read_buf(client, addr, buf32, max_len); + if (unlikely(ret)) { + tsp_err("Burst fail, addr: %x\n", __func__, addr); + return ret; + } + + buf32 += max_len; + remain_len -= max_len; + } + + return 0; +} + +int ist30xx_isp_write_burst(struct i2c_client *client, u32 addr, + u32 *buf32, u16 len) +{ + int ret = 0; + int i; + u16 max_len = I2C_MAX_WRITE_SIZE / IST30XX_DATA_LEN; + u16 remain_len = len; + + for (i = 0; i < len; i += max_len) { + if (remain_len < max_len) max_len = remain_len; + ret = ist30xx_write_buf(client, addr, buf32, max_len); + if (unlikely(ret)) { + tsp_err("Burst fail, addr: %x\n", addr); + return ret; + } + + buf32 += max_len; + remain_len -= max_len; + } + + return 0; +} + +#define IST30XX_ISP_READ_TOTAL_S (0x01) +#define IST30XX_ISP_READ_TOTAL_B (0x11) +#define IST30XX_ISP_READ_MAIN_S (0x02) +#define IST30XX_ISP_READ_MAIN_B (0x12) +#define IST30XX_ISP_READ_INFO_S (0x03) +#define IST30XX_ISP_READ_INFO_B (0x13) +#define IST30XX_ISP_PROG_TOTAL_S (0x04) +#define IST30XX_ISP_PROG_TOTAL_B (0x14) +#define IST30XX_ISP_PROG_MAIN_S (0x05) +#define IST30XX_ISP_PROG_MAIN_B (0x15) +#define IST30XX_ISP_PROG_INFO_S (0x06) +#define IST30XX_ISP_PROG_INFO_B (0x16) +#define IST30XX_ISP_ERASE_BLOCK (0x07) +#define IST30XX_ISP_ERASE_SECTOR (0x08) +#define IST30XX_ISP_ERASE_PAGE (0x09) +#define IST30XX_ISP_ERASE_INFO (0x0A) +#define IST30XX_ISP_READ_TOTAL_CRC (0x1B) +#define IST30XX_ISP_READ_MAIN_CRC (0x1C) +#define IST30XX_ISP_READ_INFO_CRC (0x1D) +int ist30xxc_isp_enable(struct i2c_client *client, bool enable) +{ + int ret = 0; + + if (enable) { + ret = ist30xx_write_cmd(client, IST30XX_FLASH_ISPEN, 0xDE01); + if (unlikely(ret)) + return ret; + +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) + ret = ist30xx_write_cmd(client, IST30XX_FLASH_MODE, 0x4100); + if (unlikely(ret)) + return ret; + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_TEST_MODE1, 0x38); + if (unlikely(ret)) + return ret; +#endif + } else { + ret = ist30xx_write_cmd(client, IST30XX_FLASH_ISPEN, 0x2); + if (unlikely(ret)) + return ret; + } + + msleep(1); + + return ret; +} + +int ist30xxc_isp_mode(struct i2c_client *client, int mode) +{ + int ret = 0; + u32 val = 0; + + switch (mode) { + case IST30XX_ISP_READ_TOTAL_S: + val = 0x8090; + break; + case IST30XX_ISP_READ_TOTAL_B: + case IST30XX_ISP_READ_TOTAL_CRC: + val = 0x8190; + break; + case IST30XX_ISP_READ_MAIN_S: + val = 0x0090; + break; + case IST30XX_ISP_READ_MAIN_B: + case IST30XX_ISP_READ_MAIN_CRC: + val = 0x0190; + break; + case IST30XX_ISP_READ_INFO_S: + val = 0x0098; + break; + case IST30XX_ISP_READ_INFO_B: + case IST30XX_ISP_READ_INFO_CRC: + val = 0x0198; + break; + case IST30XX_ISP_PROG_TOTAL_S: + val = 0x8050; + break; + case IST30XX_ISP_PROG_TOTAL_B: + val = 0x8150; + break; + case IST30XX_ISP_PROG_MAIN_S: + val = 0x0050; + break; + case IST30XX_ISP_PROG_MAIN_B: + val = 0x0150; + break; + case IST30XX_ISP_PROG_INFO_S: + val = 0x0058; + break; + case IST30XX_ISP_PROG_INFO_B: + val = 0x0158; + break; + case IST30XX_ISP_ERASE_BLOCK: + val = 0x0031; + break; + case IST30XX_ISP_ERASE_SECTOR: + val = 0x0032; + break; + case IST30XX_ISP_ERASE_PAGE: + val = 0x0030; + break; + case IST30XX_ISP_ERASE_INFO: + val = 0x0038; + break; + default: + tsp_err("ISP fail, unknown mode\n"); + return -EINVAL; + } + +#if (IMAGIS_TSP_IC > IMAGIS_IST3032C) + val &= ~(0x8000); +#endif + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_MODE, val); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_MODE\n"); + return ret; + } + + return 0; +} + +int ist30xxc_isp_erase(struct i2c_client *client, int mode, u32 index) +{ + int ret = 0; + + ret = ist30xxc_isp_mode(client, mode); + if (unlikely(ret)) + return ret; + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_DIN, index); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_DIN\n"); + return ret; + } + + msleep(50); // Flash erase time : Mininum 40msec + + return ret; +} + + +int ist30xxc_isp_program(struct i2c_client *client, u32 addr, int mode, + const u32 *buf32, int len) +{ + int ret = 0; + + ret = ist30xxc_isp_mode(client, mode); + if (unlikely(ret)) + return ret; + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_ADDR, addr); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_ADDR\n"); + return ret; + } + + if (mode & 0x10) + ret = ist30xx_isp_write_burst(client, + IST30XX_FLASH_DIN, (u32 *)buf32, len); + else + ret = ist30xx_write_buf(client, IST30XX_FLASH_DIN, (u32 *)buf32, len); + + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_DIN\n"); + return ret; + } + + return ret; +} + +int ist30xxc_isp_read(struct i2c_client *client, u32 addr, int mode, + u32 *buf32, int len) +{ + int ret = 0; + + /* IST30xxB ISP read mode */ + ret = ist30xxc_isp_mode(client, mode); + if (unlikely(ret)) + return ret; + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_ADDR, addr); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_ADDR\n"); + return ret; + } + + if (mode & 0x10) + ret = ist30xx_isp_read_burst(client, IST30XX_FLASH_DOUT, buf32, len); + else + ret = ist30xx_read_buf(client, IST30XX_FLASH_DOUT, buf32, len); + + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_DOUT\n"); + return ret; + } + + return 0; +} + +int ist30xxc_cmd_read_chksum(struct i2c_client *client, int mode, + u32 start_addr, u32 end_addr, u32 *chksum) +{ + int ret = 0; + u32 val = (1 << 28) | (1 << 25) | (1 << 24) | (1 << 20) | (1 << 16); + + val |= (end_addr / IST30XX_ADDR_LEN) - 1; + + ret = ist30xxc_isp_mode(client, mode); + if (unlikely(ret)) + return ret; + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_ADDR, start_addr); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_ADDR (%x)\n", val); + return ret; + } + + ret = ist30xx_write_cmd(client, IST30XX_FLASH_AUTO_READ, val); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_AUTO_READ (%x)\n", val); + return ret; + } + + msleep(100); + + ret = ist30xx_read_reg(client, IST30XX_FLASH_CRC, chksum); + if (unlikely(ret)) { + tsp_err("ISP fail, IST30XX_FLASH_CRC (%x)\n", chksum); + return ret; + } + + return 0; +} + +int ist30xxc_read_chksum(struct ist30xx_data *data, u32 *chksum) +{ + int ret = 0; + u32 start_addr, end_addr; + + start_addr = data->tags.fw_addr; + end_addr = data->tags.sensor_addr + data->tags.sensor_size; + ret = ist30xxc_cmd_read_chksum(data->client, + IST30XX_ISP_READ_TOTAL_CRC, start_addr, end_addr, chksum); + if (unlikely(ret)) + return ret; + + tsp_info("chksum: %x(%x~%x)\n", *chksum, start_addr, end_addr); + + return 0; +} + +int ist30xxc_read_chksum_all(struct i2c_client *client, u32 *chksum) +{ + int ret = 0; + u32 start_addr, end_addr; + + start_addr = IST30XX_FLASH_BASE_ADDR; + end_addr = IST30XX_FLASH_BASE_ADDR + IST30XX_FLASH_TOTAL_SIZE; + ret = ist30xxc_cmd_read_chksum(client, + IST30XX_ISP_READ_TOTAL_CRC, start_addr, end_addr, chksum); + if (unlikely(ret)) + return ret; + + tsp_info("chksum: %x(%x~%x)\n", *chksum, start_addr, end_addr); + + return 0; +} + +int ist30xxc_isp_fw_read(struct ist30xx_data *data, u32 *buf32) +{ + int ret = 0; + int i; + int len; + u32 addr = IST30XX_FLASH_BASE_ADDR; + + ist30xx_reset(data, true); + + /* IST30xxB ISP enable */ + ret = ist30xxc_isp_enable(data->client, true); + if (unlikely(ret)) + return ret; + +#if I2C_BURST_MODE + for (i = 0; i < IST30XX_FLASH_TOTAL_SIZE; i += I2C_MAX_READ_SIZE) { + len = I2C_MAX_READ_SIZE / IST30XX_DATA_LEN; + if ((IST30XX_FLASH_TOTAL_SIZE - i) < I2C_MAX_READ_SIZE) + len = (IST30XX_FLASH_TOTAL_SIZE - i) / IST30XX_DATA_LEN; + + ret = ist30xxc_isp_read(data->client, addr, IST30XX_ISP_READ_TOTAL_B, + buf32, len); + if (unlikely(ret)) + goto isp_fw_read_end; + + addr += len; + buf32 += len; + } +#else + for (i = 0; i < IST30XX_FLASH_MAIN_SIZE; i += IST30XX_DATA_LEN) { + ret = ist30xxc_isp_read(data->client, addr, IST30XX_ISP_READ_TOTAL_S, + buf32, 1); + if (unlikely(ret)) + goto isp_fw_read_end; + + addr++; + buf32++; + } +#endif +isp_fw_read_end: + /* IST30xxC ISP disable */ + ist30xxc_isp_enable(data->client, false); + ist30xx_reset(data, false); + return ret; +} + +int ist30xxc_isp_fw_update(struct ist30xx_data *data, const u8 *buf) +{ +#if !(I2C_BURST_MODE) + int i; +#endif + int ret = 0; + u32 addr = IST30XX_FLASH_BASE_ADDR; + + ist30xx_reset(data, true); + + /* IST30xxC ISP enable */ + ret = ist30xxc_isp_enable(data->client, true); + if (unlikely(ret)) + goto isp_fw_update_end; + + /* IST30xxC ISP erase */ + ret = ist30xxc_isp_erase(data->client, IST30XX_ISP_ERASE_BLOCK, 0); + if (unlikely(ret)) + goto isp_fw_update_end; + +#if (IST30XX_FLASH_INFO_SIZE > 0) + ret = ist30xxc_isp_erase(data->client, IST30XX_ISP_ERASE_INFO, 0); + if (unlikely(ret)) + goto isp_fw_update_end; +#endif + + ist30xx_reset(data, true); + + /* IST30xxC ISP enable */ + ret = ist30xxc_isp_enable(data->client, true); + if (unlikely(ret)) + goto isp_fw_update_end; + +#if I2C_BURST_MODE + /* IST30xxC ISP write burst */ +#if 0 + for (i = 0; i < IST30XX_FLASH_TOTAL_SIZE / IST30XX_FLASH_PAGE_SIZE; i++) { + ret = ist30xxc_isp_program(data->client, addr, + IST30XX_ISP_PROG_TOTAL_B, (u32 *)buf, + IST30XX_FLASH_PAGE_SIZE / IST30XX_DATA_LEN); + if (unlikely(ret)) + goto isp_fw_update_end; + + addr += IST30XX_FLASH_PAGE_SIZE / IST30XX_DATA_LEN; + buf += IST30XX_FLASH_PAGE_SIZE; + } +#else + ret = ist30xxc_isp_program(data->client, addr, IST30XX_ISP_PROG_TOTAL_B, + (u32 *)buf, IST30XX_FLASH_TOTAL_SIZE / IST30XX_DATA_LEN); + if (unlikely(ret)) + goto isp_fw_update_end; +#endif +#else + /* IST30xxC ISP write single */ + for (i = 0; i < IST30XX_FLASH_TOTAL_SIZE; i += IST30XX_DATA_LEN) { + ret = ist30xxc_isp_program(data->client, addr, + IST30XX_ISP_PROG_TOTAL_S, (u32 *)buf, 1); + if (unlikely(ret)) + goto isp_fw_update_end; + + addr++; + buf += IST30XX_DATA_LEN; + } +#endif + +isp_fw_update_end: + /* IST30xxC ISP disable */ + ist30xxc_isp_enable(data->client, false); + ist30xx_reset(data, false); + return ret; +} + +u32 ist30xx_parse_ver(struct ist30xx_data *data, int flag, const u8 *buf) +{ + u32 ver = 0; + u32 *buf32 = (u32 *)buf; + + if (flag == FLAG_MAIN) + ver = (u32)buf32[(data->tags.flag_addr + 0x1FC) >> 2]; + else if (flag == FLAG_TEST) + ver = (u32)buf32[(data->tags.flag_addr + 0x1F4) >> 2]; + else if (flag == FLAG_FW) + ver = (u32)(buf32[(data->tags.cfg_addr + 0x4) >> 2] & 0xFFFF); + else if (flag == FLAG_CORE) + ver = (u32)buf32[(data->tags.flag_addr + 0x1F0) >> 2]; + else + tsp_warn("Parsing ver's flag is not corrent!\n"); + + return ver; +} + +int calib_ms_delay = CALIB_WAIT_TIME; +int ist30xx_calib_wait(struct ist30xx_data *data) +{ + int cnt = calib_ms_delay; + + data->status.calib_msg = 0; + while (cnt-- > 0) { + msleep(100); + + if (data->status.calib_msg) { + tsp_info("Calibration status : %d, Max raw gap : %d - (%08x)\n", + CALIB_TO_STATUS(data->status.calib_msg), + CALIB_TO_GAP(data->status.calib_msg), + data->status.calib_msg); + if (CALIB_TO_STATUS(data->status.calib_msg) == 0) + return 0; // Calibrate success + else + return -EAGAIN; + } + } + tsp_warn("Calibration time out\n"); + + return -EPERM; +} + +int ist30xx_calibrate(struct ist30xx_data *data, int wait_cnt) +{ + int ret = -ENOEXEC; + + tsp_info("*** Calibrate %ds ***\n", calib_ms_delay / 10); + + data->status.update = 1; + ist30xx_disable_irq(data); + while (1) { + ret = ist30xx_cmd_calibrate(data->client); + if (unlikely(ret)) + continue; + + ist30xx_enable_irq(data); + ret = ist30xx_calib_wait(data); + if (likely(!ret)) + break; + + ist30xx_disable_irq(data); + + if (--wait_cnt == 0) + break; + + ist30xx_reset(data, false); + } + + ist30xx_disable_irq(data); + ist30xx_reset(data, false); + data->status.update = 2; + ist30xx_enable_irq(data); + + return ret; +} + + +int ist30xx_parse_tags(struct ist30xx_data *data, const u8 *buf, const u32 size) +{ + int ret = -EPERM; + struct ist30xx_tags *tags; + + tags = (struct ist30xx_tags *)(&buf[size - sizeof(struct ist30xx_tags)]); + + if (!strncmp(tags->magic1, IST30XX_TAG_MAGIC, sizeof(tags->magic1)) + && !strncmp(tags->magic2, IST30XX_TAG_MAGIC, sizeof(tags->magic2)) + ) { + data->tags = *tags; + + data->tags.fw_addr -= data->tags.rom_base; + data->tags.cfg_addr -= data->tags.rom_base; + data->tags.sensor_addr -= data->tags.rom_base; + data->tags.cp_addr -= data->tags.rom_base; + data->tags.flag_addr -= data->tags.rom_base; + + data->fw.index = data->tags.fw_addr; + data->fw.size = tags->flag_addr - tags->fw_addr + + tags->flag_size; + data->fw.chksum = tags->chksum; + + tsp_verb("Tagts magic1: %s, magic2: %s\n", + data->tags.magic1, data->tags.magic2); + tsp_verb(" rom: %x\n", data->tags.rom_base); + tsp_verb(" ram: %x\n", data->tags.ram_base); + tsp_verb(" fw: %x(%x)\n", data->tags.fw_addr, data->tags.fw_size); + tsp_verb(" cfg: %x(%x)\n", data->tags.cfg_addr, data->tags.cfg_size); + tsp_verb(" sensor: %x(%x)\n", + data->tags.sensor_addr, data->tags.sensor_size); + tsp_verb(" cp: %x(%x)\n", data->tags.cp_addr, data->tags.cp_size); + tsp_verb(" flag: %x(%x)\n", data->tags.flag_addr, data->tags.flag_size); + tsp_verb(" zvalue: %x\n", data->tags.zvalue_base); + tsp_verb(" algo: %x\n", data->tags.algo_base); + tsp_verb(" raw: %x\n", data->tags.raw_base); + tsp_verb(" filter: %x\n", data->tags.filter_base); + tsp_verb(" chksum: %x\n", data->tags.chksum); + tsp_verb(" chksum_all: %x\n", data->tags.chksum_all); + tsp_verb(" build time: %04d/%02d/%02d (%02d:%02d:%02d)\n", + data->tags.year, data->tags.month, data->tags.day, + data->tags.hour, data->tags.min, data->tags.sec); + + ret = 0; + } + + return ret; +} + +int 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"); + + return ret; +} + +#define TSP_INFO_SWAP_XY (1 << 0) +#define TSP_INFO_FLIP_X (1 << 1) +#define TSP_INFO_FLIP_Y (1 << 2) +u32 ist30xx_info_cal_crc(u32 *buf) +{ + int i; + u32 chksum32 = 0; + + for (i = 0; i < IST30XX_MAX_CMD_SIZE - 1; i++) { + chksum32 += *buf++; + } + + return chksum32; +} + +int ist30xx_tsp_update_info(struct ist30xx_data *data) +{ + int ret = 0; + u32 chksum; + u32 info[IST30XX_MAX_CMD_SIZE]; + u32 tsp_lcd, tsp_swap, tsp_scr, tsp_gtx, tsp_ch; + u32 tkey_info0, tkey_info1, tkey_info2; + u32 finger_info, baseline, threshold; + TSP_INFO *tsp = &data->tsp_info; +#if IST30XX_USE_KEY + TKEY_INFO *tkey = &data->tkey_info; +#endif + + ret = ist30xx_cmd_hold(data->client, 1); + if (unlikely(ret)) + return ret; + + ret = ist30xx_burst_read(data->client, + IST30XX_DA_ADDR(eHCOM_GET_CHIP_ID), &info[0], + IST30XX_MAX_CMD_SIZE, true); + if (unlikely(ret)) + return ret; + + ist30xx_cmd_hold(data->client, 0); + if (unlikely(ret)) { + ist30xx_reset(data, false); + return ret; + } + + ret = ist30xx_read_cmd(data, IST30XX_REG_CHIPID, &data->chip_id); + if (unlikely(ret)) + return ret; + + if ((info[IST30XX_CMD_VALUE(eHCOM_GET_CHIP_ID)] != data->chip_id) || + (info[IST30XX_CMD_VALUE(eHCOM_GET_CHIP_ID)] == 0) || + (info[IST30XX_CMD_VALUE(eHCOM_GET_CHIP_ID)] == 0xFFFF)) + return -EINVAL; + + chksum = ist30xx_info_cal_crc((u32*)info); + if (chksum != info[IST30XX_MAX_CMD_SIZE - 1]) { + tsp_err("info checksum : %08X, %08X\n", + chksum, info[IST30XX_MAX_CMD_SIZE - 1]); + return -EINVAL; + } + tsp_info("info read success\n"); + + tsp_lcd = info[IST30XX_CMD_VALUE(eHCOM_GET_LCD_INFO)]; + tsp_ch = info[IST30XX_CMD_VALUE(eHCOM_GET_TSP_INFO)]; + tkey_info0 = info[IST30XX_CMD_VALUE(eHCOM_GET_KEY_INFO_0)]; + tkey_info1 = info[IST30XX_CMD_VALUE(eHCOM_GET_KEY_INFO_1)]; + tkey_info2 = info[IST30XX_CMD_VALUE(eHCOM_GET_KEY_INFO_2)]; + tsp_scr = info[IST30XX_CMD_VALUE(eHCOM_GET_SCR_INFO)]; + tsp_gtx = info[IST30XX_CMD_VALUE(eHCOM_GET_GTX_INFO)]; + tsp_swap = info[IST30XX_CMD_VALUE(eHCOM_GET_SWAP_INFO)]; + finger_info = info[IST30XX_CMD_VALUE(eHCOM_GET_FINGER_INFO)]; + baseline = info[IST30XX_CMD_VALUE(eHCOM_GET_BASELINE)]; + threshold = info[IST30XX_CMD_VALUE(eHCOM_GET_TOUCH_TH)]; + + tsp->ch_num.rx = (tsp_ch >> 16) & 0xFFFF; + tsp->ch_num.tx = tsp_ch & 0xFFFF; + + tsp->node.len = tsp->ch_num.tx * tsp->ch_num.rx; + + tsp->gtx.num = (tsp_gtx >> 24) & 0xFF; + tsp->gtx.ch_num[0] = (tsp_gtx >> 16) & 0xFF; + tsp->gtx.ch_num[1] = (tsp_gtx >> 8) & 0xFF; + tsp->gtx.ch_num[2] = 0xFF; + tsp->gtx.ch_num[3] = 0xFF; + + tsp->finger_num = finger_info; + tsp->dir.swap_xy = (tsp_swap & TSP_INFO_SWAP_XY ? true : false); + tsp->dir.flip_x = (tsp_swap & TSP_INFO_FLIP_X ? true : false); + tsp->dir.flip_y = (tsp_swap & TSP_INFO_FLIP_Y ? true : false); + + tsp->baseline = baseline & 0xFFFF; + + tsp->screen.rx = (tsp_scr >> 16) & 0xFFFF; + tsp->screen.tx = tsp_scr & 0xFFFF; + + if (tsp->dir.swap_xy) { + tsp->width = tsp_lcd & 0xFFFF; + tsp->height = (tsp_lcd >> 16) & 0xFFFF; + } else { + tsp->width = (tsp_lcd >> 16) & 0xFFFF; + tsp->height = tsp_lcd & 0xFFFF; + } + +#if IST30XX_USE_KEY + tkey->enable = (((tkey_info0 >> 24) & 0xFF) ? true : false); + tkey->key_num = (tkey_info0 >> 16) & 0xFF; + tkey->ch_num[0].tx = tkey_info0 & 0xFF; + tkey->ch_num[0].rx = (tkey_info0 >> 8) & 0xFF; + tkey->ch_num[1].tx = (tkey_info1 >> 16) & 0xFF; + tkey->ch_num[1].rx = (tkey_info1 >> 24) & 0xFF; + tkey->ch_num[2].tx = tkey_info1 & 0xFF; + tkey->ch_num[2].rx = (tkey_info1 >> 8) & 0xFF; + tkey->ch_num[3].tx = (tkey_info2 >> 16) & 0xFF; + tkey->ch_num[3].rx = (tkey_info2 >> 24) & 0xFF; + tkey->ch_num[4].tx = tkey_info2 & 0xFF; + tkey->ch_num[4].rx = (tkey_info2 >> 8) & 0xFF; + + tkey->baseline = (baseline >> 16) & 0xFFFF; +#endif + + return ret; +} + +int ist30xx_get_tsp_info(struct ist30xx_data *data) +{ + int ret = 0; + int retry = 3; +#if IST30XX_INTERNAL_BIN + TSP_INFO *tsp = &data->tsp_info; +#if IST30XX_USE_KEY + TKEY_INFO *tkey = &data->tkey_info; +#endif + u8 *cfg_buf; +#endif + + while (retry--) { + ret = ist30xx_tsp_update_info(data); + if (ret == 0) { + tsp_info("tsp update info success!\n"); + return ret; + } + } + +#if IST30XX_INTERNAL_BIN + cfg_buf = (u8 *)&data->fw.buf[data->tags.cfg_addr]; + + tsp->ch_num.rx = (u8)cfg_buf[0x0C]; + tsp->ch_num.tx = (u8)cfg_buf[0x0D]; + tsp->node.len = tsp->ch_num.tx * tsp->ch_num.rx; + + tsp->gtx.num = (u8)cfg_buf[0x10]; + tsp->gtx.ch_num[0] = (u8)cfg_buf[0x11]; + tsp->gtx.ch_num[1] = (u8)cfg_buf[0x12]; + tsp->gtx.ch_num[2] = (u8)cfg_buf[0x13]; + tsp->gtx.ch_num[3] = (u8)cfg_buf[0x14]; + + tsp->finger_num = (u8)cfg_buf[0x1A]; + tsp->dir.swap_xy = (bool)(cfg_buf[0x1B] & TSP_INFO_SWAP_XY ? true : false); + tsp->dir.flip_x = (bool)(cfg_buf[0x1B] & TSP_INFO_FLIP_X ? true : false); + tsp->dir.flip_y = (bool)(cfg_buf[0x1B] & TSP_INFO_FLIP_Y ? true : false); + + tsp->baseline = (u16)((cfg_buf[0x31] << 8) | cfg_buf[0x30]); + + tsp->screen.rx = (u8)cfg_buf[0x0E]; + tsp->screen.tx = (u8)cfg_buf[0x0F]; + + if (tsp->dir.swap_xy) { + tsp->width = (u16)((cfg_buf[0x19] << 8) | cfg_buf[0x18]); + tsp->height = (u16)((cfg_buf[0x17] << 8) | cfg_buf[0x16]); + } else { + tsp->width = (u16)((cfg_buf[0x17] << 8) | cfg_buf[0x16]); + tsp->height = (u16)((cfg_buf[0x19] << 8) | cfg_buf[0x18]); + } + +#if IST30XX_USE_KEY + tkey->enable = (bool)(cfg_buf[0x3A] & 1); + tkey->key_num = (u8)cfg_buf[0x3B]; + tkey->ch_num[0].tx = (u8)cfg_buf[0x3E]; + tkey->ch_num[0].rx = (u8)cfg_buf[0x43]; + tkey->ch_num[1].tx = (u8)cfg_buf[0x3F]; + tkey->ch_num[1].rx = (u8)cfg_buf[0x44]; + tkey->ch_num[2].tx = (u8)cfg_buf[0x40]; + tkey->ch_num[2].rx = (u8)cfg_buf[0x45]; + tkey->ch_num[3].tx = (u8)cfg_buf[0x41]; + tkey->ch_num[3].rx = (u8)cfg_buf[0x46]; + tkey->ch_num[4].tx = (u8)cfg_buf[0x42]; + tkey->ch_num[4].rx = (u8)cfg_buf[0x47]; + + tkey->baseline = (u16)((cfg_buf[0x3D] << 8) | cfg_buf[0x3C]); +#endif +#endif + + return ret; +} + +void ist30xx_print_info(struct ist30xx_data *data) +{ + TSP_INFO *tsp = &data->tsp_info; + TKEY_INFO *tkey = &data->tkey_info; + + tsp_info("*** TSP/TKEY info ***\n"); + tsp_info("TSP info: \n"); + tsp_info(" finger num: %d\n", tsp->finger_num); + tsp_info(" dir swap: %d, flip x: %d, y: %d\n", + tsp->dir.swap_xy, tsp->dir.flip_x, tsp->dir.flip_y); + tsp_info(" baseline: %d\n", tsp->baseline); + tsp_info(" ch_num tx: %d, rx: %d\n", tsp->ch_num.tx, tsp->ch_num.rx); + tsp_info(" screen tx: %d, rx: %d\n", tsp->screen.tx, tsp->screen.rx); + tsp_info(" width: %d, height: %d\n", tsp->width, tsp->height); + tsp_info(" gtx num: %d, ch [1]: %d, [2]: %d, [3]: %d, [4]: %d\n", + tsp->gtx.num, tsp->gtx.ch_num[0], tsp->gtx.ch_num[1], + tsp->gtx.ch_num[2], tsp->gtx.ch_num[3]); + tsp_info(" node len: %d\n", tsp->node.len); + tsp_info("TKEY info: \n"); + tsp_info(" enable: %d, key num: %d\n", tkey->enable, tkey->key_num); + tsp_info(" ch [0]: %d,%d [1]: %d,%d [2]: %d,%d [3]: %d,%d [4]: %d,%d\n", + tkey->ch_num[0].tx, tkey->ch_num[0].rx, tkey->ch_num[1].tx, + tkey->ch_num[1].rx, tkey->ch_num[2].tx, tkey->ch_num[2].rx, + tkey->ch_num[3].tx, tkey->ch_num[3].rx, tkey->ch_num[4].tx, + tkey->ch_num[4].rx); + tsp_info(" baseline : %d\n", tkey->baseline); +} + +#define update_next_step(ret) { if (unlikely(ret)) goto end; } +int ist30xx_fw_update(struct ist30xx_data *data, const u8 *buf, int size) +{ + int ret = 0; + u32 chksum = 0; + struct ist30xx_fw *fw = &data->fw; + + tsp_info("*** Firmware update ***\n"); + tsp_info(" main: %x, fw: %x, test: %x, core: %x(addr: 0x%x ~ 0x%x)\n", + fw->bin.main_ver, fw->bin.fw_ver, fw->bin.test_ver, fw->bin.core_ver, + fw->index, (fw->index + fw->size)); + + data->status.update = 1; + data->status.update_result = 0; + + ist30xx_disable_irq(data); + + ret = ist30xxc_isp_fw_update(data, buf); + update_next_step(ret); + + ret = ist30xx_read_cmd(data, eHCOM_GET_CRC32, &chksum); + if (unlikely((ret) || (chksum != fw->chksum))) { + if (unlikely(ret)) + ist30xx_reset(data, false); + + goto end; + } + + ret = ist30xx_get_ver_info(data); + update_next_step(ret); + +end: + if (unlikely(ret)) { + data->status.update_result = 1; + tsp_warn("Firmware update Fail!, ret=%d\n", ret); + } else if (unlikely(chksum != fw->chksum)) { + data->status.update_result = 1; + tsp_warn("Error CheckSum: %x(%x)\n", chksum, fw->chksum); + ret = -ENOEXEC; + } + + ist30xx_enable_irq(data); + + data->status.update = 2; + + return ret; +} + +int ist30xx_fw_recovery(struct ist30xx_data *data) +{ + int ret = -EPERM; + u8 *fw = data->fw.buf; + int fw_size = data->fw.buf_size; + + ret = ist30xx_get_update_info(data, fw, fw_size); + if (ret) + return ret; + + data->fw.bin.main_ver = ist30xx_parse_ver(data, FLAG_MAIN, fw); + data->fw.bin.fw_ver = ist30xx_parse_ver(data, FLAG_FW, fw); + data->fw.bin.test_ver = ist30xx_parse_ver(data, FLAG_TEST, fw); + data->fw.bin.core_ver = ist30xx_parse_ver(data, FLAG_CORE, fw); + + mutex_lock(&ist30xx_mutex); + ret = ist30xx_fw_update(data, fw, fw_size); + if (ret == 0) + ist30xx_calibrate(data, 1); + mutex_unlock(&ist30xx_mutex); + + ist30xx_start(data); + + 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, eHCOM_GET_CRC32, &chksum); + if (unlikely(ret)) { + ist30xx_reset(data, false); + 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) +{ +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) + 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_TAEYANG: + case TSP_TYPE_TOVIS: + case TSP_TYPE_ELK: + case TSP_TYPE_OTHERS: + return true; + default: + return false; + } +#else + if (tsp_vendor < TSP_TYPE_NO) + return true; +#endif + + return false; +} + +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) +#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_ILJIN: + str = "ALPS/ILJIN"; + fw->buf = (u8 *)ist30xxc_fw; + fw->buf_size = sizeof(ist30xxc_fw); + break; + case TSP_TYPE_EELY: + str = "EELY"; + fw->buf = (u8 *)ist30xxc_fw2; + fw->buf_size = sizeof(ist30xxc_fw2); + 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); +} +#endif // IST30XX_MULTIPLE_TSP +#endif // (IMAGIS_TSP_IC < IMAGIS_IST3038C) + +int ist30xx_check_auto_update(struct ist30xx_data *data) +{ + int ret = 0; + int retry = IST30XX_MAX_RETRY_CNT; + u32 tsp_type = TSP_TYPE_UNKNOWN; + bool tsp_check = false; + u32 chksum; + struct ist30xx_fw *fw = &data->fw; + + while (retry--) { + ret = ist30xx_read_cmd(data, eHCOM_GET_TSP_VENDOR, &tsp_type); + if (likely(ret == 0)) { + if (likely(ist30xx_check_valid_vendor(tsp_type) == true)) + tsp_check = true; + break; + } + + ist30xx_reset(data, false); + } +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) + tsp_info("TSP vendor: %x\n", tsp_type); +#endif + + if (unlikely(!tsp_check)) + goto fw_check_end; + + ist30xx_get_ver_info(data); + + if (likely((fw->cur.fw_ver > 0) && (fw->cur.fw_ver < 0xFFFFFFFF))) { + tsp_info("Version compare IC: %x(%x), BIN: %x(%x)\n", fw->cur.fw_ver, + fw->cur.main_ver, fw->bin.fw_ver, fw->bin.main_ver); + + /* If FW version is same, check FW checksum */ + if (likely((fw->cur.main_ver == fw->bin.main_ver) && + (fw->cur.fw_ver == fw->bin.fw_ver) && + (fw->cur.test_ver == 0))) { + ret = ist30xx_read_cmd(data, eHCOM_GET_CRC32, &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->cur.main_ver : Main version in TSP IC + * fw->cur.fw_ver : FW version if TSP IC + * fw->bin.main_ver : Main version in FW Binary + * fw->bin.fw_ver : FW version in FW Binary + */ + /* If the ver of binary is higher than ver of IC, FW update operate. */ + + if (likely((fw->cur.main_ver >= fw->bin.main_ver) && + (fw->cur.fw_ver >= fw->bin.fw_ver))) + return 0; + } + +fw_check_end: + return -EAGAIN; +} + +int ist30xx_auto_bin_update(struct ist30xx_data *data) +{ + int ret = 0; + int retry = IST30XX_MAX_RETRY_CNT; + struct ist30xx_fw *fw = &data->fw; + + fw->buf = (u8 *)ist30xxc_fw; + fw->buf_size = sizeof(ist30xxc_fw); + +#if (IMAGIS_TSP_IC < IMAGIS_IST3038C) +#if IST30XX_MULTIPLE_TSP + ist30xx_set_tsp_fw(data); +#endif +#endif + + ret = ist30xx_get_update_info(data, fw->buf, fw->buf_size); + if (unlikely(ret)) + return 1; + fw->bin.main_ver = ist30xx_parse_ver(data, FLAG_MAIN, fw->buf); + fw->bin.fw_ver = ist30xx_parse_ver(data, FLAG_FW, fw->buf); + fw->bin.test_ver = ist30xx_parse_ver(data, FLAG_TEST, fw->buf); + fw->bin.core_ver = ist30xx_parse_ver(data, FLAG_CORE, fw->buf); + + tsp_info("IC: %x, Binary ver main: %x, fw: %x, test: %x, core: %x\n", + data->chip_id, fw->bin.main_ver, fw->bin.fw_ver, fw->bin.test_ver, + fw->bin.core_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. fw(main, test, core): %x(%x, %x, %x) -> %x(%x, %x, %x)\n", + fw->cur.fw_ver, fw->cur.main_ver, fw->cur.test_ver, fw->cur.core_ver, + fw->bin.fw_ver, fw->bin.main_ver, fw->bin.test_ver, fw->bin.core_ver); + + mutex_lock(&ist30xx_mutex); + while (retry--) { + ret = ist30xx_fw_update(data, fw->buf, fw->buf_size); + 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; + + mutex_lock(&ist30xx_mutex); + ist30xx_calibrate(data, IST30XX_MAX_RETRY_CNT); + mutex_unlock(&ist30xx_mutex); + + return ret; +} +#endif // IST30XX_INTERNAL_BIN + +#define MAX_FILE_PATH 255 +/* sysfs: /sys/class/touch/firmware/firmware */ +ssize_t ist30xx_fw_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + int fw_size = 0; + u8 *fw = NULL; + const u8 *buff = 0; + mm_segment_t old_fs = { 0 }; + struct file *fp = NULL; + long fsize = 0, nread = 0; + char fw_path[MAX_FILE_PATH]; + const struct firmware *request_fw = NULL; + int mode = 0; + int calib = 1; + struct ist30xx_data *data = dev_get_drvdata(dev); + + sscanf(buf, "%d %d", &mode, &calib); + + switch (mode) { + case MASK_UPDATE_INTERNAL: +#if IST30XX_INTERNAL_BIN + fw = data->fw.buf; + fw_size = data->fw.buf_size; +#else + tsp_warn("Not support internal bin!!\n"); + return size; +#endif + break; + + case MASK_UPDATE_FW: + ret = request_firmware(&request_fw, IST30XX_FW_NAME, + &data->client->dev); + if (ret) { + tsp_warn("File not found, %s\n", IST30XX_FW_NAME); + return size; + } + + fw = (u8 *)request_fw->data; + fw_size = (u32)request_fw->size; + tsp_info("firmware is loaded!!\n"); + break; + + case MASK_UPDATE_SDCARD: + old_fs = get_fs(); + set_fs(get_ds()); + + snprintf(fw_path, MAX_FILE_PATH, "/sdcard/%s", + IST30XX_FW_NAME); + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + tsp_info("file %s open error:%d\n", fw_path, (s32)fp); + goto err_file_open; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + + buff = kzalloc((size_t)fsize, GFP_KERNEL); + if (!buff) { + tsp_info("fail to alloc buffer\n"); + goto err_alloc; + } + + nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos); + if (nread != fsize) { + tsp_info("mismatch fw size\n"); + goto err_fw_size; + } + + fw = (u8 *)buff; + fw_size = (u32)fsize; + + filp_close(fp, current->files); + tsp_info("firmware is loaded!!\n"); + break; + + case MASK_UPDATE_ERASE: + tsp_info("EEPROM all erase!!\n"); + + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(data); + ist30xx_reset(data, true); + ist30xxc_isp_enable(data->client, true); + ist30xxc_isp_erase(data->client, IST30XX_ISP_ERASE_BLOCK, 0); +#if (IST30XX_FLASH_INFO_SIZE > 0) + ist30xxc_isp_erase(data->client, IST30XX_ISP_ERASE_INFO, 0); +#endif + ist30xxc_isp_enable(data->client, false); + ist30xx_reset(data, false); + ist30xx_start(data); + ist30xx_enable_irq(data); + mutex_unlock(&ist30xx_mutex); + + default: + return size; + } + + ret = ist30xx_get_update_info(data, fw, fw_size); + if (ret) + goto err_get_info; + + data->fw.bin.main_ver = ist30xx_parse_ver(data, FLAG_MAIN, fw); + data->fw.bin.fw_ver = ist30xx_parse_ver(data, FLAG_FW, fw); + data->fw.bin.test_ver = ist30xx_parse_ver(data, FLAG_TEST, fw); + data->fw.bin.core_ver = ist30xx_parse_ver(data, FLAG_CORE, fw); + + mutex_lock(&ist30xx_mutex); + ist30xx_fw_update(data, fw, fw_size); + + if (calib) + ist30xx_calibrate(data, 1); + mutex_unlock(&ist30xx_mutex); + + ist30xx_start(data); + +err_get_info: + if (request_fw != NULL) + release_firmware(request_fw); + + if (fp != NULL) { +err_fw_size: + kfree(buff); +err_alloc: + filp_close(fp, NULL); +err_file_open: + set_fs(old_fs); + } + + return size; +} + +/* sysfs: /sys/class/touch/firmware/fw_sdcard */ +ssize_t ist30xx_fw_sdcard_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + int fw_size = 0; + u8 *fw = NULL; + const u8 *buff = 0; + mm_segment_t old_fs = { 0 }; + struct file *fp = NULL; + long fsize = 0, nread = 0; + char fw_path[MAX_FILE_PATH]; + struct ist30xx_data *data = dev_get_drvdata(dev); + + old_fs = get_fs(); + set_fs(get_ds()); + + snprintf(fw_path, MAX_FILE_PATH, "/sdcard/%s", + IST30XX_FW_NAME); + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + tsp_info("file %s open error:%d\n", fw_path, (s32)fp); + goto err_file_open; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + + buff = kzalloc((size_t)fsize, GFP_KERNEL); + if (!buff) { + tsp_info("fail to alloc buffer\n"); + goto err_alloc; + } + + nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos); + if (nread != fsize) { + tsp_info("mismatch fw size\n"); + goto err_fw_size; + } + + fw = (u8 *)buff; + fw_size = (u32)fsize; + + filp_close(fp, current->files); + tsp_info("firmware is loaded!!\n"); + + ret = ist30xx_get_update_info(data, fw, fw_size); + if (ret) + goto err_get_info; + data->fw.bin.main_ver = ist30xx_parse_ver(data, FLAG_MAIN, fw); + data->fw.bin.fw_ver = ist30xx_parse_ver(data, FLAG_FW, fw); + data->fw.bin.test_ver = ist30xx_parse_ver(data, FLAG_TEST, fw); + data->fw.bin.core_ver = ist30xx_parse_ver(data, FLAG_CORE, fw); + + mutex_lock(&ist30xx_mutex); + ist30xx_fw_update(data, fw, fw_size); + mutex_unlock(&ist30xx_mutex); + + ist30xx_start(data); + +err_get_info: +err_fw_size: + if (buff) + kfree(buff); +err_alloc: + if (fp) + filp_close(fp, NULL); +err_file_open: + set_fs(old_fs); + + return 0; +} + +/* sysfs: /sys/class/touch/firmware/firmware */ +ssize_t ist30xx_fw_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count; + struct ist30xx_data *data = dev_get_drvdata(dev); + + switch (data->status.update) { + case 1: + count = sprintf(buf, "Downloading\n"); + break; + case 2: + if (data->status.update_result) { + count = sprintf(buf, "Update fail\n"); + } else { + count = sprintf(buf, "Update success, ver %x(%x, %x, %x)-> %x(%x, %x, %x), " + "status : %d, gap : %d\n", + data->fw.prev.fw_ver, data->fw.prev.main_ver, + data->fw.prev.test_ver, data->fw.prev.core_ver, + data->fw.cur.fw_ver, data->fw.cur.main_ver, + data->fw.cur.test_ver, data->fw.cur.core_ver, + CALIB_TO_STATUS(data->status.calib_msg), + CALIB_TO_GAP(data->status.calib_msg)); + } + break; + default: + count = sprintf(buf, "Pass\n"); + } + + return count; +} + +/* sysfs: /sys/class/touch/firmware/fw_read */ +u32 buf32_flash[IST30XX_FLASH_TOTAL_SIZE / IST30XX_DATA_LEN]; +ssize_t ist30xx_fw_read_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int i; + mm_segment_t old_fs = { 0 }; + struct file *fp = NULL; + char fw_path[MAX_FILE_PATH]; + u8 *buf8 = (u8 *)buf32_flash; + struct ist30xx_data *data = dev_get_drvdata(dev); + + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(data); + + ist30xxc_isp_fw_read(data, buf32_flash); + for (i = 0; i < IST30XX_FLASH_TOTAL_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]); + } + + old_fs = get_fs(); + set_fs(get_ds()); + + snprintf(fw_path, MAX_FILE_PATH, "/sdcard/%s", IST30XX_BIN_NAME); + fp = filp_open(fw_path, O_CREAT|O_WRONLY|O_TRUNC, 0); + if (IS_ERR(fp)) { + tsp_info("file %s open error:%d\n", fw_path, (s32)fp); + goto err_file_open; + } + + fp->f_op->write(fp, buf8, IST30XX_FLASH_TOTAL_SIZE, &fp->f_pos); + fput(fp); + + filp_close(fp, NULL); + set_fs(old_fs); + +err_file_open: + ist30xx_enable_irq(data); + mutex_unlock(&ist30xx_mutex); + + ist30xx_start(data); + + return 0; +} + +/* sysfs: /sys/class/touch/firmware/version */ +ssize_t ist30xx_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count; + struct ist30xx_data *data = dev_get_drvdata(dev); + + count = sprintf(buf, "ID: %x, main: %x, fw: %x, test: %x, core: %x\n", + data->chip_id, data->fw.cur.main_ver, data->fw.cur.fw_ver, + data->fw.cur.test_ver, data->fw.cur.core_ver); + +#if IST30XX_INTERNAL_BIN + { + char msg[128]; + + ist30xx_get_update_info(data, data->fw.buf, data->fw.buf_size); + + count += snprintf(msg, sizeof(msg), + " Header - main: %x, fw: %x, test: %x, core: %x\n", + ist30xx_parse_ver(data, FLAG_MAIN, data->fw.buf), + ist30xx_parse_ver(data, FLAG_FW, data->fw.buf), + ist30xx_parse_ver(data, FLAG_TEST, data->fw.buf), + ist30xx_parse_ver(data, FLAG_CORE, data->fw.buf)); + strncat(buf, msg, sizeof(msg)); + } +#endif + + return count; +} + + +/* sysfs */ +static DEVICE_ATTR(fw_read, S_IRWXUGO, ist30xx_fw_read_show, NULL); +static DEVICE_ATTR(firmware, S_IRWXUGO, ist30xx_fw_status_show, + ist30xx_fw_store); +static DEVICE_ATTR(fw_sdcard, S_IRWXUGO, ist30xx_fw_sdcard_show, NULL); +static DEVICE_ATTR(version, S_IRWXUGO, ist30xx_fw_version_show, NULL); + +struct class *ist30xx_class; +struct device *ist30xx_fw_dev; + +static struct attribute *fw_attributes[] = { + &dev_attr_fw_read.attr, + &dev_attr_firmware.attr, + &dev_attr_fw_sdcard.attr, + &dev_attr_version.attr, + NULL, +}; + +static struct attribute_group fw_attr_group = { + .attrs = fw_attributes, +}; + +int ist30xx_init_update_sysfs(struct ist30xx_data *data) +{ + /* /sys/class/touch */ + ist30xx_class = class_create(THIS_MODULE, "touch"); + + /* /sys/class/touch/firmware */ + ist30xx_fw_dev = device_create(ist30xx_class, NULL, 0, data, "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"); + + data->status.update = 0; + data->status.calib = 0; + data->status.update_result = 0; + + return 0; +} |
