blob: 7b61f943eb9cb4f3383182d6f2b4abdd57d52fcb [file] [log] [blame]
/*
*
* FocalTech fts TouchScreen driver.
*
* Copyright (c) 2010-2017, Focaltech Ltd. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
/*****************************************************************************
*
* File Name: focaltech_flash.c
*
* Author: fupeipei
*
* Created: 2016-08-08
*
* Abstract:
*
* Reference:
*
*****************************************************************************/
/*****************************************************************************
* 1.Included header files
*****************************************************************************/
#include "focaltech_core.h"
#include "focaltech_flash.h"
/*****************************************************************************
* Static variables
*****************************************************************************/
struct ft_chip_t chip_types;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/* Upgrade FW/PRAMBOOT/LCD CFG */
u8 CTPM_FW[] =
{
//#include FTS_UPGRADE_FW_APP
"include/firmware/FT8716_app_sample.i"
};
#if (FTS_GET_VENDOR_ID_NUM >= 2)
u8 CTPM_FW2[] =
{
#include FTS_UPGRADE_FW2_APP
};
#endif
#if (FTS_GET_VENDOR_ID_NUM >= 3)
u8 CTPM_FW3[] =
{
#include FTS_UPGRADE_FW3_APP
};
#endif
u8 aucFW_PRAM_BOOT[] =
{
#ifdef FTS_UPGRADE_PRAMBOOT
#include FTS_UPGRADE_PRAMBOOT
#endif
};
struct fts_upgrade_fun fts_updatefun_curr;
struct workqueue_struct *touch_wq;
struct work_struct fw_update_work;
u8 *g_fw_file;
int g_fw_len;
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
/************************************************************************
* Name: fts_ctpm_upgrade_delay
* Brief: 0
* Input: 0
* Output: 0
* Return: 0
***********************************************************************/
void fts_ctpm_upgrade_delay(u32 i)
{
do
{
i--;
}
while (i>0);
}
/************************************************************************
* Name: fts_ctpm_i2c_hid2std
* Brief: HID to I2C
* Input: i2c info
* Output: no
* Return: fail =0
***********************************************************************/
int fts_ctpm_i2c_hid2std(struct i2c_client *client)
{
#if (FTS_CHIP_IDC)
return 0;
#else
u8 buf[5] = {0};
int bRet = 0;
buf[0] = 0xeb;
buf[1] = 0xaa;
buf[2] = 0x09;
bRet =fts_i2c_write(client, buf, 3);
msleep(10);
buf[0] = buf[1] = buf[2] = 0;
fts_i2c_read(client, buf, 0, buf, 3);
if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2]))
{
FTS_DEBUG("hidi2c change to stdi2c successful!!");
bRet = 1;
}
else
{
FTS_ERROR("hidi2c change to stdi2c error!!");
bRet = 0;
}
return bRet;
#endif
}
/************************************************************************
* Name: fts_get_chip_types
* Brief: get correct chip information
* Input:
* Output:
* Return:
***********************************************************************/
void fts_get_chip_types(void)
{
struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING;
int ic_type = 0;
if (sizeof(ctype) != sizeof(struct ft_chip_t)) /* only one array */
ic_type = IC_SERIALS - 1;
chip_types = ctype[ic_type];
FTS_INFO("CHIP TYPE ID = 0x%02x%02x", chip_types.chip_idh, chip_types.chip_idl);
}
/************************************************************************
* Name: fts_ctpm_get_upgrade_array
* Brief: decide which ic
* Input: no
* Output: get ic info in fts_updateinfo_curr
* Return: no
***********************************************************************/
void fts_ctpm_get_upgrade_array(void)
{
FTS_FUNC_ENTER();
fts_get_chip_types();
fts_ctpm_i2c_hid2std(fts_i2c_client);
/* Get functin pointer */
memcpy(&fts_updatefun_curr, &fts_updatefun, sizeof(struct fts_upgrade_fun));
FTS_FUNC_EXIT();
}
/************************************************************************
* Name: fts_ctpm_rom_or_pram_reset
* Brief: RST CMD(07), reset to romboot(maybe->bootloader)
* Input:
* Output:
* Return:
***********************************************************************/
void fts_ctpm_rom_or_pram_reset(struct i2c_client *client)
{
u8 rst_cmd = FTS_REG_RESET_FW;
FTS_INFO("[UPGRADE]******Reset to romboot/bootloader******");
fts_i2c_write(client, &rst_cmd, 1);
/* The delay can't be changed */
msleep(300);
}
/************************************************************************
* Name: fts_ctpm_auto_clb
* Brief: auto calibration
* Input: i2c info
* Output: no
* Return: 0
***********************************************************************/
int fts_ctpm_auto_clb(struct i2c_client *client)
{
#if FTS_AUTO_CLB_EN
u8 uc_temp = 0x00;
u8 i = 0;
/*start auto CLB */
msleep(200);
fts_i2c_write_reg(client, 0, FTS_REG_WORKMODE_FACTORY_VALUE);
/*make sure already enter factory mode */
msleep(100);
/*write command to start calibration */
fts_i2c_write_reg(client, 2, 0x4);
msleep(300);
if ((chip_types.chip_idh==0x11) ||(chip_types.chip_idh==0x12) ||(chip_types.chip_idh==0x13) ||(chip_types.chip_idh==0x14)) //5x36,5x36i
{
for (i=0; i<100; i++)
{
fts_i2c_read_reg(client, 0x02, &uc_temp);
if (0x02 == uc_temp ||
0xFF == uc_temp)
{
break;
}
msleep(20);
}
}
else
{
for (i=0; i<100; i++)
{
fts_i2c_read_reg(client, 0, &uc_temp);
if (0x0 == ((uc_temp&0x70)>>4))
{
break;
}
msleep(20);
}
}
fts_i2c_write_reg(client, 0, 0x40);
msleep(200);
fts_i2c_write_reg(client, 2, 0x5);
msleep(300);
fts_i2c_write_reg(client, 0, FTS_REG_WORKMODE_WORK_VALUE);
msleep(300);
#endif
return 0;
}
/************************************************************************
* Name: fts_GetFirmwareSize
* Brief: get file size
* Input: file name
* Output: no
* Return: file size
***********************************************************************/
int fts_GetFirmwareSize(char *firmware_name)
{
struct file *pfile = NULL;
struct inode *inode;
unsigned long magic;
off_t fsize = 0;
char filepath[FILE_NAME_LENGTH];
memset(filepath, 0, sizeof(filepath));
sprintf(filepath, "%s%s", FTXXXX_INI_FILEPATH_CONFIG, firmware_name);
if (NULL == pfile)
{
pfile = filp_open(filepath, O_RDONLY, 0);
}
if (IS_ERR(pfile))
{
FTS_ERROR("error occured while opening file %s", filepath);
return -EIO;
}
inode = pfile->f_path.dentry->d_inode;
magic = inode->i_sb->s_magic;
fsize = inode->i_size;
filp_close(pfile, NULL);
return fsize;
}
/************************************************************************
* Name: fts_ReadFirmware
* Brief: read firmware buf for .bin file.
* Input: file name, data buf
* Output: data buf
* Return: 0
***********************************************************************/
int fts_ReadFirmware(char *firmware_name,u8 *firmware_buf)
{
struct file *pfile = NULL;
struct inode *inode;
unsigned long magic;
off_t fsize;
char filepath[FILE_NAME_LENGTH];
loff_t pos;
mm_segment_t old_fs;
memset(filepath, 0, sizeof(filepath));
sprintf(filepath, "%s%s", FTXXXX_INI_FILEPATH_CONFIG, firmware_name);
if (NULL == pfile)
{
pfile = filp_open(filepath, O_RDONLY, 0);
}
if (IS_ERR(pfile))
{
FTS_ERROR("[UPGRADE] Error occured while opening file %s.\n", filepath);
return -EIO;
}
inode = pfile->f_path.dentry->d_inode;
magic = inode->i_sb->s_magic;
fsize = inode->i_size;
old_fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
vfs_read(pfile, firmware_buf, fsize, &pos);
filp_close(pfile, NULL);
set_fs(old_fs);
return 0;
}
/************************************************************************
* Name: fts_getsize
* Brief: Get different file's size
* Input:
* Output:
* Return: file's size
***********************************************************************/
u32 fts_getsize(u8 fw_type)
{
int fw_len = 0;
if (fw_type == FW_SIZE)
fw_len = sizeof(CTPM_FW);
#if (FTS_GET_VENDOR_ID_NUM >= 2)
else if (fw_type == FW2_SIZE)
fw_len = sizeof(CTPM_FW2);
#endif
#if (FTS_GET_VENDOR_ID_NUM >= 3)
else if (fw_type == FW3_SIZE)
fw_len = sizeof(CTPM_FW3);
#endif
#if FTS_CHIP_IDC
else if (fw_type == PRAMBOOT_SIZE)
fw_len = sizeof(aucFW_PRAM_BOOT);
#endif
return fw_len;
}
/************************************************************************
* Name: fts_ctpm_get_pram_or_rom_id
* Brief: 0
* Input: 0
* Output: 0
* Return: 0
***********************************************************************/
enum FW_STATUS fts_ctpm_get_pram_or_rom_id(struct i2c_client *client)
{
u8 buf[4];
u8 reg_val[2] = {0};
enum FW_STATUS inRomBoot = FTS_RUN_IN_ERROR;
fts_ctpm_i2c_hid2std(client);
/*Enter upgrade mode*/
/*send 0x55 in time windows*/
buf[0] = FTS_UPGRADE_55;
buf[1] = FTS_UPGRADE_AA;
fts_i2c_write(client, buf, 2);
msleep(20);
buf[0] = 0x90;
buf[1] = buf[2] = buf[3] =0x00;
fts_i2c_read(client, buf, 4, reg_val, 2);
FTS_DEBUG("[UPGRADE] Read ROM/PRAM/Bootloader id:0x%02x%02x", reg_val[0], reg_val[1]);
if ((reg_val[0] == 0x00) || (reg_val[0] == 0xFF))
{
inRomBoot = FTS_RUN_IN_ERROR;
}
else if (reg_val[0] == chip_types.pramboot_idh && reg_val[1] == chip_types.pramboot_idl)
{
inRomBoot = FTS_RUN_IN_PRAM;
}
else if (reg_val[0] == chip_types.rom_idh && reg_val[1] == chip_types.rom_idl)
{
inRomBoot = FTS_RUN_IN_ROM;
}
else if (reg_val[0] == chip_types.bootloader_idh && reg_val[1] == chip_types.bootloader_idl)
{
inRomBoot = FTS_RUN_IN_BOOTLOADER;
}
return inRomBoot;
}
/************************************************************************
* Name: fts_ctpm_get_app_file
* Brief: get app file by Vendor ID
* Input:
* Output:
* Return: <0: vendor id not correct,not upgrade
***********************************************************************/
int fts_ctpm_get_i_file(struct i2c_client *client, int fw_valid)
{
int ret;
if (fts_updatefun_curr.get_i_file)
ret = fts_updatefun_curr.get_i_file(client, fw_valid);
else
ret = -EIO;
return ret;
}
/************************************************************************
* Name: fts_ctpm_get_app_ver
* Brief: get app file version
* Input:
* Output:
* Return: fw version
***********************************************************************/
int fts_ctpm_get_app_ver(void)
{
int i_ret = 0;
if (fts_updatefun_curr.get_app_i_file_ver)
i_ret = fts_updatefun_curr.get_app_i_file_ver();
return i_ret;
}
/************************************************************************
* Name: fts_ctpm_fw_upgrade
* Brief: fw upgrade entry funciotn
* Input:
* Output:
* Return: 0 - upgrade successfully
* <0 - upgrade failed
***********************************************************************/
int fts_ctpm_fw_upgrade(struct i2c_client *client)
{
int i_ret = 0;
if (fts_updatefun_curr.upgrade_with_app_i_file)
i_ret = fts_updatefun_curr.upgrade_with_app_i_file(client);
return i_ret;
}
/************************************************************************
* Name: fts_ctpm_fw_upgrade
* Brief: fw upgrade entry funciotn
* Input:
* Output:
* Return: 0 - upgrade successfully
* <0 - upgrade failed
***********************************************************************/
int fts_ctpm_lcd_cfg_upgrade(struct i2c_client *client)
{
int i_ret = 0;
if (fts_updatefun_curr.upgrade_with_lcd_cfg_i_file)
i_ret = fts_updatefun_curr.upgrade_with_lcd_cfg_i_file(client);
return i_ret;
}
bool fts_check_fw_valid(struct i2c_client *client)
{
int i = 0;
u8 chip_id = 0;
for (i = 0; i < 3; i++)
{
fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id);
if(chip_id == chip_types.chip_idh)
return true;
}
return false;
}
#if (!(FTS_UPGRADE_STRESS_TEST))
/************************************************************************
* Name: fts_ctpm_check_fw_status
* Brief: Check App is valid or not
* Input:
* Output:
* Return: -EIO - I2C communication error
* FTS_RUN_IN_APP - APP valid
* 0 - APP invalid
***********************************************************************/
static int fts_ctpm_check_fw_status(struct i2c_client *client)
{
u8 chip_id1 = 0;
u8 chip_id2 = 0;
int fw_status = FTS_RUN_IN_ERROR;
int i = 0;
int ret = 0;
int i2c_noack_retry = 0;
for (i = 0; i < 5; i++)
{
ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id1);
if (ret < 0)
{
i2c_noack_retry++;
continue;
}
ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id2);
if (ret < 0)
{
i2c_noack_retry++;
continue;
}
if ((chip_id1 == chip_types.chip_idh)
#if FTS_CHIP_IDC
&& (chip_id2 == chip_types.chip_idl)
#endif
)
{
fw_status = FTS_RUN_IN_APP;
break;
}
}
FTS_DEBUG("[UPGRADE]: chip_id = %02x%02x, chip_types.chip_idh = %02x%02x",
chip_id1, chip_id2, chip_types.chip_idh, chip_types.chip_idl);
/* I2C No ACK 5 times, then return -EIO */
if (i2c_noack_retry >= 5)
return -EIO;
/* I2C communication ok, but not get correct ID, need check pram/rom/bootloader */
if (i >= 5)
{
fw_status = fts_ctpm_get_pram_or_rom_id(client);
}
return fw_status;
}
/************************************************************************
* Name: fts_ctpm_check_fw_ver
* Brief: Check vendor id is valid or not
* Input:
* Output:
* Return: 1 - vendor id valid
* 0 - vendor id invalid
***********************************************************************/
static int fts_ctpm_check_fw_ver(struct i2c_client *client)
{
u8 uc_tp_fm_ver;
u8 uc_host_fm_ver = 0;
fts_i2c_read_reg(client, FTS_REG_FW_VER, &uc_tp_fm_ver);
uc_host_fm_ver = fts_ctpm_get_app_ver();
FTS_DEBUG("[UPGRADE]: uc_tp_fm_ver = 0x%x, uc_host_fm_ver = 0x%x!!", uc_tp_fm_ver, uc_host_fm_ver);
if (uc_tp_fm_ver < uc_host_fm_ver )
{
return 1;
}
else
{
return 0;
}
}
/************************************************************************
* Name: fts_ctpm_check_need_upgrade
* Brief:
* Input:
* Output:
* Return: 1 - Need upgrade
* 0 - No upgrade
***********************************************************************/
static int fts_ctpm_check_need_upgrade(struct i2c_client *client)
{
int fw_status = 0;
int bUpgradeFlag = false;
FTS_FUNC_ENTER();
/* 1. veriry FW APP is valid or not */
fw_status = fts_ctpm_check_fw_status(client);
FTS_DEBUG( "[UPGRADE]: fw_status = %d!!", fw_status);
if (fw_status < 0)
{
/* I2C no ACK, return immediately */
FTS_ERROR("[UPGRADE]******I2C NO ACK,exit upgrade******");
return -EIO;
}
else if (fw_status == FTS_RUN_IN_ERROR)
{
FTS_ERROR("[UPGRADE]******IC Type Fail******");
}
else if (fw_status == FTS_RUN_IN_APP)
{
FTS_INFO("[UPGRADE]**********FW APP valid**********");
if (fts_ctpm_get_i_file(client, 1) != 0)
{
FTS_DEBUG("[UPGRADE]******Get upgrade file(fw) fail******");
return -EIO;
}
if (fts_ctpm_check_fw_ver(client) == 1)
{
FTS_DEBUG("[UPGRADE]**********need upgrade fw**********");
bUpgradeFlag = true;
}
else
{
FTS_DEBUG("[UPGRADE]**********Don't need upgrade fw**********");
bUpgradeFlag = false;
}
}
else
{
/* if app is invalid, reset to run ROM */
FTS_INFO("[UPGRADE]**********FW APP invalid**********");
fts_ctpm_rom_or_pram_reset(client);
if (fts_ctpm_get_i_file(client, 0) != 0)
{
FTS_DEBUG("[UPGRADE]******Get upgrade file(flash) fail******");
fts_ctpm_rom_or_pram_reset(client);
return -EIO;
}
fts_ctpm_rom_or_pram_reset(client);
bUpgradeFlag = true;
}
FTS_FUNC_EXIT();
return bUpgradeFlag;
}
/************************************************************************
* Name: fts_ctpm_auto_upgrade
* Brief: auto upgrade
* Input:
* Output:
* Return: 0 - no upgrade
***********************************************************************/
int fts_ctpm_auto_upgrade(struct i2c_client *client)
{
u8 uc_tp_fm_ver;
int i_ret = 0;
int bUpgradeFlag = false;
u8 uc_upgrade_times = 0;
FTS_DEBUG("[UPGRADE]********************check upgrade need or not********************");
bUpgradeFlag = fts_ctpm_check_need_upgrade(client);
FTS_DEBUG("[UPGRADE]**********bUpgradeFlag = 0x%x**********", bUpgradeFlag);
if (bUpgradeFlag <= 0)
{
FTS_DEBUG("[UPGRADE]**********No Upgrade, exit**********");
return 0;
}
else
{
/* FW Upgrade */
do
{
uc_upgrade_times++;
FTS_DEBUG("[UPGRADE]********************star upgrade(%d)********************", uc_upgrade_times);
i_ret = fts_ctpm_fw_upgrade(client);
if (i_ret == 0)
{
/* upgrade success */
fts_i2c_read_reg(client, FTS_REG_FW_VER, &uc_tp_fm_ver);
FTS_DEBUG("[UPGRADE]********************Success upgrade to new fw version 0x%x********************", uc_tp_fm_ver);
fts_ctpm_auto_clb(client);
break;
}
else
{
/* upgrade fail, reset to run ROM BOOT..
* if app in flash is ok, TP will work success
*/
FTS_ERROR("[UPGRADE]********************upgrade fail, reset now********************");
fts_ctpm_rom_or_pram_reset(client);
}
}
while (uc_upgrade_times < 2); /* if upgrade fail, upgrade again. then return */
}
return i_ret;
}
#endif
#if FTS_AUTO_UPGRADE_FOR_LCD_CFG_EN
int fts_get_host_lic_ver(u8* ver)
{
int i_ret = 0;
if (fts_updatefun_curr.get_hlic_ver)
i_ret = fts_updatefun_curr.get_hlic_ver();
if (i_ret > 0)
*ver = i_ret;
return i_ret;
}
/* check if lcd init code need upgrade
* true-need false-no need
*/
static bool fts_lic_need_upgrade(struct i2c_client *client)
{
int ret = 0;
u8 initcode_ver_in_tp = 0;
u8 initcode_ver_in_host = 0;
bool fwvalid = false;
fwvalid = fts_check_fw_valid(client);
if( !fwvalid) {
FTS_INFO("fw is invalid, no upgrade lcd init code");
return false;
}
ret = fts_get_host_lic_ver(&initcode_ver_in_host);
if(ret < 0)
{
FTS_ERROR("init code in host invalid");
return false;
}
ret = fts_i2c_read_reg(client, 0xE4, &initcode_ver_in_tp);
if (ret < 0) {
FTS_ERROR("read reg0xE4 fail");
return false;
}
FTS_DEBUG("tp init ver:%x, fw init ver:%x", initcode_ver_in_tp, initcode_ver_in_host);
if(0xA5 == initcode_ver_in_tp) {
FTS_INFO("lcd init code ver is 0xA5, don't upgade init code");
return false;
}
else if(0xFF == initcode_ver_in_tp) {
FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code");
return true;
}
else if(initcode_ver_in_tp < initcode_ver_in_host)
return true;
else
return false;
}
int fts_lic_upgrade(struct i2c_client *client)
{
int ret = 0;
bool hlic_upgrade = false;
int upgrade_count = 0;
hlic_upgrade = fts_lic_need_upgrade(client);
FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade);
if(hlic_upgrade) {
FTS_INFO("lcd init code upgrade...");
do {
upgrade_count++;
ret = fts_ctpm_lcd_cfg_upgrade(client);
if(0 == ret) {
FTS_INFO("lcd init code upgrade succussfully");
break;
}
else {
fts_ctpm_rom_or_pram_reset(client);
}
}while (upgrade_count < 2);
}
return ret;
}
#endif /* FTS_AUTO_UPGRADE_FOR_LCD_CFG_EN */
#if FTS_AUTO_UPGRADE_EN
static void fts_ctpm_update_work_func(struct work_struct *work)
{
int i_ret = 0;
FTS_DEBUG( "[UPGRADE]******************************FTS enter upgrade******************************");
fts_irq_disable();
/* esd check */
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(DISABLE);
#endif
i_ret = fts_ctpm_auto_upgrade(fts_i2c_client);
if (i_ret < 0)
FTS_ERROR("[UPGRADE]**********TP FW upgrade failed**********");
#if FTS_AUTO_UPGRADE_FOR_LCD_CFG_EN
i_ret = fts_lic_upgrade(fts_i2c_client);
if (i_ret < 0)
FTS_ERROR("**********lcd init code upgrade failed**********");
#endif
#if FTS_ESDCHECK_EN
fts_esdcheck_switch(ENABLE);
#endif
fts_irq_enable();
FTS_DEBUG( "[UPGRADE]******************************FTS exit upgrade******************************");
}
/*****************************************************************************
* Name: fts_ctpm_upgrade_init
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
void fts_ctpm_upgrade_init(void)
{
FTS_FUNC_ENTER();
touch_wq = create_singlethread_workqueue("touch_wq");
if (touch_wq)
{
INIT_WORK(&fw_update_work, fts_ctpm_update_work_func);
queue_work(touch_wq, &fw_update_work);
}
else
{
FTS_ERROR("[UPGRADE]create_singlethread_workqueue failed\n");
}
FTS_FUNC_EXIT();
}
/*****************************************************************************
* Name: fts_ctpm_upgrade_exit
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
void fts_ctpm_upgrade_exit(void)
{
FTS_FUNC_ENTER();
destroy_workqueue(touch_wq);
FTS_FUNC_EXIT();
}
#endif /* #if FTS_AUTO_UPGRADE_EN */