summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/stm/ftm4_fwu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/stm/ftm4_fwu.c')
-rw-r--r--drivers/input/touchscreen/stm/ftm4_fwu.c960
1 files changed, 960 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/stm/ftm4_fwu.c b/drivers/input/touchscreen/stm/ftm4_fwu.c
new file mode 100644
index 000000000000..76e8db5be4ff
--- /dev/null
+++ b/drivers/input/touchscreen/stm/ftm4_fwu.c
@@ -0,0 +1,960 @@
+/******************** (C) COPYRIGHT 2012 STMicroelectronics ********************
+*
+* File Name : ftm4_fwu.c
+* Authors : AMS(Analog Mems Sensor) Team
+* Description : FTS Capacitive touch screen controller (FingerTipS)
+*
+********************************************************************************
+*
+* 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.
+*
+* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*
+* THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+*******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include "ftm4_ts.h"
+
+#define FTS64FILE_SIGNATURE 0xaaaa5555
+
+enum {
+ BUILT_IN = 0,
+ UMS,
+ NONE,
+ FFU,
+};
+
+struct fts64_header {
+ unsigned int signature;
+ unsigned short fw_ver;
+ unsigned char fw_id;
+ unsigned char reserved1;
+ unsigned char internal_ver[8];
+ unsigned char released_ver[8];
+ unsigned int reserved2;
+ unsigned int checksum;
+};
+
+bool get_pure_autotune_status(struct fts_ts_info *info)
+{
+ int rc = 0;
+ unsigned char addrs[3];
+ unsigned char buf[5];
+ bool ret = false;
+ int doffset = 1;
+
+ if (info->digital_rev == FTS_DIGITAL_REV_1)
+ doffset = 0;
+
+ addrs[0] = 0xd0;
+ addrs[1] = 0x00;
+ addrs[2] = 0x4E;
+
+ rc = info->fts_read_reg(info, addrs, 3, buf, 4);
+ if (rc < 0) {
+ tsp_debug_err(info->dev,
+ "%s: PureAutotune Information Read Fail!!"
+ "[Data : %2X%2X]\n",
+ __func__, buf[0 + doffset],
+ buf[1 + doffset]);
+ } else {
+ if ((buf[0 + doffset] == 0xA5) && (buf[1 + doffset] == 0x96))
+ ret = 1;
+ tsp_debug_info(info->dev,
+ "%s: PureAutotune Information !! "
+ "[Data : %2X%2X]\n", __func__,
+ buf[0 + doffset],
+ buf[1 + doffset]);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(get_pure_autotune_status);
+
+static bool get_afe_status(struct fts_ts_info *info)
+{
+ int rc = 0;
+ unsigned char addrs[3];
+ unsigned char buf[5];
+ bool ret = false;
+ int doffset = 1;
+
+ if (info->digital_rev == FTS_DIGITAL_REV_1)
+ doffset = 0;
+
+ addrs[0] = 0xd0;
+ addrs[1] = 0x00;
+ addrs[2] = 0x52;
+
+ rc = info->fts_read_reg(info, addrs, 3, buf, 4);
+ if (rc < 0) {
+ tsp_debug_err(info->dev,
+ "%s: Read Fail - Final AFE [Data :"
+ " %2X] AFE Ver [Data : %2X]\n",
+ __func__,
+ buf[0 + doffset],
+ buf[1 + doffset]);
+ return rc;
+ }
+
+ if (buf[0 + doffset])
+ ret = true;
+
+ tsp_debug_info(info->dev,
+ "%s: Final AFE [Data : %2X] AFE Ver "
+ "[Data : %2X]\n",
+ __func__,
+ buf[0 + doffset],
+ buf[1 + doffset]);
+
+ return ret;
+}
+
+int fts_fw_wait_for_specific_event(struct fts_ts_info *info,
+ unsigned char eid0, unsigned char eid1, unsigned char eid2)
+{
+ int rc = 0;
+ unsigned char addrs;
+ unsigned char data[FTS_EVENT_SIZE];
+ int retry = 0;
+
+ memset(data, 0x0, FTS_EVENT_SIZE);
+
+ addrs = READ_ONE_EVENT;
+
+ while (info->fts_read_reg(info, &addrs, 1, (unsigned char *)data,
+ FTS_EVENT_SIZE)) {
+ if (data[0]) {
+ if ((data[0] == eid0) && (data[1] == eid1) &&
+ (data[2] == eid2)) {
+ rc = 0;
+ break;
+ } else {
+ tsp_debug_dbg(info->dev,
+ "%s: %2X, %2X, %2X, %2X\n",
+ __func__, data[0], data[1],
+ data[2], data[3]);
+ }
+ }
+ if (retry++ > FTS_RETRY_COUNT * 15) {
+ rc = -1;
+ tsp_debug_err(info->dev,
+ "%s: Time Over ( %2X, %2X, %2X, %2X )\n",
+ __func__, data[0], data[1],
+ data[2], data[3]);
+ break;
+ }
+ fts_delay(20);
+ }
+
+ return rc;
+}
+
+int fts_fw_wait_for_event(struct fts_ts_info *info, unsigned char eid0,
+ unsigned char eid1)
+{
+ int rc = 0;
+ unsigned char addrs;
+ unsigned char data[FTS_EVENT_SIZE];
+ int retry = 0;
+
+ memset(data, 0x0, FTS_EVENT_SIZE);
+
+ addrs = READ_ONE_EVENT;
+
+ while (info->fts_read_reg(info, &addrs, 1, (unsigned char *)data,
+ FTS_EVENT_SIZE)) {
+ if ((data[0] == EVENTID_STATUS_EVENT) ||
+ (data[0] == EVENTID_ERROR)) {
+ if ((data[0] == EVENTID_STATUS_EVENT) &&
+ (data[1] == eid0) && (data[2] == eid1)) {
+ break;
+ } else if ((data[0] == EVENTID_STATUS_EVENT) &&
+ (data[1] == STATUS_EVENT_FORCE_CAL_DONE)) {
+ break;
+ } else {
+ tsp_debug_dbg(info->dev,
+ "%s: %2X,%2X,%2X,%2X\n",
+ __func__,
+ data[0],
+ data[1],
+ data[2],
+ data[3]);
+ }
+ }
+
+ if (retry++ > FTS_RETRY_COUNT * 15) {
+ rc = -1;
+ tsp_debug_err(info->dev,
+ "%s: Time Over (%2X,%2X,%2X,%2X)\n",
+ __func__,
+ data[0],
+ data[1],
+ data[2],
+ data[3]);
+ break;
+ }
+ fts_delay(20);
+ }
+
+ return rc;
+}
+
+void fts_execute_autotune(struct fts_ts_info *info)
+{
+ int ret = 0;
+ unsigned char regData[4]; /* {0xC1, 0x0E}; */
+ bool bFinalAFE = false;
+ bool NoNeedAutoTune = false; /* default for factory */
+
+ bFinalAFE = get_afe_status(info);
+
+ /* Check flag and decide cx_tune */
+ NoNeedAutoTune = get_pure_autotune_status(info);
+
+ tsp_debug_info(info->dev,
+ "%s: AFE(%d), NoNeedAutoTune(%d)\n", __func__,
+ bFinalAFE, NoNeedAutoTune);
+
+ if ((!NoNeedAutoTune) || (info->o_afe_ver != info->afe_ver)) {
+ info->fts_command(info, CX_TUNNING);
+ fts_delay(300);
+ fts_fw_wait_for_event(info, STATUS_EVENT_MUTUAL_AUTOTUNE_DONE,
+ 0x00);
+
+ info->fts_command(info, SELF_AUTO_TUNE);
+ fts_delay(300);
+ fts_fw_wait_for_event(info, STATUS_EVENT_SELF_AUTOTUNE_DONE,
+ 0x00);
+
+ if (NoNeedAutoTune) {
+ tsp_debug_info(info->dev,
+ "%s: AFE_status(%d) write ( C8 01 )\n",
+ __func__, bFinalAFE);
+
+ regData[0] = 0xC8;
+ regData[1] = 0x01;
+ ret = info->fts_write_reg(info, regData, 2);
+ if (ret < 0) {
+ tsp_debug_err(info->dev,
+ "%s: Flash Back up PureAutotune"
+ "Fail (Clear)\n", __func__);
+ }
+
+ fts_delay(20);
+ fts_fw_wait_for_event(info,
+ STATUS_EVENT_PURE_AUTOTUNE_FLAG_CLEAR_FINISH,
+ 0x00);
+ }
+
+ info->fts_command(info, FTS_CMD_SAVE_CX_TUNING);
+ fts_delay(230);
+ fts_fw_wait_for_event(info,
+ STATUS_EVENT_FLASH_WRITE_CXTUNE_VALUE, 0x00);
+
+ info->fts_command(info, FTS_CMD_SAVE_FWCONFIG);
+ fts_delay(230);
+ fts_fw_wait_for_event(info, STATUS_EVENT_FLASH_WRITE_CONFIG,
+ 0x00);
+
+ /* Reset FTS */
+ info->fts_systemreset(info);
+ fts_delay(20);
+ /* wait for ready event */
+ info->fts_wait_for_ready(info);
+ }
+}
+
+#define FW_IMAGE_NAME_D2_TB_INTEG "tsp_stm/stm_tb_integ.fw"
+#define FW_IMAGE_NAME_D2_Z2A "tsp_stm/stm_z2a.fw"
+#define FW_IMAGE_NAME_D2_Z2I "tsp_stm/stm_z2i.fw"
+#define CONFIG_ID_D1_S 0x2C
+#define CONFIG_ID_D2_TR 0x2E
+#define CONFIG_ID_D2_TB 0x30
+#define CONFIG_OFFSET_BIN_D1 0xf822
+#define CONFIG_OFFSET_BIN_D2 0x1E822
+#define RX_OFFSET_BIN_D2 0x1E834
+#define TX_OFFSET_BIN_D2 0x1E835
+
+#define FW_IMAGE_SIZE_D3 (256 * 1024)
+#define SIGNEDKEY_SIZE (256)
+
+static int wait_for_flash_ready(struct fts_ts_info *info, uint8_t type)
+{
+ uint8_t cmd[2] = {FLASH_CMD_READ_REGISTER, type};
+ uint8_t readData;
+ int i, res = -1;
+
+ tsp_debug_info(info->dev, "[wait_for_flash_ready"
+ " Waiting for flash ready\n");
+
+ for (i = 0; i < 1000 && res != 0; i++) {
+ info->fts_read_reg(info, cmd, sizeof(cmd), &readData, 1);
+ res = readData & 0x80;
+ fts_delay(50);
+ }
+
+ if (i >= 1000 && res != 0) {
+ tsp_debug_err(info->dev, "[wait_for_flash_ready]"
+ " Wait for flash TIMEOUT! ERROR\n");
+ return 0;
+ }
+
+ tsp_debug_info(info->dev, "[wait_for_flash_ready]"
+ " Flash READY!\n");
+
+ return 1;
+}
+
+static int start_flash_dma(struct fts_ts_info *info)
+{
+ int status;
+ uint8_t cmd[3] = {FLASH_CMD_WRITE_REGISTER,
+ FLASH_DMA_CODE0, FLASH_DMA_CODE1};
+
+ tsp_debug_info(info->dev,
+ "[start_flash_dma] Command flash DMA ...\n");
+ info->fts_write_reg(info, cmd, sizeof(cmd));
+
+ status = wait_for_flash_ready(info, FLASH_DMA_CODE0);
+
+ if (status != true) {
+ tsp_debug_err(info->dev,
+ "[start_flash_dma] start_flash_dma: ERROR\n");
+ return false;
+ }
+ tsp_debug_info(info->dev, "[start_flash_dma] flash DMA DONE!\n");
+
+ return true;
+}
+
+static int fillFlash(struct fts_ts_info *info, uint32_t address, uint8_t *data,
+ int size)
+{
+ int remaining;
+ int toWrite = 0;
+ int byteBlock = 0;
+ int wheel = 0;
+ uint32_t addr = 0;
+ int res;
+ int delta;
+
+ uint8_t buff[DMA_CHUNK + 3] = {0};
+
+ remaining = size;
+ while (remaining > 0) {
+ byteBlock = 0;
+ addr = 0;
+ tsp_debug_info(info->dev,
+ "[fillFlash] [%d] Write data to memory.\n",
+ wheel);
+ while (byteBlock < FLASH_CHUNK && remaining > 0) {
+ buff[0] = FLASH_CMD_WRITE_64K;
+ if (remaining >= DMA_CHUNK) {
+ if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) {
+ toWrite = DMA_CHUNK;
+ remaining -= DMA_CHUNK;
+ byteBlock += DMA_CHUNK;
+ } else {
+ delta = FLASH_CHUNK - byteBlock;
+ toWrite = delta;
+ remaining -= delta;
+ byteBlock += delta;
+ }
+ } else {
+ if ((byteBlock + remaining) <= FLASH_CHUNK) {
+ toWrite = remaining;
+ byteBlock += remaining;
+ remaining = 0;
+
+ } else {
+ delta = FLASH_CHUNK - byteBlock;
+ toWrite = delta;
+ remaining -= delta;
+ byteBlock += delta;
+ }
+ }
+
+ buff[1] = (uint8_t) ((addr & 0x0000FF00) >> 8);
+ buff[2] = (uint8_t) (addr & 0x000000FF);
+ memcpy(&buff[3], data, toWrite);
+ info->fts_write_reg(info, buff, 3 + toWrite);
+
+ addr += toWrite;
+ data += toWrite;
+ }
+
+ /* configuring the DMA */
+ tsp_debug_info(info->dev,
+ "[fillFlash] [%d] Configure DMA\n", wheel);
+ byteBlock = byteBlock / 4 - 1;
+
+ buff[0] = FLASH_CMD_WRITE_REGISTER;
+ buff[1] = FLASH_DMA_CONFIG;
+ buff[2] = 0x00;
+ buff[3] = 0x00;
+
+ addr = address + ((wheel * FLASH_CHUNK)/4);
+ buff[4] = (uint8_t) ((addr & 0x000000FF));
+ buff[5] = (uint8_t) ((addr & 0x0000FF00) >> 8);
+ buff[6] = (uint8_t) (byteBlock & 0x000000FF);
+ buff[7] = (uint8_t) ((byteBlock & 0x0000FF00) >> 8);
+ buff[8] = 0x00;
+
+ info->fts_write_reg(info, buff, 9);
+ fts_delay(10);
+
+ tsp_debug_info(info->dev,
+ "[fillFlash] [%d] Start flash DMA\n", wheel);
+ res = start_flash_dma(info);
+ if (res < true) {
+ tsp_debug_err(info->dev,
+ "[fillFlash] Error during flashing DMA! ERROR\n");
+ return false;
+ }
+ tsp_debug_info(info->dev,
+ "[fillFlash] [%d] DMA done\n", wheel);
+
+ wheel++;
+ }
+ return true;
+}
+
+uint32_t convU8toU32(uint8_t *src)
+{
+ uint32_t tmpData;
+
+ tmpData = (uint32_t) (((src[3] & 0xFF) << 24) +
+ ((src[2] & 0xFF) << 16) +
+ ((src[1] & 0xFF) << 8) +
+ (src[0] & 0xFF));
+
+ return tmpData;
+}
+
+static int parseBinFile(struct fts_ts_info *info, uint8_t *data,
+ int fw_size,
+ struct FW_FTB_HEADER *fw_header,
+ int keep_cx)
+{
+ int dimension, index;
+ uint32_t temp;
+ int file_type;
+
+ /* start the parsing */
+ index = 0;
+ fw_header->signature = convU8toU32(&data[index]);
+ if (fw_header->signature == FW_HEADER_FTB_SIGNATURE) {
+ tsp_debug_info(info->dev,
+ "[parseBinFile] FW Signature - ftb file\n");
+ file_type = BIN_FTB;
+ } else {
+ tsp_debug_info(info->dev,
+ "[parseBinFile] FW Signature - ftsxxx file. %08X\n",
+ fw_header->signature);
+ file_type = BIN_FTS256;
+ return file_type;
+ }
+
+ index += FW_BYTES_ALLIGN;
+#ifdef FTS_USE_FTB_1
+ fw_header->ftb_ver = convU8toU32(&data[index]);
+ if (fw_header->ftb_ver != FW_FTB_VER) {
+ tsp_debug_err(info->dev,
+ "[parseBinFile] Wrong" " ftb_version %08X ... ERROR\n",
+ fw_header->ftb_ver);
+ return false;
+ }
+#endif /* FTS_USE_FTB_1 */
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->target = convU8toU32(&data[index]);
+ if (fw_header->target != 0x00007036) {
+ tsp_debug_err(info->dev,
+ "[parseBinFile] Wrong target version %08X ... ERROR\n",
+ fw_header->target);
+ return false;
+ }
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->fw_id = convU8toU32(&data[index]);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->fw_ver = convU8toU32(&data[index]);
+ info->fw_version_of_bin = fw_header->fw_ver;
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->cfg_id = convU8toU32(&data[index]);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->cfg_ver = convU8toU32(&data[index]);
+ info->config_version_of_bin = fw_header->cfg_ver;
+
+ index += FW_BYTES_ALLIGN * 3; /* skip 2 reserved data */
+ fw_header->bl_fw_ver = convU8toU32(&data[index]);
+ index += FW_BYTES_ALLIGN;
+
+ fw_header->ext_ver = convU8toU32(&data[index]);
+
+ tsp_debug_info(info->dev,
+ "[parseBinFile] Version : External"
+ " = %04X, FW = %04X, CFG = %04X\n",
+ fw_header->ext_ver,
+ fw_header->fw_ver,
+ fw_header->cfg_ver);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->sec0_size = convU8toU32(&data[index]);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->sec1_size = convU8toU32(&data[index]);
+
+ tsp_debug_info(info->dev,
+ "[parseBinFile] sec0_size = %08X"
+ " (%d bytes), sec1_size = %08X (%d bytes)\n",
+ fw_header->sec0_size,
+ fw_header->sec0_size,
+ fw_header->sec1_size,
+ fw_header->sec1_size);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->sec2_size = convU8toU32(&data[index]);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->sec3_size = convU8toU32(&data[index]);
+
+ tsp_debug_info(info->dev,
+ "[parseBinFile] sec2_size = %08X"
+ " (%d bytes), sec3_size = %08X (%d bytes)\n",
+ fw_header->sec2_size,
+ fw_header->sec2_size,
+ fw_header->sec3_size,
+ fw_header->sec3_size);
+
+ index += FW_BYTES_ALLIGN;
+ fw_header->hdr_crc = convU8toU32(&data[index]);
+
+ if (!keep_cx) {
+ dimension = fw_header->sec0_size + fw_header->sec1_size
+ + fw_header->sec2_size + fw_header->sec3_size;
+
+ temp = fw_size;
+ } else {
+ /* sec2 may contain cx data (future implementation)
+ * sec3 atm not used */
+ dimension = fw_header->sec0_size + fw_header->sec1_size;
+ temp = fw_size - fw_header->sec2_size - fw_header->sec3_size;
+ }
+
+ if (dimension + FW_HEADER_SIZE + FW_BYTES_ALLIGN != temp) {
+ tsp_debug_info(info->dev,
+ "[parseBinFile] Read only %d"
+ " instead of %d... ERROR\n",
+ fw_size, dimension
+ + FW_HEADER_SIZE
+ + FW_BYTES_ALLIGN);
+
+ return false;
+ }
+
+ return file_type;
+}
+
+static int fts_check_erase_done(struct fts_ts_info *info)
+{
+ int timeout = 60; /* 3 sec timeout */
+ unsigned char addrs[2] = {0xF9, 0x02};
+ unsigned char val[1];
+ int rc = 0;
+
+ do {
+ info->fts_read_reg(info, &addrs[0], 2, (unsigned char *)val, 1);
+
+ if ((val[0] & 0x80) != 0x80)
+ break;
+
+ fts_delay(50);
+ timeout--;
+ } while (timeout != 0);
+
+ if (timeout == 0)
+ rc = -1;
+
+ return rc;
+}
+
+int fw_download(struct fts_ts_info *info, uint8_t *pFilename,
+ struct FW_FTB_HEADER *fw_Header, int8_t block_type)
+{
+ uint32_t FTS_TOTAL_SIZE = (256 * 1024); /* Total 256kB */
+ int HEADER_DATA_SIZE = 32;
+
+ int res = 0, rc = 0, i = 0;
+ uint8_t addrs[8] = {0};
+
+ /* System reset */
+ /* System Reset ==> F7 52 34 */
+
+ addrs[0] = 0xF7;
+ addrs[1] = 0x52;
+ addrs[2] = 0x34;
+ info->fts_write_reg(info, &addrs[0], 3);
+ fts_delay(30);
+
+ /* Unlock Flash */
+ /* Unlock Flash Command ==> F7 74 45 */
+ addrs[0] = 0xF7;
+ addrs[1] = 0x74;
+ addrs[2] = 0x45;
+ info->fts_write_reg(info, &addrs[0], 3);
+ fts_delay(100);
+
+ /* Unlock Erase Operation */
+ addrs[0] = 0xFA;
+ addrs[1] = 0x72;
+ addrs[2] = 0x01;
+ info->fts_write_reg(info, &addrs[0], 3);
+ fts_delay(30);
+
+ /* Erase Partial Flash */
+ for (i = 0; i < 64; i++) {
+ /* skip CX2 area (page 61 and page 62) */
+ if ((i == 61) || (i == 62))
+ continue;
+
+ addrs[0] = 0xFA;
+ addrs[1] = 0x02;
+ addrs[2] = (0x80 + i) & 0xFF;
+ info->fts_write_reg(info, &addrs[0], 3);
+ rc = fts_check_erase_done(info);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Unlock Programming operation */
+ addrs[0] = 0xFA;
+ addrs[1] = 0x72;
+ addrs[2] = 0x02;
+ info->fts_write_reg(info, &addrs[0], 3);
+ fts_delay(100);
+
+ /* Write to FLASH */
+ if (block_type == BIN_FTB) {
+ tsp_debug_info(info->dev,
+ "[fw_download] Start sec0 program\n");
+
+ res = fillFlash(info,
+ FLASH_ADDR_CODE,
+ &pFilename[FW_HEADER_SIZE],
+ fw_Header->sec0_size);
+
+ if (res != true) {
+ tsp_debug_err(info->dev,
+ "[fw_download] Error - load sec0 program\n");
+ return false;
+ }
+ tsp_debug_info(info->dev,
+ "[fw_download] load sec0 program DONE!\n");
+ tsp_debug_info(info->dev,
+ "[fw_download] Start sec1 program\n");
+
+ res = fillFlash(info,
+ FLASH_ADDR_CONFIG,
+ &pFilename[FW_HEADER_SIZE +
+ fw_Header->sec0_size],
+ fw_Header->sec1_size);
+
+ if (res != true) {
+ tsp_debug_err(info->dev,
+ "[fw_download] Error - load sec1 program\n");
+ return false;
+ }
+ tsp_debug_info(info->dev,
+ "[fw_download] load sec1 program DONE!\n");
+
+ tsp_debug_info(info->dev,
+ "[fw_download] Flash burn COMPLETED!\n");
+ } else {
+ tsp_debug_info(info->dev,
+ "[fw_download] Start firmware downloading\n");
+ res = fillFlash(info, FLASH_ADDR_CODE,
+ &pFilename[HEADER_DATA_SIZE], FTS_TOTAL_SIZE);
+ if (res != true) {
+ tsp_debug_err(info->dev,
+ "[fw_download] Error - load sec0 program\n");
+ return false;
+ }
+ }
+
+ /* System reset */
+ addrs[0] = 0xF7;
+ addrs[1] = 0x52;
+ addrs[2] = 0x34;
+ info->fts_write_reg(info, &addrs[0], 3);
+ if (fts_cmd_completion_check(info, 0x10, 0x00, 0x00) < 0) {
+ tsp_debug_err(info->dev,
+ "[fw_download] Error - System Reset FAILED\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int fts_fw_compare(struct fts_ts_info *info, const struct firmware *fw)
+{
+ u32 bin_fw_ver_addr_1 = 0;
+ u32 bin_fw_ver_addr_2 = 0;
+ u32 bin_fw_ver_offset = 24;
+ u8 buf[2] = {0};
+ struct fts_version *binary = NULL;
+ struct fts_version *device = &info->ic_fw_ver;
+ int update = 0;
+
+ if ((u32)fw->size < bin_fw_ver_offset) {
+ tsp_debug_err(info->dev,
+ " fw->size(0x%08X) < bin_fw_ver_offset(0x%08X)\n",
+ (u32)fw->size, bin_fw_ver_offset);
+ update = 0;
+ goto error;
+ }
+
+ bin_fw_ver_addr_1 = (u32)fw->size - bin_fw_ver_offset;
+ bin_fw_ver_addr_2 = bin_fw_ver_addr_1 + 1;
+ tsp_debug_info(info->dev,
+ "%s: bin_fw_ver_addr_1 = 0x%08X , bin_fw_ver_addr_2 = 0x%08X\n",
+ __func__, bin_fw_ver_addr_1, bin_fw_ver_addr_2);
+
+ binary = kzalloc(sizeof(struct fts_version), GFP_KERNEL);
+ if (binary == NULL) {
+ tsp_debug_err(info->dev, "failed to kzalloc binary\n");
+ update = 0;
+ goto error;
+ }
+
+ buf[0] = fw->data[bin_fw_ver_addr_1];
+ buf[1] = fw->data[bin_fw_ver_addr_2];
+
+ binary->build = (buf[0] >> 4) & 0x0F;
+ binary->major = buf[0] & 0x0F;
+ binary->minor = buf[1];
+
+ if (binary->major != device->major) {
+ update = 1;
+ } else {
+ if (binary->minor != device->minor)
+ update = 1;
+ else if (binary->build > device->build)
+ update = 1;
+ }
+
+ tsp_debug_info(info->dev,
+ "%s : binary[%d.%02d.%d] device[%d.%02d.%d]"
+ " -> update: %d\n", __func__,
+ binary->major, binary->minor, binary->build,
+ device->major, device->minor, device->build,
+ update);
+
+error:
+ if (binary)
+ kfree(binary);
+
+ return update;
+}
+
+void fts_fw_init(struct fts_ts_info *info)
+{
+ tsp_debug_info(info->dev, "%s\n", __func__);
+
+ info->fts_command(info, FTS_CMD_TRIM_LOW_POWER_OSCILLATOR);
+ fts_delay(200);
+ info->fts_command(info, FTS_CMD_SAVE_CX_TUNING);
+ fts_delay(230);
+ fts_fw_wait_for_event(info, STATUS_EVENT_FLASH_WRITE_CXTUNE_VALUE, 0x00);
+
+ fts_get_afe_info(info);
+
+ fts_execute_autotune(info);
+
+ info->fts_command(info, SENSEON);
+
+ fts_fw_wait_for_event(info, STATUS_EVENT_FORCE_CAL_DONE, 0x00);
+
+ info->fts_interrupt_set(info, INT_ENABLE);
+}
+
+static int fts_fw_check(struct fts_ts_info *info)
+{
+ int retval = 0;
+
+ retval = fts_systemreset(info);
+ if (retval < 0)
+ return retval;
+
+ retval = fts_wait_for_ready(info);
+ if (retval < 0)
+ return retval;
+
+ retval = fts_read_chip_id(info);
+ if (retval < 0)
+ return retval;
+
+ return retval;
+}
+
+static int fts_fw_update(struct fts_ts_info *info)
+{
+ const struct firmware *fw_entry = NULL;
+ unsigned char *fw_data = NULL;
+ char fw_path[FTS_MAX_FW_PATH];
+ const struct FW_FTB_HEADER *header;
+ int fw_size;
+ int fw_type;
+ int keep_cx;
+ int retval = 0;
+ struct FW_FTB_HEADER fw_ftbHeader;
+
+ if (info->fts_power_state != FTS_POWER_STATE_ACTIVE) {
+ tsp_debug_err(info->dev,
+ "%s : FTS_POWER_STATE is not ACTIVE\n", __func__);
+ return -EPERM;
+ }
+
+ if (info->test_fwpath[0]) {
+ strlcpy(fw_path, &info->test_fwpath[0], sizeof(fw_path));
+ } else if(info->board->firmware_name) {
+ /* A pointer and size of buffer for binary file */
+ strlcpy(fw_path, &info->board->firmware_name[0], sizeof(fw_path));
+ } else {
+ tsp_debug_err(info->dev, "%s : no firmware file\n", __func__);
+ return -EPERM;
+ }
+
+ tsp_debug_info(info->dev,
+ "%s : firmware name : %s\n", __func__, fw_path);
+
+ retval = request_firmware(&fw_entry, fw_path, info->dev);
+ if (retval) {
+ tsp_debug_err(info->dev,
+ "%s : Firmware image %s not available\n", __func__,
+ fw_path);
+ return retval;
+ }
+
+ if (!fts_fw_compare(info, fw_entry)) {
+ tsp_debug_info(info->dev,
+ "%s : skip fw_upgrade(ic_fw_ver == bin_fw_ver)\n",
+ __func__);
+ goto out;
+ }
+
+ fw_size = fw_entry->size;
+ fw_data = (unsigned char *)fw_entry->data;
+ header = (struct FW_FTB_HEADER *)fw_data;
+
+ info->fw_version_of_bin = header->fw_ver;
+ info->config_version_of_bin = header->cfg_ver;
+ /* saver previous afe version before downloading */
+ info->o_afe_ver = info->afe_ver;
+#ifdef FTS_FTB_STYLE_2
+ info->fw_main_version_of_bin =
+ ((header->ext_ver & 0xff)<<8) +
+ ((header->ext_ver >> 8) & 0xff);
+
+ tsp_debug_info(info->dev,
+ "Bin Firmware Version : 0x%04X "
+ "Bin Config Version : 0x%04X "
+ "Bin Main Firmware Version : 0x%04X ",
+ info->fw_version_of_bin,
+ info->config_version_of_bin,
+ info->fw_main_version_of_bin);
+#else /* FTS_FTB_STYLE_2 */
+ tsp_debug_info(info->dev,
+ "Bin Firmware Version : 0x%04X "
+ "Bin Config Version : 0x%04X ",
+ info->fw_version_of_bin,
+ info->config_version_of_bin);
+#endif
+ memcpy(&fw_ftbHeader, fw_data, sizeof(struct FW_FTB_HEADER));
+
+ tsp_debug_info(info->dev,
+ "[flashProcedure] Firmware size : %d\n", fw_size);
+ keep_cx = 0;
+
+ fw_type = parseBinFile(info, fw_data, fw_size, &fw_ftbHeader, keep_cx);
+ if (fw_type == false) {
+ tsp_debug_err(info->dev,
+ "[flashProcedure] Error - FW is not appreciate\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ retval = fw_download(info, fw_data, &fw_ftbHeader, fw_type);
+ if (retval == 0) {
+ tsp_debug_err(info->dev,
+ "[flashProcedure] Error - Firmware update is not completed.\n");
+ retval = -EIO;
+ goto out;
+ }
+
+ fts_fw_init(info);
+ retval = fts_fw_check(info);
+ if (retval < 0 ||
+ info->flash_corruption_info.fw_broken ||
+ info->flash_corruption_info.cfg_broken ||
+ info->flash_corruption_info.cx_broken) {
+ retval = -EIO;
+ goto out;
+ }
+
+ fts_get_version_info(info);
+ if (fts_fw_compare(info, fw_entry)) {
+ tsp_debug_err(info->dev,
+ "[flashProcedure] Firmware update failed\n");
+ retval = -EIO;
+ goto out;
+ }
+
+ tsp_debug_info(info->dev,
+ "[flashProcedure] Firmware update is done successfully.\n");
+ retval = 0;
+out:
+ if (fw_entry)
+ release_firmware(fw_entry);
+ return retval;
+}
+
+int fts_fw_verify_update(struct fts_ts_info *info)
+{
+ int retry = 0;
+
+ info->fts_irq_enable(info, false);
+ while (retry++ < FTS_FW_UPDATE_RETRY) {
+ tsp_debug_info(info->dev,
+ "[fw_update] try:%d\n", retry);
+ if (0 == fts_fw_update(info)) {
+ info->fts_irq_enable(info, true);
+ return 0;
+ }
+ }
+ info->fts_irq_enable(info, true);
+ return -EIO;
+}
+EXPORT_SYMBOL(fts_fw_verify_update);