unplugged-kernel/drivers/input/touchscreen/NT36532/nt36xxx_fw_update.c

880 lines
23 KiB
C
Raw Permalink Normal View History

/*
* Copyright (C) 2010 - 2022 Novatek, Inc.
*
* $Revision: 107367 $
* $Date: 2022-10-26 08:30:52 +0800 (, 26 2022) $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/firmware.h>
#include <linux/gpio.h>
#include "nt36xxx.h"
#if BOOT_UPDATE_FIRMWARE
static ktime_t start, end;
const struct firmware *fw_entry = NULL;
static size_t fw_need_write_size = 0;
static uint8_t *fwbuf = NULL;
struct nvt_ts_bin_map {
char name[12];
uint32_t BIN_addr;
uint32_t SRAM_addr;
uint32_t size;
uint32_t crc;
};
static struct nvt_ts_bin_map *bin_map;
extern void get_tp_info(void);
static int32_t nvt_get_fw_need_write_size(const struct firmware *fw_entry)
{
int32_t i = 0;
int32_t total_sectors_to_check = 0;
total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE;
for (i = total_sectors_to_check; i > 0; i--) {
/* check if there is end flag "NVT" at the end of this sector */
if (strncmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) {
fw_need_write_size = i * FLASH_SECTOR_SIZE;
NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n", fw_need_write_size, fw_need_write_size);
return 0;
}
/* check if there is end flag "MOD" at the end of this sector */
if (strncmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], "MOD", NVT_FLASH_END_FLAG_LEN) == 0) {
fw_need_write_size = i * FLASH_SECTOR_SIZE;
NVT_LOG("fw_need_write_size = %zu(0x%zx), MOD end flag\n", fw_need_write_size, fw_need_write_size);
return 0;
}
}
NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n");
return -1;
}
/*******************************************************
Description:
Novatek touchscreen init variable and allocate buffer
for download firmware function.
return:
n.a.
*******************************************************/
static int32_t nvt_download_init(void)
{
/* allocate buffer for transfer firmware */
//NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_TRANSFER_LEN);
if (fwbuf == NULL) {
fwbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN + 1 + DUMMY_BYTES), GFP_KERNEL);
if(fwbuf == NULL) {
NVT_ERR("kzalloc for fwbuf failed!\n");
return -ENOMEM;
}
}
return 0;
}
/*******************************************************
Description:
Novatek touchscreen checksum function. Calculate bin
file checksum for comparison.
return:
n.a.
*******************************************************/
static uint32_t CheckSum(const u8 *data, size_t len)
{
uint32_t i = 0;
uint32_t checksum = 0;
for (i = 0 ; i < len+1 ; i++)
checksum += data[i];
checksum += len;
checksum = ~checksum +1;
return checksum;
}
static uint32_t byte_to_word(const uint8_t *data)
{
return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
}
/*******************************************************
Description:
Novatek touchscreen parsing bin header function.
return:
n.a.
*******************************************************/
static uint32_t partition = 0;
static uint8_t ilm_dlm_num = 2;
static uint8_t cascade_2nd_header_info = 0;
static int32_t nvt_bin_header_parser(const u8 *fwdata, size_t fwsize)
{
uint32_t list = 0;
uint32_t pos = 0x00;
uint32_t end = 0x00;
uint8_t info_sec_num = 0;
uint8_t ovly_sec_num = 0;
uint8_t ovly_info = 0;
uint8_t find_bin_header = 0;
/* Find the header size */
end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24);
/* check cascade next header */
cascade_2nd_header_info = (fwdata[0x20] & 0x02) >> 1;
NVT_LOG("cascade_2nd_header_info = %d\n", cascade_2nd_header_info);
if (cascade_2nd_header_info) {
pos = 0x30; // info section start at 0x30 offset
while (pos < (end / 2)) {
info_sec_num ++;
pos += 0x10; /* each header info is 16 bytes */
}
info_sec_num = info_sec_num + 1; //next header section
} else {
pos = 0x30; // info section start at 0x30 offset
while (pos < end) {
info_sec_num ++;
pos += 0x10; /* each header info is 16 bytes */
}
}
/*
* Find the DLM OVLY section
* [0:3] Overlay Section Number
* [4] Overlay Info
*/
ovly_info = (fwdata[0x28] & 0x10) >> 4;
ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0;
/*
* calculate all partition number
* ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num
*/
partition = ilm_dlm_num + ovly_sec_num + info_sec_num;
NVT_LOG("ovly_info = %d, ilm_dlm_num = %d, ovly_sec_num = %d, info_sec_num = %d, partition = %d\n",
ovly_info, ilm_dlm_num, ovly_sec_num, info_sec_num, partition);
/* allocated memory for header info */
bin_map = (struct nvt_ts_bin_map *)kzalloc((partition+1) * sizeof(struct nvt_ts_bin_map), GFP_KERNEL);
if(bin_map == NULL) {
NVT_ERR("kzalloc for bin_map failed!\n");
return -ENOMEM;
}
for (list = 0; list < partition; list++) {
/*
* [1] parsing ILM & DLM header info
* BIN_addr : SRAM_addr : size (12-bytes)
* crc located at 0x18 & 0x1C
*/
if (list < ilm_dlm_num) {
bin_map[list].BIN_addr = byte_to_word(&fwdata[0 + list*12]);
bin_map[list].SRAM_addr = byte_to_word(&fwdata[4 + list*12]);
bin_map[list].size = byte_to_word(&fwdata[8 + list*12]);
if (ts->hw_crc)
bin_map[list].crc = byte_to_word(&fwdata[0x18 + list*4]);
else { //ts->hw_crc
if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size);
else {
NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size);
return -EINVAL;
}
} //ts->hw_crc
if (list == 0)
sprintf(bin_map[list].name, "ILM");
else if (list == 1)
sprintf(bin_map[list].name, "DLM");
}
/*
* [2] parsing others header info
* SRAM_addr : size : BIN_addr : crc (16-bytes)
*/
if ((list >= ilm_dlm_num) && (list < (ilm_dlm_num + info_sec_num))) {
if (find_bin_header == 0) {
/* others partition located at 0x30 offset */
pos = 0x30 + (0x10 * (list - ilm_dlm_num));
} else if (find_bin_header && cascade_2nd_header_info) {
/* cascade 2nd header info */
pos = end - 0x10;
}
bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]);
bin_map[list].size = byte_to_word(&fwdata[pos+4]);
bin_map[list].BIN_addr = byte_to_word(&fwdata[pos+8]);
if (ts->hw_crc)
bin_map[list].crc = byte_to_word(&fwdata[pos+12]);
else { //ts->hw_crc
if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size);
else {
NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size);
return -EINVAL;
}
} //ts->hw_crc
/* detect header end to protect parser function */
if ((bin_map[list].BIN_addr < end) && (bin_map[list].size != 0)) {
sprintf(bin_map[list].name, "Header");
find_bin_header = 1;
} else {
sprintf(bin_map[list].name, "Info-%d", (list - ilm_dlm_num));
}
}
/*
* [3] parsing overlay section header info
* SRAM_addr : size : BIN_addr : crc (16-bytes)
*/
if (list >= (ilm_dlm_num + info_sec_num)) {
/* overlay info located at DLM (list = 1) start addr */
pos = bin_map[1].BIN_addr + (0x10 * (list- ilm_dlm_num - info_sec_num));
bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]);
bin_map[list].size = byte_to_word(&fwdata[pos+4]);
bin_map[list].BIN_addr = byte_to_word(&fwdata[pos+8]);
if (ts->hw_crc)
bin_map[list].crc = byte_to_word(&fwdata[pos+12]);
else { //ts->hw_crc
if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
bin_map[list].crc = CheckSum(&fwdata[bin_map[list].BIN_addr], bin_map[list].size);
else {
NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size);
return -EINVAL;
}
} //ts->hw_crc
sprintf(bin_map[list].name, "Overlay-%d", (list- ilm_dlm_num - info_sec_num));
}
/* BIN size error detect */
if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) {
NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size);
return -EINVAL;
}
// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X), CRC (0x%08X)\n",
// list, bin_map[list].name,
// bin_map[list].SRAM_addr, bin_map[list].size, bin_map[list].BIN_addr, bin_map[list].crc);
}
return 0;
}
/*******************************************************
Description:
Novatek touchscreen release update firmware function.
return:
n.a.
*******************************************************/
static void update_firmware_release(void)
{
if (fw_entry) {
release_firmware(fw_entry);
}
fw_entry = NULL;
}
/*******************************************************
Description:
Novatek touchscreen request update firmware function.
return:
Executive outcomes. 0---succeed. -1,-22---failed.
*******************************************************/
static int32_t update_firmware_request(char *filename)
{
uint8_t retry = 0;
int32_t ret = 0;
if (NULL == filename) {
return -ENOENT;
}
while (1) {
NVT_LOG("filename is %s\n", filename);
ret = request_firmware(&fw_entry, filename, &ts->client->dev);
if (ret) {
NVT_ERR("firmware load failed, ret=%d\n", ret);
goto request_fail;
}
// check FW need to write size
if (nvt_get_fw_need_write_size(fw_entry)) {
NVT_ERR("get fw need to write size fail!\n");
ret = -EINVAL;
goto invalid;
}
// check if FW version add FW version bar equals 0xFF
if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) {
NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n");
NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET));
ret = -ENOEXEC;
goto invalid;
}
/* BIN Header Parser */
ret = nvt_bin_header_parser(fw_entry->data, fw_entry->size);
if (ret) {
NVT_ERR("bin header parser failed\n");
goto invalid;
} else {
break;
}
invalid:
update_firmware_release();
if (!IS_ERR_OR_NULL(bin_map)) {
kfree(bin_map);
bin_map = NULL;
}
request_fail:
retry++;
if(unlikely(retry > 2)) {
NVT_ERR("error, retry=%d\n", retry);
break;
}
}
return ret;
}
/*******************************************************
Description:
Novatek touchscreen write data to sram function.
- fwdata : The buffer is written
- SRAM_addr: The sram destination address
- size : Number of data bytes in @fwdata being written
- BIN_addr : The transferred data offset of @fwdata
return:
Executive outcomes. 0---succeed. else---fail.
*******************************************************/
static int32_t nvt_write_sram(const u8 *fwdata,
uint32_t SRAM_addr, uint32_t size, uint32_t BIN_addr)
{
int32_t ret = 0;
uint32_t i = 0;
uint16_t len = 0;
int32_t count = 0;
if (size % NVT_TRANSFER_LEN)
count = (size / NVT_TRANSFER_LEN) + 1;
else
count = (size / NVT_TRANSFER_LEN);
for (i = 0 ; i < count ; i++) {
len = (size < NVT_TRANSFER_LEN) ? size : NVT_TRANSFER_LEN;
//---set xdata index to start address of SRAM---
ret = nvt_set_page(SRAM_addr);
if (ret) {
NVT_ERR("set page failed, ret = %d\n", ret);
return ret;
}
//---write data into SRAM---
fwbuf[0] = SRAM_addr & 0x7F; //offset
memcpy(fwbuf+1, &fwdata[BIN_addr], len); //payload
ret = CTP_SPI_WRITE(ts->client, fwbuf, len+1);
if (ret) {
NVT_ERR("write to sram failed, ret = %d\n", ret);
return ret;
}
SRAM_addr += NVT_TRANSFER_LEN;
BIN_addr += NVT_TRANSFER_LEN;
size -= NVT_TRANSFER_LEN;
}
return ret;
}
/*******************************************************
Description:
Novatek touchscreen nvt_write_firmware function to write
firmware into each partition.
return:
n.a.
*******************************************************/
static int32_t nvt_write_firmware(const u8 *fwdata, size_t fwsize)
{
uint32_t list = 0;
char *name;
uint32_t BIN_addr, SRAM_addr, size;
int32_t ret = 0;
memset(fwbuf, 0, (NVT_TRANSFER_LEN+1));
for (list = 0; list < partition; list++) {
/* initialize variable */
SRAM_addr = bin_map[list].SRAM_addr;
size = bin_map[list].size;
BIN_addr = bin_map[list].BIN_addr;
name = bin_map[list].name;
// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X)\n",
// list, name, SRAM_addr, size, BIN_addr);
/* Check data size */
if ((BIN_addr + size) > fwsize) {
NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
BIN_addr, BIN_addr + size);
ret = -EINVAL;
goto out;
}
/* ignore reserved partition (Reserved Partition size is zero) */
if (!size)
continue;
else
size = size +1;
/* write data to SRAM */
ret = nvt_write_sram(fwdata, SRAM_addr, size, BIN_addr);
if (ret) {
NVT_ERR("sram program failed, ret = %d\n", ret);
goto out;
}
}
out:
return ret;
}
/*******************************************************
Description:
Novatek touchscreen check checksum function.
This function will compare file checksum and fw checksum.
return:
n.a.
*******************************************************/
static int32_t nvt_check_fw_checksum(void)
{
uint32_t fw_checksum = 0;
uint32_t len = partition*4;
uint32_t list = 0;
int32_t ret = 0;
memset(fwbuf, 0, (len+1));
//---set xdata index to checksum---
nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR);
/* read checksum */
fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F;
ret = CTP_SPI_READ(ts->client, fwbuf, len+1);
if (ret) {
NVT_ERR("Read fw checksum failed\n");
return ret;
}
/*
* Compare each checksum from fw
* ILM + DLM + Overlay + Info
* ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num
*/
for (list = 0; list < partition; list++) {
fw_checksum = byte_to_word(&fwbuf[1+list*4]);
/* ignore reserved partition (Reserved Partition size is zero) */
if(!bin_map[list].size)
continue;
if (bin_map[list].crc != fw_checksum) {
NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n",
list, bin_map[list].crc, fw_checksum);
ret = -EIO;
}
}
return ret;
}
/*******************************************************
Description:
Novatek touchscreen set bootload crc reg bank function.
This function will set hw crc reg before enable crc function.
return:
n.a.
*******************************************************/
static void nvt_set_bld_crc_bank(uint32_t DES_ADDR, uint32_t SRAM_ADDR,
uint32_t LENGTH_ADDR, uint32_t size,
uint32_t G_CHECKSUM_ADDR, uint32_t crc)
{
/* write destination address */
nvt_set_page(DES_ADDR);
fwbuf[0] = DES_ADDR & 0x7F;
fwbuf[1] = (SRAM_ADDR) & 0xFF;
fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF;
fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF;
CTP_SPI_WRITE(ts->client, fwbuf, 4);
/* write length */
//nvt_set_page(LENGTH_ADDR);
fwbuf[0] = LENGTH_ADDR & 0x7F;
fwbuf[1] = (size) & 0xFF;
fwbuf[2] = (size >> 8) & 0xFF;
fwbuf[3] = (size >> 16) & 0xFF;
if (ts->hw_crc == HWCRC_LEN_2Bytes) {
CTP_SPI_WRITE(ts->client, fwbuf, 3);
} else if (ts->hw_crc >= HWCRC_LEN_3Bytes) {
CTP_SPI_WRITE(ts->client, fwbuf, 4);
}
/* write golden dlm checksum */
//nvt_set_page(G_CHECKSUM_ADDR);
fwbuf[0] = G_CHECKSUM_ADDR & 0x7F;
fwbuf[1] = (crc) & 0xFF;
fwbuf[2] = (crc >> 8) & 0xFF;
fwbuf[3] = (crc >> 16) & 0xFF;
fwbuf[4] = (crc >> 24) & 0xFF;
CTP_SPI_WRITE(ts->client, fwbuf, 5);
return;
}
/*******************************************************
Description:
Novatek touchscreen set BLD hw crc function.
This function will set ILM and DLM crc information to register.
return:
n.a.
*******************************************************/
static void nvt_set_bld_hw_crc(void)
{
/* [0] ILM */
/* write register bank */
nvt_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, bin_map[0].SRAM_addr,
ts->mmap->ILM_LENGTH_ADDR, bin_map[0].size,
ts->mmap->G_ILM_CHECKSUM_ADDR, bin_map[0].crc);
/* [1] DLM */
/* write register bank */
nvt_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, bin_map[1].SRAM_addr,
ts->mmap->DLM_LENGTH_ADDR, bin_map[1].size,
ts->mmap->G_DLM_CHECKSUM_ADDR, bin_map[1].crc);
}
/*******************************************************
Description:
Novatek touchscreen read BLD hw crc info function.
This function will check crc results from register.
return:
n.a.
*******************************************************/
static void nvt_read_bld_hw_crc(void)
{
uint8_t buf[8] = {0};
uint32_t g_crc = 0, r_crc = 0;
/* CRC Flag */
nvt_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR);
buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F;
buf[1] = 0x00;
CTP_SPI_READ(ts->client, buf, 2);
NVT_ERR("crc_done = %d, ilm_crc_flag = %d, dlm_crc_flag = %d\n",
(buf[1] >> 2) & 0x01, (buf[1] >> 0) & 0x01, (buf[1] >> 1) & 0x01);
/* ILM CRC */
nvt_set_page(ts->mmap->G_ILM_CHECKSUM_ADDR);
buf[0] = ts->mmap->G_ILM_CHECKSUM_ADDR & 0x7F;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x00;
CTP_SPI_READ(ts->client, buf, 5);
g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR);
buf[0] = ts->mmap->R_ILM_CHECKSUM_ADDR & 0x7F;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x00;
CTP_SPI_READ(ts->client, buf, 5);
r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
NVT_ERR("ilm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n",
bin_map[0].crc, g_crc, r_crc);
/* DLM CRC */
nvt_set_page(ts->mmap->G_DLM_CHECKSUM_ADDR);
buf[0] = ts->mmap->G_DLM_CHECKSUM_ADDR & 0x7F;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x00;
CTP_SPI_READ(ts->client, buf, 5);
g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
nvt_set_page(ts->mmap->R_DLM_CHECKSUM_ADDR);
buf[0] = ts->mmap->R_DLM_CHECKSUM_ADDR & 0x7F;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
buf[4] = 0x00;
CTP_SPI_READ(ts->client, buf, 5);
r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
NVT_ERR("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n",
bin_map[1].crc, g_crc, r_crc);
return;
}
/*******************************************************
Description:
Novatek touchscreen Download_Firmware with HW CRC
function. It's complete download firmware flow.
return:
Executive outcomes. 0---succeed. else---fail.
*******************************************************/
static int32_t nvt_download_firmware_hw_crc(void)
{
uint8_t retry = 0;
int32_t ret = 0;
start = ktime_get();
while (1) {
/* bootloader reset to reset MCU */
nvt_bootloader_reset();
/* set ilm & dlm reg bank */
nvt_set_bld_hw_crc();
/* Start to write firmware process */
if (cascade_2nd_header_info) {
/* for cascade */
nvt_tx_auto_copy_mode();
ret = nvt_write_firmware(fw_entry->data, fw_entry->size);
if (ret) {
NVT_ERR("Write_Firmware failed. (%d)\n", ret);
goto fail;
}
ret = nvt_wait_auto_copy();
if (ret) {
NVT_ERR("wait auto copy failed. (%d)\n", ret);
goto fail;
}
} else {
ret = nvt_write_firmware(fw_entry->data, fw_entry->size);
if (ret) {
NVT_ERR("Write_Firmware failed. (%d)\n", ret);
goto fail;
}
}
/* clear fw reset status & enable fw crc check */
nvt_fw_crc_enable();
/* Set Boot Ready Bit */
nvt_boot_ready();
ret = nvt_check_fw_reset_state(RESET_STATE_INIT);
if (ret) {
NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret);
goto fail;
} else {
break;
}
fail:
retry++;
if(unlikely(retry > 2)) {
NVT_ERR("error, retry=%d\n", retry);
nvt_read_bld_hw_crc();
break;
}
}
end = ktime_get();
return ret;
}
/*******************************************************
Description:
Novatek touchscreen Download_Firmware function. It's
complete download firmware flow.
return:
n.a.
*******************************************************/
static int32_t nvt_download_firmware(void)
{
uint8_t retry = 0;
int32_t ret = 0;
start = ktime_get();
while (1) {
/*
* Send eng reset cmd before download FW
* Keep TP_RESX low when send eng reset cmd
*/
#if NVT_TOUCH_SUPPORT_HW_RST
gpio_set_value(ts->reset_gpio, 0);
mdelay(1); //wait 1ms
#endif
nvt_eng_reset();
#if NVT_TOUCH_SUPPORT_HW_RST
gpio_set_value(ts->reset_gpio, 1);
mdelay(10); //wait tRT2BRST after TP_RST
#endif
nvt_bootloader_reset();
/* clear fw reset status */
nvt_write_addr(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE, 0x00);
/* Start to write firmware process */
ret = nvt_write_firmware(fw_entry->data, fw_entry->size);
if (ret) {
NVT_ERR("Write_Firmware failed. (%d)\n", ret);
goto fail;
}
/* Set Boot Ready Bit */
nvt_boot_ready();
ret = nvt_check_fw_reset_state(RESET_STATE_INIT);
if (ret) {
NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret);
goto fail;
}
/* check fw checksum result */
ret = nvt_check_fw_checksum();
if (ret) {
NVT_ERR("firmware checksum not match, retry=%d\n", retry);
goto fail;
} else {
break;
}
fail:
retry++;
if(unlikely(retry > 2)) {
NVT_ERR("error, retry=%d\n", retry);
break;
}
}
end = ktime_get();
return ret;
}
/*******************************************************
Description:
Novatek touchscreen update firmware main function.
return:
n.a.
*******************************************************/
int32_t nvt_update_firmware(char *firmware_name)
{
int32_t ret = 0;
// request bin file in "/etc/firmware"
ret = update_firmware_request(firmware_name);
if (ret) {
NVT_ERR("update_firmware_request failed. (%d)\n", ret);
goto request_firmware_fail;
}
/* initial buffer and variable */
ret = nvt_download_init();
if (ret) {
NVT_ERR("Download Init failed. (%d)\n", ret);
goto download_fail;
}
/* download firmware process */
if (ts->hw_crc)
ret = nvt_download_firmware_hw_crc();
else
ret = nvt_download_firmware();
if (ret) {
NVT_ERR("Download Firmware failed. (%d)\n", ret);
goto download_fail;
}
NVT_LOG("Update firmware success! <%ld us>\n",
(long) ktime_us_delta(end, start));
/* Get FW Info */
ret = nvt_get_fw_info();
if (ret) {
NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret);
}
download_fail:
if (!IS_ERR_OR_NULL(bin_map)) {
kfree(bin_map);
bin_map = NULL;
}
update_firmware_release();
request_firmware_fail:
return ret;
}
/*******************************************************
Description:
Novatek touchscreen update firmware when booting
function.
return:
n.a.
*******************************************************/
void Boot_Update_Firmware(struct work_struct *work)
{
mutex_lock(&ts->lock);
nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME);
get_tp_info();
mutex_unlock(&ts->lock);
}
#endif /* BOOT_UPDATE_FIRMWARE */