kernel-brax3-ubuntu-touch/drivers/input/touchscreen/focaltech_ft8725_spi/focaltech_flash_spi.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

1452 lines
40 KiB
C
Executable file

/*
*
* FocalTech fts TouchScreen driver.
*
* Copyright (c) 2012-2020, 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: Focaltech Driver Team
*
* Created: 2017-12-06
*
* Abstract:
*
* Reference:
*
*****************************************************************************/
/*****************************************************************************
* 1.Included header files
*****************************************************************************/
#include "focaltech_core.h"
#include "focaltech_flash.h"
//drv add hardware info -20240717-start
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
#include "../../../misc/mediatek/prize/hardware_info/hardware_info.h"
extern struct hardware_info current_tp_info;
#endif
//drv add hardware info -20240717-end
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_FW_REQUEST_SUPPORT 1
/* Example: focaltech_ts_fw_tianma.bin */
#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_"
#define FTS_READ_BOOT_ID_TIMEOUT 3
#define FTS_FLASH_PACKET_LENGTH_SPI_LOW (4 * 1024 - 4)
#define FTS_FLASH_PACKET_LENGTH_SPI (32 * 1024 - 16)
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
u8 fw_file[] = {
#include FTS_UPGRADE_FW_FILE
};
u8 fw_file2[] = {
#include FTS_UPGRADE_FW2_FILE
};
u8 fw_file3[] = {
#include FTS_UPGRADE_FW3_FILE
};
struct upgrade_module module_list[] = {
{FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)},
{FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)},
{FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)},
};
struct upgrade_setting_nf upgrade_setting_list[] = {
{0x87, 0x19, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 1, 0, 0},
{0x86, 0x22, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 0, 0, 0},
{0x87, 0x56, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x80, 0x09, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x86, 0x32, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0},
{0x86, 0x42, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0},
{0x87, 0x20, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x87, 0x22, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x82, 0x01, 0, (96 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0},
{0xF0, 0xC6, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x56, 0x62, 0, (128 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 4, 0, 0, 5},
{0x82, 0x05, 0, (120 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0},
{0x80, 0x57, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x80, 0xC7, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x23, 0x89, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x87, 0x25, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0},
{0x82, 0x06, 0, (128 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0},
{0x82, 0xC5, 0, (128 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0},
};
struct fts_upgrade *fwupgrade;
static int fts_check_bootid(void)
{
int ret = 0;
u8 cmd = 0;
u8 id[2] = { 0 };
struct fts_upgrade *upg = fwupgrade;
struct ft_chip_t *chip_id;
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
chip_id = &upg->ts_data->ic_info.ids;
cmd = FTS_CMD_READ_ID;
ret = fts_read(&cmd, 1, id, 2);
if (ret < 0) {
FTS_ERROR("read boot id(0x%02x 0x%02x) fail", id[0], id[1]);
return ret;
}
FTS_INFO("read boot id:0x%02x 0x%02x", id[0], id[1]);
if ((chip_id->rom_idh == id[0]) && (chip_id->rom_idl == id[1])) {
return 0;
}
return -EIO;
}
static int fts_enter_into_boot(void)
{
int ret = 0;
int i = 0;
int j = 0;
u8 cmd[2] = { 0 };
struct fts_upgrade *upg = fwupgrade;
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
FTS_INFO("enter into boot environment");
for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
/* hardware tp reset to boot */
fts_reset_proc(upg->ts_data, true, 0);
mdelay(upg->setting_nf->delay_init + i * 2);
/* enter into boot & check boot id*/
for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) {
cmd[0] = FTS_CMD_START1;
ret = fts_write(cmd, 1);
if (ret >= 0) {
fts_msleep(upg->setting_nf->delay_init);
ret = fts_check_bootid();
if (0 == ret) {
FTS_INFO("boot id check pass, retry=%d", i);
return 0;
}
}
}
}
return -EIO;
}
static bool fts_check_fast_download(void)
{
int ret = 0;
u8 cmd[6] = {0xF2, 0x00, 0x78, 0x0A, 0x00, 0x02};
u8 value = 0;
u8 value2[2] = { 0 };
ret = fts_read_reg(0xdb, &value);
if (ret < 0) {
FTS_ERROR("read 0xdb fail");
goto read_err;
}
ret = fts_read(cmd, 6, value2, 2);
if (ret < 0) {
FTS_ERROR("read f2 fail");
goto read_err;
}
FTS_INFO("0xdb = 0x%x, 0xF2 = 0x%x", value, value2[0]);
if ((value >= 0x18) && (value2[0] == 0x55)) {
FTS_INFO("IC support fast-download");
return true;
}
read_err:
FTS_INFO("IC not support fast-download");
return false;
}
static int fts_dpram_write_pe(u32 saddr, const u8 *buf, u32 len, bool wpram)
{
int ret = 0;
int i = 0;
int j = 0;
u8 *cmd = NULL;
u32 addr = 0;
u32 offset = 0;
u32 remainder = 0;
u32 packet_number = 0;
u32 packet_len = 0;
u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI;
bool fd_support = true;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("dpram write");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (!buf) {
FTS_ERROR("fw buf is null");
return -EINVAL;
}
if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) {
FTS_ERROR("fw length(%d) fail", len);
return -EINVAL;
}
if (upg->setting_nf->fd_check) {
fd_support = fts_check_fast_download();
if (!fd_support)
packet_size = FTS_FLASH_PACKET_LENGTH_SPI_LOW;
}
cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1);
if (NULL == cmd) {
FTS_ERROR("malloc memory for pram write buffer fail");
return -ENOMEM;
}
memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1);
packet_number = len / packet_size;
remainder = len % packet_size;
if (remainder > 0)
packet_number++;
packet_len = packet_size;
FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder);
cmd[0] = FTS_ROMBOOT_CMD_WRITE;
for (i = 0; i < packet_number; i++) {
offset = i * packet_size;
addr = saddr + offset;
cmd[1] = BYTE_OFF_16(addr);
cmd[2] = BYTE_OFF_8(addr);
cmd[3] = BYTE_OFF_0(addr);
/* last packet */
if ((i == (packet_number - 1)) && remainder)
packet_len = remainder;
cmd[4] = BYTE_OFF_8(packet_len);
cmd[5] = BYTE_OFF_0(packet_len);
for (j = 0; j < packet_len; j++) {
cmd[FTS_CMD_WRITE_LEN + j] = buf[offset + j];
}
ret = fts_write(&cmd[0], FTS_CMD_WRITE_LEN + packet_len);
if (ret < 0) {
FTS_ERROR("write fw to pram(%d) fail", i);
goto write_pram_err;
}
if (!fd_support)
fts_msleep(3);
}
write_pram_err:
if (cmd) {
vfree(cmd);
cmd = NULL;
}
return ret;
}
static int fts_dpram_write(u32 saddr, const u8 *buf, u32 len, bool wpram)
{
int ret = 0;
int i = 0;
int j = 0;
u8 *cmd = NULL;
u32 addr = 0;
u32 baseaddr = wpram ? FTS_PRAM_SADDR : FTS_DRAM_SADDR;
u32 offset = 0;
u32 remainder = 0;
u32 packet_number = 0;
u32 packet_len = 0;
u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("dpram write");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (!buf) {
FTS_ERROR("fw buf is null");
return -EINVAL;
}
if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) {
FTS_ERROR("fw length(%d) fail", len);
return -EINVAL;
}
cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1);
if (NULL == cmd) {
FTS_ERROR("malloc memory for pram write buffer fail");
return -ENOMEM;
}
memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1);
packet_number = len / packet_size;
remainder = len % packet_size;
if (remainder > 0)
packet_number++;
packet_len = packet_size;
FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder);
for (i = 0; i < packet_number; i++) {
offset = i * packet_size;
addr = saddr + offset + baseaddr;
/* last packet */
if ((i == (packet_number - 1)) && remainder)
packet_len = remainder;
/* set pram address */
cmd[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR;
cmd[1] = BYTE_OFF_16(addr);
cmd[2] = BYTE_OFF_8(addr);
cmd[3] = BYTE_OFF_0(addr);
ret = fts_write(&cmd[0], FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN);
if (ret < 0) {
FTS_ERROR("set pram(%d) addr(%d) fail", i, addr);
goto write_pram_err;
}
/* write pram data */
cmd[0] = FTS_ROMBOOT_CMD_WRITE;
for (j = 0; j < packet_len; j++) {
cmd[1 + j] = buf[offset + j];
}
ret = fts_write(&cmd[0], 1 + packet_len);
if (ret < 0) {
FTS_ERROR("write fw to pram(%d) fail", i);
goto write_pram_err;
}
}
write_pram_err:
if (cmd) {
vfree(cmd);
cmd = NULL;
}
return ret;
}
static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value)
{
int ret = 0;
int i = 0;
u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 };
u8 value[2] = { 0 };
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("ecc calc in tp");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
cmd[0] = FTS_ROMBOOT_CMD_ECC;
cmd[1] = BYTE_OFF_16(ecc_saddr);
cmd[2] = BYTE_OFF_8(ecc_saddr);
cmd[3] = BYTE_OFF_0(ecc_saddr);
cmd[4] = BYTE_OFF_16(ecc_len);
cmd[5] = BYTE_OFF_8(ecc_len);
cmd[6] = BYTE_OFF_0(ecc_len);
/* make boot to calculate ecc in pram */
ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN);
if (ret < 0) {
FTS_ERROR("ecc calc cmd fail");
return ret;
}
fts_msleep(2);
/* wait boot calculate ecc finish */
if (upg->setting_nf->ecc_delay) {
fts_msleep(upg->setting_nf->ecc_delay);
} else {
cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH;
for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) {
ret = fts_read(cmd, 1, value, 1);
if (ret < 0) {
FTS_ERROR("ecc finish cmd fail");
return ret;
}
if (upg->setting_nf->eccok_val == value[0])
break;
fts_msleep(1);
}
if (i >= FTS_ECC_FINISH_TIMEOUT) {
FTS_ERROR("wait ecc finish timeout,ecc_finish=%x", value[0]);
return -EIO;
}
}
/* get ecc value calculate in boot */
cmd[0] = FTS_ROMBOOT_CMD_ECC_READ;
ret = fts_read(cmd, 1, value, 2);
if (ret < 0) {
FTS_ERROR("ecc read cmd fail");
return ret;
}
*ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF;
return 0;
}
static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value)
{
u16 ecc = 0;
u32 i = 0;
u32 j = 0;
u16 al2_fcs_coef = AL2_FCS_COEF;
for (i = 0; i < data_len; i += 2 ) {
ecc ^= ((data[i] << 8) | (data[i + 1]));
for (j = 0; j < 16; j ++) {
if (ecc & 0x01)
ecc = (u16)((ecc >> 1) ^ al2_fcs_coef);
else
ecc >>= 1;
}
}
*ecc_value = ecc & 0x0000FFFF;
return 0;
}
static int fts_ecc_check(const u8 *buf, u32 len, u32 ecc_saddr)
{
int ret = 0;
int i = 0;
u16 ecc_in_host = 0;
u16 ecc_in_tp = 0;
int packet_length = 0;
int packet_number = 0;
int packet_remainder = 0;
int offset = 0;
u32 packet_size = FTS_MAX_LEN_FILE;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("ecc check");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (upg->setting_nf->ecclen_max) {
packet_size = upg->setting_nf->ecclen_max;
}
packet_number = len / packet_size;
packet_remainder = len % packet_size;
if (packet_remainder)
packet_number++;
packet_length = packet_size;
for (i = 0; i < packet_number; i++) {
/* last packet */
if ((i == (packet_number - 1)) && packet_remainder)
packet_length = packet_remainder;
ret = fts_ecc_cal_host(buf + offset, packet_length, &ecc_in_host);
if (ret < 0) {
FTS_ERROR("ecc in host calc fail");
return ret;
}
ret = fts_ecc_cal_tp(ecc_saddr + offset, packet_length, &ecc_in_tp);
if (ret < 0) {
FTS_ERROR("ecc in tp calc fail");
return ret;
}
FTS_DEBUG("ecc in tp:%04x,host:%04x,i:%d", ecc_in_tp, ecc_in_host, i);
if (ecc_in_tp != ecc_in_host) {
FTS_ERROR("ecc_in_tp(%x) != ecc_in_host(%x), ecc check fail",
ecc_in_tp, ecc_in_host);
return -EIO;
}
offset += packet_length;
}
return 0;
}
static int fts_pram_write_ecc(const u8 *buf, u32 len)
{
int ret = 0;
u32 pram_app_size = 0;
u16 code_len = 0;
u16 code_len_n = 0;
u32 pram_start_addr = 0;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("begin to write pram app(bin len:%d)", len);
if (!upg || !upg->setting_nf) {
FTS_ERROR("upgrade/setting_nf is null");
return -EINVAL;
}
/* get pram app length */
code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8)
+ buf[FTS_APP_INFO_OFFSET + 1];
code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8)
+ buf[FTS_APP_INFO_OFFSET + 3];
if ((code_len + code_len_n) != 0xFFFF) {
FTS_ERROR("pram code len(%x %x) fail", code_len, code_len_n);
return -EINVAL;
}
pram_app_size = ((u32)code_len) * upg->setting_nf->length_coefficient;
FTS_INFO("pram app length in fact:%d", pram_app_size);
/* write pram */
if (upg->setting_nf->spi_pe)
ret = fts_dpram_write_pe(pram_start_addr, buf, pram_app_size, true);
else
ret = fts_dpram_write(pram_start_addr, buf, pram_app_size, true);
if (ret < 0) {
FTS_ERROR("write pram fail");
return ret;
}
/* check ecc */
ret = fts_ecc_check(buf, pram_app_size, pram_start_addr);
if (ret < 0) {
FTS_ERROR("pram ecc check fail");
return ret;
}
FTS_INFO("pram app write successfully");
return 0;
}
static int fts_dram_write_ecc(const u8 *buf, u32 len)
{
int ret = 0;
u32 dram_size = 0;
u32 pram_app_size = 0;
u32 dram_start_addr = 0;
u16 const_len = 0;
u16 const_len_n = 0;
const u8 *dram_buf = NULL;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("begin to write dram data(bin len:%d)", len);
if (!upg || !upg->setting_nf) {
FTS_ERROR("upgrade/setting_nf is null");
return -EINVAL;
}
/* get dram data length */
const_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0x8] << 8)
+ buf[FTS_APP_INFO_OFFSET + 0x9];
const_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 0x0A] << 8)
+ buf[FTS_APP_INFO_OFFSET + 0x0B];
if (((const_len + const_len_n) != 0xFFFF) || (const_len == 0)) {
FTS_INFO("no support dram,const len(%x %x)", const_len, const_len_n);
return 0;
}
dram_size = ((u32)const_len) * upg->setting_nf->length_coefficient;
pram_app_size = ((u32)(((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8)
+ buf[FTS_APP_INFO_OFFSET + 1]));
pram_app_size = pram_app_size * upg->setting_nf->length_coefficient;
dram_buf = buf + pram_app_size;
FTS_INFO("dram buf length in fact:%d,offset:%d", dram_size, pram_app_size);
/* write pram */
ret = fts_dpram_write(dram_start_addr, dram_buf, dram_size, false);
if (ret < 0) {
FTS_ERROR("write dram fail");
return ret;
}
/* check ecc */
ret = fts_ecc_check(dram_buf, dram_size, dram_start_addr);
if (ret < 0) {
FTS_ERROR("dram ecc check fail");
return ret;
}
FTS_INFO("dram data write successfully");
return 0;
}
static int fts_pram_start(void)
{
int ret = 0;
u8 cmd = FTS_ROMBOOT_CMD_START_APP;
FTS_INFO("remap to start pram");
ret = fts_write(&cmd, 1);
if (ret < 0) {
FTS_ERROR("write start pram cmd fail");
return ret;
}
fts_msleep(10);
return 0;
}
/*
* description: download fw to IC and run
*
* param - buf: const, fw data buffer
* len: length of fw
*
* return 0 if success, otherwise return error code
*/
static int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset)
{
int ret = 0;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("begin to write and start fw(bin len:%d)", len);
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
upg->ts_data->fw_is_running = false;
if (need_reset) {
/* enter into boot environment */
ret = fts_enter_into_boot();
if (ret < 0) {
FTS_ERROR("enter into boot environment fail");
return ret;
}
}
/* write pram */
ret = fts_pram_write_ecc(buf, len);
if (ret < 0) {
FTS_ERROR("write pram fail");
return ret;
}
if (upg->setting_nf->drwr_support) {
/* write dram */
ret = fts_dram_write_ecc(buf, len);
if (ret < 0) {
FTS_ERROR("write dram fail");
return ret;
}
}
/* remap pram and run fw */
ret = fts_pram_start();
if (ret < 0) {
FTS_ERROR("pram start fail");
return ret;
}
upg->ts_data->fw_is_running = true;
FTS_INFO("fw download successfully");
return 0;
}
static int fts_fw_download(const u8 *buf, u32 len, bool need_reset)
{
int ret = 0;
int i = 0;
int irq_need_recovery = false;
int esd_need_recovery = false;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("fw upgrade download function");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (!buf || (len < FTS_MIN_LEN)) {
FTS_ERROR("fw/len(%d) is invalid", len);
return -EINVAL;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return -EINVAL;
}
upg->ts_data->fw_loading = 1;
if (!upg->ts_data->irq_disabled) {
fts_irq_disable();
irq_need_recovery = true;
}
if (fts_esdcheck_is_running(upg->ts_data)) {
fts_esdcheck_switch(upg->ts_data, DISABLE);
esd_need_recovery = true;
}
for (i = 0; i < 3; i++) {
FTS_INFO("fw download times:%d", i + 1);
ret = fts_fw_write_start(buf, len, need_reset);
if (0 == ret)
break;
}
if (i >= 3) {
FTS_ERROR("fw download fail");
ret = -EIO;
goto err_fw_download;
}
if (esd_need_recovery) fts_esdcheck_switch(upg->ts_data, ENABLE);
ret = 0;
err_fw_download:
if (irq_need_recovery) fts_irq_enable();
upg->ts_data->fw_loading = 0;
return ret;
}
static int fts_read_file_default(char *file_name, u8 **file_buf)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
int ret = 0;
char file_path[FILE_NAME_LENGTH] = { 0 };
struct file *filp = NULL;
struct inode *inode;
mm_segment_t old_fs;
loff_t pos;
loff_t file_len = 0;
if ((NULL == file_name) || (NULL == file_buf)) {
FTS_ERROR("filename/filebuf is NULL");
return -EINVAL;
}
snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name);
filp = filp_open(file_path, O_RDONLY, 0);
if (IS_ERR(filp)) {
FTS_ERROR("open %s file fail", file_path);
return -ENOENT;
}
#if 1
inode = filp->f_inode;
#else
/* reserved for linux earlier verion */
inode = filp->f_dentry->d_inode;
#endif
file_len = inode->i_size;
*file_buf = (u8 *)vmalloc(file_len);
if (NULL == *file_buf) {
FTS_ERROR("file buf malloc fail");
filp_close(filp, NULL);
return -ENOMEM;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
ret = vfs_read(filp, *file_buf, file_len , &pos);
if (ret < 0)
FTS_ERROR("read file fail");
FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos);
filp_close(filp, NULL);
set_fs(old_fs);
return ret;
#else
FTS_INFO("not support vfs_read to get fw file");
return -EINVAL;
#endif
}
static int fts_read_file_request_firmware(char *file_name, u8 **file_buf)
{
#if FTS_FW_REQUEST_SUPPORT
int ret = 0;
const struct firmware *fw = NULL;
char fwname[FILE_NAME_LENGTH] = { 0 };
struct fts_upgrade *upg = fwupgrade;
snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name);
ret = request_firmware(&fw, fwname, upg->ts_data->dev);
if (0 == ret) {
FTS_INFO("firmware(%s) request successfully", fwname);
*file_buf = vmalloc(fw->size);
if (NULL == *file_buf) {
FTS_ERROR("fw buffer vmalloc fail");
ret = -ENOMEM;
} else {
memcpy(*file_buf, fw->data, fw->size);
ret = fw->size;
}
} else {
FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
ret = -EIO;
}
if (fw != NULL) {
release_firmware(fw);
fw = NULL;
}
return ret;
#else
FTS_INFO("not support request_firmware to get fw file");
return -EINVAL;
#endif
}
static int fts_read_file(char *file_name, u8 **file_buf)
{
int ret = 0;
ret = fts_read_file_request_firmware(file_name, file_buf);
if (ret < 0) {
ret = fts_read_file_default(file_name, file_buf);
if (ret < 0) {
FTS_INFO("get fw file(default) abnormal");
return ret;
}
}
return ret;
}
int fts_upgrade_bin(char *fw_name, bool force)
{
int ret = 0;
u32 fw_file_len = 0;
u8 *fw_file_buf = NULL;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("start upgrade with fw bin");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return -EINVAL;
}
ret = fts_read_file(fw_name, &fw_file_buf);
if ((ret < 0) || (ret < FTS_MIN_LEN)) {
FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret);
goto err_bin;
}
fw_file_len = ret;
FTS_INFO("fw bin file len:%d", fw_file_len);
ret = fts_fw_download(fw_file_buf, fw_file_len, true);
if (ret < 0) {
FTS_ERROR("upgrade fw bin failed");
goto err_bin;
}
FTS_INFO("upgrade fw bin success");
err_bin:
if (fw_file_buf) {
vfree(fw_file_buf);
fw_file_buf = NULL;
}
return ret;
}
int fts_enter_test_environment(bool test_state)
{
int ret = 0;
u8 detach_flag = 0;
u32 app_offset = 0;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("fw test download function");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upgrade/ts_data/setting_nf is null");
return -EINVAL;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return -EINVAL;
}
if (!upg->fw || (upg->fw_length <= upg->setting_nf->app2_offset)) {
FTS_INFO("not multi-app");
return 0;
}
if (test_state) {
app_offset = upg->setting_nf->app2_offset;
}
/*download firmware*/
ret = fts_fw_download(upg->fw + app_offset, upg->fw_length, true);
if (ret < 0) {
FTS_ERROR("upgrade test FW failed");
return ret;
}
fts_msleep(50);
ret = fts_read_reg(FTS_REG_FW_MODE, &detach_flag);
FTS_INFO("regb4:0x%02x", detach_flag);
return 0;
}
static int fts_fw_resume(bool need_reset, enum FW_TYPE fw_type)
{
int ret = 0;
struct fts_upgrade *upg = fwupgrade;
const struct firmware *fw = NULL;
char fwname[FILE_NAME_LENGTH] = { 0 };
bool get_fw_i_flag = true;
const u8 *fw_buf = NULL;
u32 fwlen = 0;
u32 app_off = 0;
FTS_INFO("fw upgrade resume function");
#if !FTS_AUTO_UPGRADE_EN
FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade");
return 0;
#endif
if (!upg || !upg->fw) {
FTS_ERROR("upg/fw is null");
return -EINVAL;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return -EINVAL;
}
if (FTS_FW_REQUEST_SUPPORT) {
snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \
FTS_FW_NAME_PREX_WITH_REQUEST, upg->module_info->vendor_name);
ret = request_firmware(&fw, fwname, upg->ts_data->dev);
if (ret == 0) {
FTS_INFO("firmware(%s) request successfully", fwname);
fw_buf = fw->data;
fwlen = fw->size;
get_fw_i_flag = false;
} else {
FTS_ERROR("%s:firmware(%s) request fail,ret=%d\n",
__func__, fwname, ret);
}
}
if (get_fw_i_flag) {
FTS_INFO("download fw from bootimage");
fw_buf = upg->fw;
fwlen = upg->fw_length;
}
if (!fw_buf || (fwlen < FTS_MIN_LEN)) {
FTS_ERROR("fw/len(%d) is invalid", fwlen);
return -EINVAL;
}
if ((fw_type == FW_GESTURE) ||
((fw_type == FW_AUTO) && upg->ts_data->gesture_support && upg->ts_data->suspended)) {
/*Need download gesture firmware*/
if (fwlen <= (upg->setting_nf->app2_offset * 2)) {
FTS_INFO("not support gesture-app");
ret = 0;
goto _release_firmware;
}
FTS_INFO("get gesture-app");
app_off = upg->setting_nf->app2_offset * 2;
}
ret = fts_fw_download(fw_buf + app_off, fwlen - app_off, need_reset);
if (ret < 0) {
FTS_ERROR("upgrade fw(resume) failed");
goto _release_firmware;
}
ret = 0;
_release_firmware:
if (FTS_FW_REQUEST_SUPPORT) {
if (fw != NULL) {
release_firmware(fw);
fw = NULL;
}
}
return ret;
}
/* true:fw is normal fw*/
static bool fts_check_fw_normal(void)
{
int i = 0;
int max_retries = FTS_MAX_RETRIES_READID_RESUME;
u8 val = 0;
u8 boot_state = 0;
struct fts_upgrade *upg = fwupgrade;
struct ft_chip_t *chip_id = &upg->ts_data->ic_info.ids;
for (i = 0; i < max_retries; i++) {
fts_read_reg(FTS_REG_CHIP_ID, &val);
if ((val == chip_id->chip_idh) || (fts_check_cid(upg->ts_data, val) == 0)) {
FTS_INFO("TP Ready,Read ID=0x%02x", val);
#if (FTS_MULTI_FW_NUM > 1)
fts_read_reg(FTS_REG_FW_MODE, &val);
if ((FW_MODE_FACTORY == val) || (FW_MODE_GESTURE == val)) {
FTS_INFO("FW(%x) need upgrade", val);
return false;
}
#endif
return true;
} else {
upg->ts_data->fw_is_running = false;
fts_read_reg(FTS_CMD_READ_BOOT_STATE, &boot_state);
FTS_INFO("Read BOOT state=0x%02x", boot_state);
if ((boot_state == upg->setting_nf->upgsts_boot) && (0 == fts_check_bootid())) {
FTS_INFO("boot state:0x%x,need upgrade", boot_state);
upg->ts_data->fw_is_running = true;
return false;
}
upg->ts_data->fw_is_running = true;
}
if ((i + 1) < max_retries) fts_msleep((i + 1) * 20);
}
return false;
}
int fts_enter_gesture_fw(void)
{
u8 fw_mode = 0;
struct fts_upgrade *upg = fwupgrade;
FTS_FUNC_ENTER();
if (fts_fw_resume(true, FW_GESTURE) == 0) {
fts_tp_state_recovery(upg->ts_data);
fts_read_reg(FTS_REG_FW_MODE, &fw_mode);
FTS_INFO("FW Mode:0x%02x", fw_mode);
} else {
FTS_ERROR("download gesture firmware failed");
}
FTS_FUNC_EXIT();
return 0;
}
int fts_enter_normal_fw(void)
{
FTS_FUNC_ENTER();
if (fts_check_fw_normal()) {
FTS_INFO("FW works normally");
} else {
if (fts_fw_resume(true, FW_NORMAL) == 0) {
fts_wait_tp_to_valid();
} else {
FTS_ERROR("download normal firmware failed");
}
}
FTS_FUNC_EXIT();
return 0;
}
/* work thread for TP driver to recover FW when TP FW is lost */
static void fts_fwrecover_work(struct work_struct *work)
{
u8 boot_state = 0;
struct fts_upgrade *upg = container_of(work, struct fts_upgrade, fwrecover_work);
FTS_FUNC_ENTER();
upg->ts_data->fw_is_running = false;
fts_read_reg(FTS_CMD_READ_BOOT_STATE, &boot_state);
if ((boot_state == upg->setting_nf->upgsts_boot) && (0 == fts_check_bootid())) {
FTS_INFO("abnormal situation,to download fw");
fts_fw_resume(false, FW_AUTO);
fts_tp_state_recovery(upg->ts_data);
FTS_INFO("FW recovery pass");
}
upg->ts_data->fw_is_running = true;
FTS_FUNC_EXIT();
}
int fts_fw_recovery(void)
{
int ret = 0;
u8 boot_state = 0;
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("check if boot recovery");
if (!upg || !upg->ts_data || !upg->setting_nf) {
FTS_ERROR("upg/ts_data/setting_nf is null");
return -EINVAL;
}
if (!upg->ts_data->ts_workqueue || !upg->fwrecover_work.func) {
FTS_ERROR("ts_workqueue/work.func is NULL");
return -EINVAL;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return -EINVAL;
}
upg->ts_data->fw_is_running = false;
ret = fts_check_bootid();
if (ret < 0) {
FTS_ERROR("check boot id fail");
upg->ts_data->fw_is_running = true;
return ret;
}
ret = fts_read_reg(FTS_CMD_READ_BOOT_STATE, &boot_state);
if (ret < 0) {
FTS_ERROR("read boot state failed, ret=%d", ret);
upg->ts_data->fw_is_running = true;
return ret;
}
if (boot_state != upg->setting_nf->upgsts_boot) {
FTS_INFO("not in boot mode(0x%x),exit", boot_state);
upg->ts_data->fw_is_running = true;
return -EIO;
}
FTS_INFO("abnormal situation,need download fw");
queue_work(upg->ts_data->ts_workqueue, &upg->fwrecover_work);
return 0;
}
/* work thread for LCD driver to call fw loading of TP driver */
static void fts_fwload_work(struct work_struct *work)
{
u8 chip_id = 0xFF;
FTS_FUNC_ENTER();
fts_fw_resume(true, FW_NORMAL);
fts_read_reg(FTS_REG_CHIP_ID, &chip_id);
FTS_INFO("read chip id:0x%02x", chip_id);
FTS_FUNC_EXIT();
}
#ifdef IDC_LOADFW_IN_LCD_DRIVER
/* Only for LCD driver to call, only for IDC chip */
int fts_load_fw_init(void)
{
struct fts_upgrade *upg = fwupgrade;
FTS_INFO("LCD driver calls FW loading function of TP driver.");
if (!upg || !upg->ts_data) {
FTS_ERROR("upg/ts_data is null");
return -EINVAL;
}
if (!upg->ts_data->ts_workqueue || !upg->fwload_work.func) {
FTS_ERROR("ts_workqueue/work.func is NULL, can't upgrade");
return -EINVAL;
}
queue_work(upg->ts_data->ts_workqueue, &upg->fwload_work);
return 0;
}
EXPORT_SYMBOL(fts_load_fw_init);
#endif
static int fts_fwupg_get_module_info(struct fts_upgrade *upg)
{
int i = 0;
struct upgrade_module *info = &module_list[0];
if (!upg || !upg->ts_data) {
FTS_ERROR("upg/ts_data is null");
return -EINVAL;
}
if (FTS_GET_MODULE_NUM > 1) {
FTS_INFO("module id:%04x", upg->module_id);
for (i = 0; i < FTS_GET_MODULE_NUM; i++) {
info = &module_list[i];
if (upg->module_id == info->id) {
FTS_INFO("module id match, get fw file successfully");
break;
}
}
if (i >= FTS_GET_MODULE_NUM) {
FTS_ERROR("no module id match, don't get file");
return -ENODATA;
}
}
upg->module_info = info;
return 0;
}
static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg)
{
int ret = 0;
const struct firmware *fw = NULL;
u8 *tmpbuf = NULL;
char fwname[FILE_NAME_LENGTH] = { 0 };
snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \
FTS_FW_NAME_PREX_WITH_REQUEST, \
upg->module_info->vendor_name);
ret = request_firmware(&fw, fwname, upg->ts_data->dev);
if (0 == ret) {
FTS_INFO("firmware(%s) request successfully", fwname);
tmpbuf = vmalloc(fw->size);
if (NULL == tmpbuf) {
FTS_ERROR("fw buffer vmalloc fail");
ret = -ENOMEM;
} else {
memcpy(tmpbuf, fw->data, fw->size);
upg->fw = tmpbuf;
upg->fw_length = fw->size;
upg->fw_from_request = 1;
}
} else {
FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
}
if (fw != NULL) {
release_firmware(fw);
fw = NULL;
}
return ret;
}
static int fts_get_fw_file_via_i(struct fts_upgrade *upg)
{
upg->fw = upg->module_info->fw_file;
upg->fw_length = upg->module_info->fw_len;
upg->fw_from_request = 0;
return 0;
}
/*****************************************************************************
* Name: fts_fwupg_get_fw_file
* Brief: get fw image/file,
* If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_-
* MODULE_ID/FTS_MODULE_NAME;
* If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F-
* TS_MODULE_ID; will use module id to distingwish different modules;
* If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT
* =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt-
* ech_ts_fw_" & FTS_MODULE_NAME;
*
* If have flash, module_id=vendor_id, If non-flash,module_id need
* transfer from LCD driver(gpio or lcm_id or ...);
* Input:
* Output:
* Return: return 0 if success, otherwise return error code
*****************************************************************************/
static int fts_fwupg_get_fw_file(struct fts_upgrade *upg)
{
int ret = 0;
bool get_fw_i_flag = false;
FTS_DEBUG("get upgrade fw file");
if (!upg || !upg->ts_data) {
FTS_ERROR("upg/ts_data is null");
return -EINVAL;
}
ret = fts_fwupg_get_module_info(upg);
if ((ret < 0) || (!upg->module_info)) {
FTS_ERROR("get module info fail");
return ret;
}
if (FTS_FW_REQUEST_SUPPORT) {
fts_msleep(500);
ret = fts_get_fw_file_via_request_firmware(upg);
if (ret != 0) {
get_fw_i_flag = true;
}
} else {
get_fw_i_flag = true;
}
if (get_fw_i_flag) {
ret = fts_get_fw_file_via_i(upg);
}
FTS_INFO("upgrade fw file len:%d", upg->fw_length);
if (upg->fw_length < FTS_MIN_LEN) {
FTS_ERROR("fw file len(%d) fail", upg->fw_length);
return -ENODATA;
}
return ret;
}
static void fts_fwupg_work(struct work_struct *work)
{
int ret = 0;
u8 chip_id = 0;
//drv add hardware info -20240717-start
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
u8 chip_id1 = 0;
#endif
//drv add hardware info -20240717-end
struct fts_upgrade *upg = fwupgrade;
#if !FTS_AUTO_UPGRADE_EN
FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on");
return ;
#endif
FTS_INFO("fw upgrade work function");
if (!upg || !upg->ts_data) {
FTS_ERROR("upg/ts_data is null");
return ;
}
/* get fw */
ret = fts_fwupg_get_fw_file(upg);
if (ret < 0) {
FTS_ERROR("get file fail, can't upgrade");
return ;
}
if (upg->ts_data->fw_loading) {
FTS_INFO("fw is loading, not download again");
return ;
}
ret = fts_fw_download(upg->fw, upg->fw_length, true);
if (ret < 0) {
FTS_ERROR("fw auto download failed");
} else {
fts_msleep(50);
ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id);
FTS_INFO("read chip id:0x%02x", chip_id);
//drv add hardware info -20240717-start
#if IS_ENABLED(CONFIG_PRIZE_HARDWARE_INFO)
ret = fts_read_reg(FTS_REG_FW_VER, &chip_id1);
if ((ret < 0) || (chip_id1 == 0xFF) || (chip_id1 == 0x00)){
strcpy(current_tp_info.chip, "FT8057S,Firmware version:get tp fw version fail!\n");
}
else
{
sprintf(current_tp_info.chip, "FT8725,Firmware version:0x%02x",chip_id1);
}
sprintf(current_tp_info.id,"0x%02x%02x", upg->ts_data->ic_info.ids.chip_idh,upg->ts_data->ic_info.ids.chip_idl);
strcpy(current_tp_info.vendor, "Focaltech");
sprintf(current_tp_info.more, "%d*%d",2408,1080);
#endif
//drv add hardware info -20240717-end
fts_esdcheck_switch(upg->ts_data, ENABLE);
}
}
int fts_fwupg_init(struct fts_ts_data *ts_data)
{
int i = 0;
struct upgrade_setting_nf *setting = &upgrade_setting_list[0];
int setting_count =
sizeof(upgrade_setting_list) / sizeof(upgrade_setting_list[0]);
FTS_INFO("fw upgrade init function");
if (!ts_data || !ts_data->ts_workqueue) {
FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function");
return -EINVAL;
}
if (0 == setting_count) {
FTS_ERROR("no upgrade settings in tp driver, init fail");
return -ENODATA;
}
fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL);
if (NULL == fwupgrade) {
FTS_ERROR("malloc memory for upgrade fail");
return -ENOMEM;
}
if (1 == setting_count) {
fwupgrade->setting_nf = setting;
} else {
for (i = 0; i < setting_count; i++) {
setting = &upgrade_setting_list[i];
if ((setting->rom_idh == ts_data->ic_info.ids.rom_idh)
&& (setting->rom_idl == ts_data->ic_info.ids.rom_idl)) {
FTS_INFO("match upgrade setting,type(ID):0x%02x%02x",
setting->rom_idh, setting->rom_idl);
fwupgrade->setting_nf = setting;
}
}
}
if (NULL == fwupgrade->setting_nf) {
FTS_ERROR("no upgrade settings match, can't upgrade");
kfree(fwupgrade);
fwupgrade = NULL;
return -ENODATA;
}
fts_esdcheck_switch(ts_data, DISABLE);
fwupgrade->ts_data = ts_data;
INIT_WORK(&fwupgrade->fwupg_work, fts_fwupg_work);
INIT_WORK(&fwupgrade->fwrecover_work, fts_fwrecover_work);
INIT_WORK(&fwupgrade->fwload_work, fts_fwload_work);
queue_work(ts_data->ts_workqueue, &fwupgrade->fwupg_work);
return 0;
}
int fts_fwupg_exit(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (fwupgrade) {
cancel_work_sync(&fwupgrade->fwupg_work);
cancel_work_sync(&fwupgrade->fwrecover_work);
cancel_work_sync(&fwupgrade->fwload_work);
if (fwupgrade->fw_from_request) {
vfree(fwupgrade->fw);
fwupgrade->fw = NULL;
}
kfree(fwupgrade);
fwupgrade = NULL;
}
FTS_FUNC_EXIT();
return 0;
}