From b5b1a6a0eeba0eccd0c600f6ae2e047ebacc2411 Mon Sep 17 00:00:00 2001 From: Super Liu Date: Fri, 6 Sep 2019 09:08:27 +0800 Subject: input: touchscreen: sec_touch: initial sec_touch driver initial driver fork from branch android-msm-pixel-4.9 with ac4cba46cdb9 input: touchscreen: sec_ts: Updates usages of timestamp to use ktime_t Bug: 140591953 Signed-off-by: Super Liu Change-Id: Ib4cca3592961082baf41eede1cb4431d873fb0e6 --- sec_ts_fw.c | 1264 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1264 insertions(+) create mode 100644 sec_ts_fw.c (limited to 'sec_ts_fw.c') diff --git a/sec_ts_fw.c b/sec_ts_fw.c new file mode 100644 index 0000000..efd9152 --- /dev/null +++ b/sec_ts_fw.c @@ -0,0 +1,1264 @@ +/* drivers/input/touchscreen/sec_ts_fw.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "sec_ts.h" + +#define SEC_TS_FW_BLK_SIZE 256 + +enum { + BUILT_IN = 0, + UMS, + BL, + FFU, +}; + +typedef struct { + u32 signature; /* signature */ + u32 version; /* version */ + u32 totalsize; /* total size */ + u32 checksum; /* checksum */ + u32 img_ver; /* image file version */ + u32 img_date; /* image file date */ + u32 img_description; /* image file description */ + u32 fw_ver; /* firmware version */ + u32 fw_date; /* firmware date */ + u32 fw_description; /* firmware description */ + u32 para_ver; /* parameter version */ + u32 para_date; /* parameter date */ + u32 para_description; /* parameter description */ + u32 num_chunk; /* number of chunk */ + u32 reserved1; + u32 reserved2; +} fw_header; + +typedef struct { + u32 signature; + u32 addr; + u32 size; + u32 reserved; +} fw_chunk; + +static int sec_ts_enter_fw_mode(struct sec_ts_data *ts) +{ + int ret; + u8 fw_update_mode_passwd[] = {0x55, 0xAC}; + u8 fw_status; + u8 id[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd, sizeof(fw_update_mode_passwd)); + sec_ts_delay(20); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, enter_fw_mode\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: write ok, enter_fw_mode - 0x%x 0x%x 0x%x\n", + __func__, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd[0], fw_update_mode_passwd[1]); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status\n", __func__); + return 0; + } + if (fw_status != SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, "%s: enter fail! read_boot_status = 0x%x\n", __func__, fw_status); + return 0; + } + + input_info(true, &ts->client->dev, "%s: Success! read_boot_status = 0x%x\n", __func__, fw_status); + + sec_ts_delay(10); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, id, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read id fail\n", __func__); + return 0; + } + + ts->boot_ver[0] = id[0]; + ts->boot_ver[1] = id[1]; + ts->boot_ver[2] = id[2]; + + ts->flash_page_size = SEC_TS_FW_BLK_SIZE_DEFAULT; + if ((ts->boot_ver[1] == 0x37) && (ts->boot_ver[2] == 0x61)) + ts->flash_page_size = 512; + + input_info(true, &ts->client->dev, "%s: read_boot_id = %02X%02X%02X\n", __func__, id[0], id[1], id[2]); + + return 1; +} + +int sec_ts_hw_reset(struct sec_ts_data *ts) +{ + int reset_gpio = ts->plat_data->reset_gpio; + + if (!gpio_is_valid(reset_gpio)) { + input_err(true, &ts->client->dev, "%s: invalid gpio %d\n", + __func__, reset_gpio); + return -EINVAL; + } + + gpio_set_value(reset_gpio, 0); + sec_ts_delay(10); + gpio_set_value(reset_gpio, 1); + + return 0; +} + +int sec_ts_sw_reset(struct sec_ts_data *ts) +{ + int ret; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, sw_reset\n", __func__); + return 0; + } + + sec_ts_delay(100); + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: time out\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: sw_reset\n", __func__); + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return 0; + } + + return ret; +} + +int sec_ts_system_reset(struct sec_ts_data *ts) +{ + int ret = -1; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: write fail, sw_reset\n", + __func__); + else { + sec_ts_delay(100); + /* Normally it should not happen with any retry. + * But, if happened, retry less time to wait ack + */ + ret = sec_ts_wait_for_ready_with_count(ts, + SEC_TS_ACK_BOOT_COMPLETE, 10); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: sw_reset time out!\n", __func__); + else + input_info(true, + &ts->client->dev, "%s: sw_reset done\n", + __func__); + } + + if (ret < 0) { + if (!gpio_is_valid(ts->plat_data->reset_gpio)) { + input_err(true, &ts->client->dev, + "%s: reset gpio is unavailable!\n", __func__); + return ret; + } + + input_err(true, &ts->client->dev, + "%s: sw_reset failed or time out, try hw_reset to recover!\n", + __func__); + ret = sec_ts_hw_reset(ts); + if (ret) { + input_err(true, &ts->client->dev, + "%s: hw_reset failed\n", __func__); + return ret; + } + + ret = sec_ts_wait_for_ready_with_count(ts, + SEC_TS_ACK_BOOT_COMPLETE, 10); + if (ret < 0) { + input_err(true, + &ts->client->dev, "%s: hw_reset time out\n", + __func__); + return ret; + } + input_info(true, &ts->client->dev, "%s: hw_reset done\n", + __func__); + } + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", + __func__); + return ret; + } + + return 0; +} + +static void sec_ts_save_version_of_bin(struct sec_ts_data *ts, const fw_header *fw_hd) +{ + ts->plat_data->img_version_of_bin[3] = ((fw_hd->img_ver >> 24) & 0xff); + ts->plat_data->img_version_of_bin[2] = ((fw_hd->img_ver >> 16) & 0xff); + ts->plat_data->img_version_of_bin[1] = ((fw_hd->img_ver >> 8) & 0xff); + ts->plat_data->img_version_of_bin[0] = ((fw_hd->img_ver >> 0) & 0xff); + + ts->plat_data->core_version_of_bin[3] = ((fw_hd->fw_ver >> 24) & 0xff); + ts->plat_data->core_version_of_bin[2] = ((fw_hd->fw_ver >> 16) & 0xff); + ts->plat_data->core_version_of_bin[1] = ((fw_hd->fw_ver >> 8) & 0xff); + ts->plat_data->core_version_of_bin[0] = ((fw_hd->fw_ver >> 0) & 0xff); + + ts->plat_data->config_version_of_bin[3] = ((fw_hd->para_ver >> 24) & 0xff); + ts->plat_data->config_version_of_bin[2] = ((fw_hd->para_ver >> 16) & 0xff); + ts->plat_data->config_version_of_bin[1] = ((fw_hd->para_ver >> 8) & 0xff); + ts->plat_data->config_version_of_bin[0] = ((fw_hd->para_ver >> 0) & 0xff); + + input_info(true, &ts->client->dev, "%s: img_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->img_version_of_bin[0], + ts->plat_data->img_version_of_bin[1], + ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: core_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->core_version_of_bin[0], + ts->plat_data->core_version_of_bin[1], + ts->plat_data->core_version_of_bin[2], + ts->plat_data->core_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: config_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->config_version_of_bin[0], + ts->plat_data->config_version_of_bin[1], + ts->plat_data->config_version_of_bin[2], + ts->plat_data->config_version_of_bin[3]); +} + +static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) +{ + u8 img_ver[4] = {0,}; + u8 core_ver[4] = {0,}; + u8 config_ver[4] = {0,}; + int ret; + + /* Image ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, img_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Image version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Image version info : %x.%x.%x.%x\n", + __func__, img_ver[0], img_ver[1], img_ver[2], img_ver[3]); + + ts->plat_data->img_version_of_ic[0] = img_ver[0]; + ts->plat_data->img_version_of_ic[1] = img_ver[1]; + ts->plat_data->img_version_of_ic[2] = img_ver[2]; + ts->plat_data->img_version_of_ic[3] = img_ver[3]; + + /* Core ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FW_VERSION, core_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: core version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Core version info : %x.%x.%x.%x,\n", + __func__, core_ver[0], core_ver[1], core_ver[2], core_ver[3]); + + ts->plat_data->core_version_of_ic[0] = core_ver[0]; + ts->plat_data->core_version_of_ic[1] = core_ver[1]; + ts->plat_data->core_version_of_ic[2] = core_ver[2]; + ts->plat_data->core_version_of_ic[3] = core_ver[3]; + + /* Config ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_PARA_VERSION, config_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: config version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC config version info : %x.%x.%x.%x\n", + __func__, config_ver[0], config_ver[1], config_ver[2], config_ver[3]); + + ts->plat_data->config_version_of_ic[0] = config_ver[0]; + ts->plat_data->config_version_of_ic[1] = config_ver[1]; + ts->plat_data->config_version_of_ic[2] = config_ver[2]; + ts->plat_data->config_version_of_ic[3] = config_ver[3]; + + return 1; +} + +static int sec_ts_check_firmware_version(struct sec_ts_data *ts, const u8 *fw_info) +{ + fw_header *fw_hd; + u8 buff[1]; + int i; + int ret; + /* + * sec_ts_check_firmware_version + * return value = 1 : firmware download needed, + * return value = 0 : skip firmware download + */ + + fw_hd = (fw_header *)fw_info; + + sec_ts_save_version_of_bin(ts, fw_hd); + + /* firmware download if READ_BOOT_STATUS = 0x10 */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, buff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to read BootStatus\n", __func__); + return -EIO; + } + + if (buff[0] == SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, + "%s: ReadBootStatus = 0x%x, Firmware download Start!\n", + __func__, buff[0]); + return 1; + } + + ret = sec_ts_save_version_of_ic(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to read ic version\n", __func__); + return -EIO; + } + + /* check f/w version + * ver[0] : IC version + * ver[1] : Project version + */ + for (i = 0; i < 2; i++) { + if (ts->plat_data->img_version_of_ic[i] != ts->plat_data->img_version_of_bin[i]) { + input_err(true, &ts->client->dev, "%s: do not matched version info\n", __func__); + return 0; + } + } + + for (i = 2; i < 4; i++) { + if (ts->plat_data->img_version_of_ic[i] != + ts->plat_data->img_version_of_bin[i]) + return 1; + } + + return 0; +} + +static u8 sec_ts_checksum(u8 *data, int offset, int size) +{ + int i; + u8 checksum = 0; + + for (i = 0; i < size; i++) + checksum += data[i + offset]; + + return checksum; +} + +static int sec_ts_flashpageerase(struct sec_ts_data *ts, u32 page_idx, u32 page_num) +{ + int ret; + u8 tCmd[6]; + + tCmd[0] = SEC_TS_CMD_FLASH_ERASE; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + tCmd[3] = (u8)((page_num >> 8) & 0xFF); + tCmd[4] = (u8)((page_num >> 0) & 0xFF); + tCmd[5] = sec_ts_checksum(tCmd, 1, 4); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 6); + + return ret; +} + +static int sec_ts_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret; + u8 tCmd[1 + 2 + SEC_TS_FW_BLK_SIZE_MAX + 1]; + int flash_page_size = (int)ts->flash_page_size; + + tCmd[0] = 0xD9; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + + memcpy(&tCmd[3], page_data, flash_page_size); + tCmd[1 + 2 + flash_page_size] = sec_ts_checksum(tCmd, 1, 2 + flash_page_size); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 1 + 2 + flash_page_size + 1); + return ret; +} + +static bool sec_ts_limited_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret = 0; + u8 *tCmd; + u8 copy_data[3 + SEC_TS_FW_BLK_SIZE_MAX]; + int copy_left = (int)ts->flash_page_size + 3; + int copy_size = 0; + int copy_max = ts->i2c_burstmax - 1; + int flash_page_size = (int)ts->flash_page_size; + + copy_data[0] = (u8)((page_idx >> 8) & 0xFF); /* addH */ + copy_data[1] = (u8)((page_idx >> 0) & 0xFF); /* addL */ + + memcpy(©_data[2], page_data, flash_page_size); /* DATA */ + copy_data[2 + flash_page_size] = sec_ts_checksum(copy_data, 0, 2 + flash_page_size); /* CS */ + + while (copy_left > 0) { + int copy_cur = (copy_left > copy_max) ? copy_max : copy_left; + + tCmd = kzalloc(copy_cur + 1, GFP_KERNEL); + if (!tCmd) + goto err_write; + + if (copy_size == 0) + tCmd[0] = SEC_TS_CMD_FLASH_WRITE; + else + tCmd[0] = SEC_TS_CMD_FLASH_PADDING; + + memcpy(&tCmd[1], ©_data[copy_size], copy_cur); + + ret = ts->sec_ts_i2c_write_burst_heap(ts, tCmd, 1 + copy_cur); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed, ret:%d\n", __func__, ret); + + copy_size += copy_cur; + copy_left -= copy_cur; + kfree(tCmd); + } + return ret; + +err_write: + input_err(true, &ts->client->dev, + "%s: failed to alloc.\n", __func__); + return -ENOMEM; + +} + +static int sec_ts_flashwrite(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size, int retry) +{ + int ret; + u32 page_idx; + u32 size_copy; + u32 flash_page_size; + u32 page_idx_start; + u32 page_idx_end; + u32 page_num; + u8 page_buf[SEC_TS_FW_BLK_SIZE_MAX]; + + if (mem_size == 0) + return 0; + + flash_page_size = ts->flash_page_size; + page_idx_start = mem_addr / flash_page_size; + page_idx_end = (mem_addr + mem_size - 1) / flash_page_size; + page_num = page_idx_end - page_idx_start + 1; + + ret = sec_ts_flashpageerase(ts, page_idx_start, page_num); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fw erase failed, mem_addr= %08X, pagenum = %d\n", + __func__, mem_addr, page_num); + return -EIO; + } + + sec_ts_delay(page_num + 10); + + size_copy = mem_size % flash_page_size; + if (size_copy == 0) + size_copy = flash_page_size; + + memset(page_buf, 0, flash_page_size); + + for (page_idx = page_num - 1;; page_idx--) { + memcpy(page_buf, mem_data + (page_idx * flash_page_size), size_copy); + if (ts->boot_ver[0] == 0xB2) { + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + } else { + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + + } + + size_copy = flash_page_size; + sec_ts_delay(5); + + if (page_idx == 0) /* end condition (page_idx >= 0) page_idx type unsinged int */ + break; + } + + return mem_size; +err: + return -EIO; +} + +static int sec_ts_memoryblockread(struct sec_ts_data *ts, u32 mem_addr, int mem_size, u8 *buf) +{ + int ret; + u8 cmd[5]; + u8 *data; + + if (mem_size >= 64 * 1024) { + input_err(true, &ts->client->dev, + "%s: mem size over 64K\n", __func__); + return -EIO; + } + + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_ADDR; + cmd[1] = (u8)((mem_addr >> 24) & 0xff); + cmd[2] = (u8)((mem_addr >> 16) & 0xff); + cmd[3] = (u8)((mem_addr >> 8) & 0xff); + cmd[4] = (u8)((mem_addr >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 5); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_SIZE; + cmd[1] = (u8)((mem_size >> 8) & 0xff); + cmd[2] = (u8)((mem_size >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_DATA; + + data = buf; + + + ret = ts->sec_ts_i2c_read_heap(ts, cmd[0], data, mem_size); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: memory read failed\n", __func__); + return -EIO; + } +/* + ret = ts->sec_ts_i2c_write(ts, cmd[0], NULL, 0); + ret = ts->sec_ts_i2c_read_bulk_heap(ts, data, mem_size); +*/ + return 0; +} + +static int sec_ts_memoryread(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size) +{ + int ret; + int retry = 3; + int read_size = 0; + int unit_size; + int max_size = 1024; + int read_left = (int)mem_size; + u8 *tmp_data; + + tmp_data = kmalloc(max_size, GFP_KERNEL); + if (!tmp_data) { + input_err(true, &ts->client->dev, + "%s: failed to kmalloc\n", __func__); + return -ENOMEM; + } + + while (read_left > 0) { + unit_size = (read_left > max_size) ? max_size : read_left; + retry = 3; + do { + ret = sec_ts_memoryblockread(ts, mem_addr, unit_size, tmp_data); + if (retry-- == 0) { + input_err(true, &ts->client->dev, + "%s: fw read fail mem_addr=%08X,unit_size=%d\n", + __func__, mem_addr, unit_size); + kfree(tmp_data); + return -1; + } + + memcpy(mem_data + read_size, tmp_data, unit_size); + } while (ret < 0); + + mem_addr += unit_size; + read_size += unit_size; + read_left -= unit_size; + } + + kfree(tmp_data); + + return read_size; +} + +static int sec_ts_chunk_update(struct sec_ts_data *ts, u32 addr, u32 size, u8 *data, int retry) +{ + u32 fw_size; + u32 write_size; + u8 *mem_rb; + int ret = 0; + + fw_size = size; + + write_size = sec_ts_flashwrite(ts, addr, data, fw_size, retry); + if (write_size != fw_size) { + input_err(true, &ts->client->dev, "%s: fw write failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + mem_rb = vzalloc(fw_size); + if (!mem_rb) { + input_err(true, &ts->client->dev, "%s: vzalloc failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + if (sec_ts_memoryread(ts, addr, mem_rb, fw_size) >= 0) { + u32 ii; + + for (ii = 0; ii < fw_size; ii++) { + if (data[ii] != mem_rb[ii]) + break; + } + + if (fw_size != ii) { + input_err(true, &ts->client->dev, "%s: fw verify fail\n", __func__); + ret = -1; + goto out; + } + } else { + ret = -1; + goto out; + } + + input_info(true, &ts->client->dev, "%s: verify done(%d)\n", __func__, ret); + +out: + vfree(mem_rb); +err_write_fail: + sec_ts_delay(10); + + return ret; +} + +static int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, size_t size, int bl_update, int restore_cal, int retry) +{ + int i; + int ret; + fw_header *fw_hd; + fw_chunk *fw_ch; + u8 fw_status = 0; + u8 *fd = (u8 *)data; + u8 tBuff[3]; +#ifdef PAT_CONTROL + char buff[SEC_CMD_STR_LEN] = {0}; + u8 img_ver[4]; + bool magic_cal = false; +#endif + + /* Check whether CRC is appended or not. + * Enter Firmware Update Mode + */ + if (!sec_ts_enter_fw_mode(ts)) { + input_err(true, &ts->client->dev, "%s: firmware mode failed\n", __func__); + return -1; + } + + if (bl_update && (ts->boot_ver[0] == 0xB4)) { + input_info(true, &ts->client->dev, "%s: bootloader is up to date\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: firmware update retry :%d\n", __func__, retry); + + fw_hd = (fw_header *)fd; + fd += sizeof(fw_header); + + if (fw_hd->signature != SEC_TS_FW_HEADER_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware header error = %08X\n", __func__, fw_hd->signature); + return -1; + } + + input_err(true, &ts->client->dev, "%s: num_chunk : %d\n", __func__, fw_hd->num_chunk); + + for (i = 0; i < fw_hd->num_chunk; i++) { + fw_ch = (fw_chunk *)fd; + + input_err(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, + fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); + + if (fw_ch->signature != SEC_TS_FW_CHUNK_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware chunk error = %08X\n", __func__, fw_ch->signature); + return -1; + } + fd += sizeof(fw_chunk); + ret = sec_ts_chunk_update(ts, fw_ch->addr, fw_ch->size, fd, retry); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: firmware chunk write failed, addr=%08X, size = %d\n", __func__, fw_ch->addr, fw_ch->size); + return -1; + } + fd += fw_ch->size; + } + + sec_ts_sw_reset(ts); + +#ifdef PAT_CONTROL + if (restore_cal) { + if(ts->plat_data->pat_function == PAT_CONTROL_PAT_MAGIC) { + /* NOT to control cal count that was marked on external factory ( E0~E5 )*/ + if ((ts->cal_count >= PAT_MAGIC_NUMBER) && (ts->cal_count < PAT_MAX_MAGIC)) + magic_cal = true; + } + } + input_info(true, &ts->client->dev, "%s: cal_count(0x%02X) pat_function dt(%d) restore_cal(%d) magic_cal(%d)\n", + __func__,ts->cal_count,ts->plat_data->pat_function,restore_cal,magic_cal); +#endif + + if (!bl_update) { +#ifdef PAT_CONTROL + if ((ts->cal_count == 0) || (ts->cal_count == 0xFF) || (magic_cal == true)) { + input_err(true, &ts->client->dev, "%s: RUN OFFSET CALIBRATION(0x%02X)\n", __func__, ts->cal_count); + + ret = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SEC); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write OFFSET CAL SEC!\n", __func__); + +#ifdef USE_PRESSURE_SENSOR + ret = sec_ts_execute_force_calibration(ts, PRESSURE_CAL); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write PRESSURE CAL!\n", __func__); +#endif + if (ret >= 0 && magic_cal) { + + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + if (ts->cal_count == 0x00 || ts->cal_count == 0xFF) + ts->cal_count = PAT_MAGIC_NUMBER; + else if (PAT_MAGIC_NUMBER <= ts->cal_count && ts->cal_count < PAT_MAX_MAGIC) + ts->cal_count++; + + /* Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stored data - 1 (ex. using 1byte, value is 1 - 1 = 0) + * buff[2] : write data + */ + buff[0] = SEC_TS_NVM_OFFSET_CAL_COUNT; + buff[1] = 0; + buff[2] = ts->cal_count; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 3); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + + sec_ts_delay(20); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + input_info(true, &ts->client->dev, "%s: cal_count = [%02X]\n", + __func__, ts->cal_count); + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, img_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Image version read error\n", __func__); + } else { + memset(buff, 0x00, SEC_CMD_STR_LEN); + buff[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION); + if (buff[0] == 0xFF) { + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION); + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION+1); + } + + ts->tune_fix_ver = (img_ver[2] << 8 | img_ver[3]); + buff[0] = SEC_TS_NVM_OFFSET_TUNE_VERSION; + buff[1] = 1;// 2bytes + buff[2] = img_ver[2]; + buff[3] = img_ver[3]; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: nvm write failed. ret: %d\n", __func__, ret); + } + sec_ts_delay(20); + + buff[0] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION); + buff[1] = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION+1); + ts->tune_fix_ver = buff[0]<<8 | buff[1]; + input_info(true, &ts->client->dev, "%s: tune_fix_ver [%02X %02X]\n", __func__, buff[0], buff[1]); + } + } else { + input_err(true, &ts->client->dev, "%s: DO NOT CALIBRATION(0x%02X)\n", __func__, ts->cal_count); + } +#else + /* always calibration after fw update */ + input_err(true, &ts->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); + + ret = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SEC); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write OFFSET CAL SEC!\n", __func__); + +#ifdef USE_PRESSURE_SENSOR + ret = sec_ts_execute_force_calibration(ts, PRESSURE_CAL); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write PRESSURE CAL!\n", __func__); +#endif +#endif + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return -EIO; + } + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1) < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + if (fw_status != SEC_TS_STATUS_APP_MODE) { + input_err(true, &ts->client->dev, "%s: fw update sequence done, BUT read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + input_info(true, &ts->client->dev, "%s: fw update Success! read_boot_status = 0x%x\n", __func__, fw_status); + + return 1; + } else { + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3) < 0) { + input_err(true, &ts->client->dev, "%s: read device id fail after bl fw download\n", __func__); + return -EIO; + } + + if (tBuff[0] == 0xA0) { + input_info(true, &ts->client->dev, "%s: bl fw download success - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } else { + input_err(true, &ts->client->dev, "%s: bl fw id does not match - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } + } + +} + +int sec_ts_firmware_update_bl(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1; + + disable_irq(ts->client->irq); + + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_BL_NAME); + + input_info(true, &ts->client->dev, "%s: initial bl update %s\n", __func__, fw_path); + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: bt is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request bt done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 1, 0, 0); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + + return result; +} + +int sec_ts_bl_update(struct sec_ts_data *ts) +{ + int ret; + u8 tCmd[5] = { 0xDE, 0xAD, 0xBE, 0xEF }; + u8 tBuff[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_READ_BL_UPDATE_STATUS, tCmd, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: bl update command send fail!\n", __func__); + goto err; + } + sec_ts_delay(10); + + do { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BL_UPDATE_STATUS, tBuff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read bl update status fail!\n", __func__); + goto err; + } + sec_ts_delay(2); + + } while (tBuff[0] == 0x1); + + tCmd[0] = 0x55; + tCmd[1] = 0xAC; + ret = ts->sec_ts_i2c_write(ts, 0x57, tCmd, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write passwd fail!\n", __func__); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3); + + if (tBuff[0] == 0xB4) { + input_info(true, &ts->client->dev, "%s: bl update completed!\n", __func__); + ret = 1; + } else { + input_info(true, &ts->client->dev, "%s: bl updated but bl version not matching, ver=%02X\n", __func__, tBuff[0]); + goto err; + } + + return ret; +err: + return -EIO; +} + +int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1, restore_cal = 0; + int ii = 0; + int ret = 0; + + if (ts->plat_data->bringup == 1) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + return 0; + } + + if (ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + else + return 0; + + disable_irq(ts->client->irq); + + /* read cal status */ + ts->cal_status = sec_ts_read_calibration_report(ts); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s, cal:%X\n", + __func__, fw_path, ts->cal_status); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_check_firmware_version(ts, fw_entry->data); + + if (ts->plat_data->bringup == 2) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + result = 0; + goto err_request_fw; + } + +#ifdef PAT_CONTROL + /* ic fw ver > bin fw ver && force is false*/ + if ((result <= 0) && (!force_update)) { + /* clear nv, forced f/w update eventhough same f/w, then apply pat magic */ + if (ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE) { + input_info(true, &ts->client->dev, "%s: run forced f/w update and excute autotune\n", __func__); + } else { + input_info(true, &ts->client->dev, "%s: skip - fw update & nv read\n", __func__); + goto err_request_fw; + } + } + + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + input_info(true, &ts->client->dev, "%s: cal_count [%02X]\n", __func__, ts->cal_count); + + /* initialize nv default value from 0xff to 0x00 */ + if (ts->cal_count == 0xFF) { + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION); + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION+1); + input_info(true, &ts->client->dev, "%s: initialize nv as default value & excute autotune\n", __func__); + } + + ts->tune_fix_ver = (get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION) << 8) | get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_TUNE_VERSION+1); + input_info(true, &ts->client->dev, "%s: tune_fix_ver [%04X] afe_base [%04X]\n", __func__, ts->tune_fix_ver, ts->plat_data->afe_base); + + /* check dt to clear pat */ + if (ts->plat_data->pat_function == PAT_CONTROL_CLEAR_NV || ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE) + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + + /* mismatch calibration - ic has too old calibration data after pat enabled*/ + if (ts->plat_data->afe_base > ts->tune_fix_ver) { + restore_cal = 1; + set_tsp_nvm_data_clear(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + } + + input_info(true, &ts->client->dev, "%s: cal_count [%02X]\n", __func__, ts->cal_count); +#else + /* ic firmware version >= binary firmare version + * && forced is FALSE */ + /* ic fw ver > bin fw ver && force is false*/ + if ((result <= 0) && (!force_update)) { + input_info(true, &ts->client->dev, "%s: skip fw update\n", __func__); + goto err_request_fw; + } +#endif + + for (ii = 0; ii < 3; ii++) { + ret = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, restore_cal, ii); + if (ret >= 0) + break; + } + + if (ret < 0) { + result = -1; + } else { + result = 0; +#ifdef PAT_CONTROL + /* change cal_count from 0 to magic number to make virtual pure auto tune */ + if ((ts->cal_count == 0 && ts->plat_data->pat_function == PAT_CONTROL_PAT_MAGIC)|| + (ts->plat_data->pat_function == PAT_CONTROL_FORCE_UPDATE)) { + set_pat_magic_number(ts); + ts->cal_count = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + } +#endif + } + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +static int sec_ts_load_fw_from_bin(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int error = 0; + + if (ts->client->irq) + disable_irq(ts->client->irq); + + if (!ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_FW_NAME); + else + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + error = -1; + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, 1, 0) < 0) + error = -1; + else + error = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + if (ts->client->irq) + enable_irq(ts->client->irq); + + return error; +} + +static int sec_ts_load_fw_from_ums(struct sec_ts_data *ts) +{ + fw_header *fw_hd; + struct file *fp; + mm_segment_t old_fs; + long fw_size, nread; + int error = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(SEC_TS_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR); + if (IS_ERR(fp)) { + input_err(true, ts->dev, "%s: failed to open %s.\n", __func__, + SEC_TS_DEFAULT_UMS_FW); + error = -ENOENT; + goto open_err; + } + + fw_size = fp->f_path.dentry->d_inode->i_size; + + if (fw_size > 0) { + unsigned char *fw_data; + + fw_data = kzalloc(fw_size, GFP_KERNEL); + nread = vfs_read(fp, (char __user *)fw_data, + fw_size, &fp->f_pos); + + input_info(true, ts->dev, + "%s: start, file path %s, size %ld Bytes\n", + __func__, SEC_TS_DEFAULT_UMS_FW, fw_size); + + if (nread != fw_size) { + input_err(true, ts->dev, + "%s: failed to read firmware file, nread %ld Bytes\n", + __func__, nread); + error = -EIO; + } else { + fw_hd = (fw_header *)fw_data; +/* + sec_ts_check_firmware_version(ts, fw_data); +*/ + input_info(true, &ts->client->dev, "%s: firmware version %08X\n", __func__, fw_hd->fw_ver); + input_info(true, &ts->client->dev, "%s: parameter version %08X\n", __func__, fw_hd->para_ver); + + if (ts->client->irq) + disable_irq(ts->client->irq); + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_data, fw_size, 0, 1, 0) < 0) + goto done; + + sec_ts_save_version_of_ic(ts); + } + + if (error < 0) + input_err(true, ts->dev, "%s: failed update firmware\n", + __func__); + +done: + if (ts->client->irq) + enable_irq(ts->client->irq); + kfree(fw_data); + } + + filp_close(fp, NULL); + +open_err: + set_fs(old_fs); + return error; +} + +static int sec_ts_load_fw_from_ffu(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + const char *fw_path = SEC_TS_DEFAULT_FFU_FW; + int result = -1; + + disable_irq(ts->client->irq); + + input_info(true, ts->dev, "%s: Load firmware : %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + sec_ts_check_firmware_version(ts, fw_entry->data); + + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, 0, 0) < 0) + result = -1; + else + result = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, int update_type) +{ + int ret = 0; + + /* Factory cmd for firmware update + * argument represent what is source of firmware like below. + * + * 0 : [BUILT_IN] Getting firmware which is for user. + * 1 : [UMS] Getting firmware from sd card. + * 2 : none + * 3 : [FFU] Getting firmware from air. + */ + + switch (update_type) { + case BUILT_IN: + ret = sec_ts_load_fw_from_bin(ts); + break; + case UMS: + ret = sec_ts_load_fw_from_ums(ts); + break; + case FFU: + ret = sec_ts_load_fw_from_ffu(ts); + break; + case BL: + ret = sec_ts_firmware_update_bl(ts); + if (ret < 0) { + break; + } else if (!ret) { + ret = sec_ts_firmware_update_on_probe(ts, false); + break; + } else { + ret = sec_ts_bl_update(ts); + if (ret < 0) + break; + ret = sec_ts_firmware_update_on_probe(ts, false); + if (ret < 0) + break; + } + break; + default: + input_err(true, ts->dev, "%s: Not support command[%d]\n", + __func__, update_type); + break; + } + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_check_custom_library(ts); +#endif + + return ret; +} +EXPORT_SYMBOL(sec_ts_firmware_update_on_hidden_menu); -- cgit v1.2.3