kernel_samsung_a34x-permissive/drivers/input/touchscreen/NT36672C/nt36xxx_fw_update.c
2024-04-28 15:51:13 +02:00

1015 lines
25 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include <linux/firmware.h>
#include <linux/gpio.h>
#include "nt36xxx.h"
#if BOOT_UPDATE_FIRMWARE
#define SIZE_4KB 4096
#define FLASH_SECTOR_SIZE SIZE_4KB
#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB)
#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1)
#define NVT_FLASH_END_FLAG_LEN 3
#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN)
#define NVT_DUMP_PARTITION (0)
#define NVT_DUMP_PARTITION_LEN (1024)
#define NVT_DUMP_PARTITION_PATH "/data/local/tmp"
static struct timeval start, end;
const struct firmware *fw_entry;
static size_t fw_need_write_size;
static uint8_t *fwbuf;
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;
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), 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;
static uint8_t ilm_dlm_num = 2;
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;
/* Find the header size */
end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24);
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))) {
/* others partition located at 0x30 offset */
pos = 0x30 + (0x10 * (list - ilm_dlm_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
/* detect header end to protect parser function */
if ((bin_map[list].BIN_addr == 0) && (bin_map[list].size != 0)) {
sprintf(bin_map[list].name, "Header");
} 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;
}
#if NVT_DUMP_PARTITION
/*******************************************************
Description:
Novatek touchscreen dump flash partition function.
return:
n.a.
*******************************************************/
loff_t file_offset;
static int32_t nvt_read_ram_and_save_file(uint32_t addr, uint16_t len, char *name)
{
char file[256] = "";
uint8_t *fbufp = NULL;
int32_t ret = 0;
struct file *fp = NULL;
mm_segment_t org_fs;
sprintf(file, "%s/dump_%s.bin", NVT_DUMP_PARTITION_PATH, name);
NVT_LOG("Dump [%s] from 0x%08X to 0x%08X\n", file, addr, addr + len);
fbufp = (uint8_t *) kzalloc(len + 1, GFP_KERNEL);
if (fbufp == NULL) {
NVT_ERR("kzalloc for fbufp failed!\n");
ret = -ENOMEM;
goto alloc_buf_fail;
}
org_fs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open(file, O_RDWR | O_CREAT, 0644);
if (fp == NULL || IS_ERR(fp)) {
ret = -ENOMEM;
NVT_ERR("open file failed\n");
goto open_file_fail;
}
/* SPI read */
//---set xdata index to addr---
nvt_set_page(addr);
fbufp[0] = addr & 0x7F; //offset
CTP_SPI_READ(ts->client, fbufp, len + 1);
/* Write to file */
ret = vfs_write(fp, (char __user *)fbufp + 1, len, &file_offset);
if (ret != len) {
NVT_ERR("write file failed\n");
goto open_file_fail;
} else {
ret = 0;
}
open_file_fail:
set_fs(org_fs);
if (!IS_ERR_OR_NULL(fp)) {
filp_close(fp, NULL);
fp = NULL;
}
if (!IS_ERR_OR_NULL(fbufp)) {
kfree(fbufp);
fbufp = NULL;
}
alloc_buf_fail:
return ret;
}
/*******************************************************
Description:
Novatek touchscreen nvt_dump_partition function to dump
each partition for debug.
return:
n.a.
*******************************************************/
static int32_t nvt_dump_partition(void)
{
uint32_t list = 0;
char *name;
uint32_t SRAM_addr, size;
uint32_t i = 0;
uint16_t len = 0;
int32_t count = 0;
int32_t ret = 0;
if (NVT_DUMP_PARTITION_LEN >= sizeof(ts->rbuf)) {
NVT_ERR("dump len %d is larger than buffer size %ld\n",
NVT_DUMP_PARTITION_LEN, sizeof(ts->rbuf));
return -EINVAL;
} else if (NVT_DUMP_PARTITION_LEN >= NVT_TRANSFER_LEN) {
NVT_ERR("dump len %d is larger than NVT_TRANSFER_LEN\n", NVT_DUMP_PARTITION_LEN);
return -EINVAL;
}
if (bin_map == NULL) {
NVT_ERR("bin_map is NULL\n");
return -ENOMEM;
}
memset(fwbuf, 0, (NVT_DUMP_PARTITION_LEN + 1));
for (list = 0; list < partition; list++) {
/* initialize variable */
SRAM_addr = bin_map[list].SRAM_addr;
size = bin_map[list].size;
name = bin_map[list].name;
/* ignore reserved partition (Reserved Partition size is zero) */
if (!size)
continue;
else
size = size + 1;
/* write data to SRAM */
if (size % NVT_DUMP_PARTITION_LEN)
count = (size / NVT_DUMP_PARTITION_LEN) + 1;
else
count = (size / NVT_DUMP_PARTITION_LEN);
for (i = 0; i < count; i++) {
len = (size < NVT_DUMP_PARTITION_LEN) ? size : NVT_DUMP_PARTITION_LEN;
/* dump for debug download firmware */
ret = nvt_read_ram_and_save_file(SRAM_addr, len, name);
if (ret < 0) {
NVT_ERR("nvt_read_ram_and_save_file failed, ret = %d\n", ret);
goto out;
}
SRAM_addr += NVT_DUMP_PARTITION_LEN;
size -= NVT_DUMP_PARTITION_LEN;
}
file_offset = 0;
}
out:
return ret;
}
#endif /* NVT_DUMP_PARTITION */
/*******************************************************
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) & 0x01;
if (ts->hw_crc == 1) {
CTP_SPI_WRITE(ts->client, fwbuf, 3);
} else if (ts->hw_crc > 1) {
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;
do_gettimeofday(&start);
while (1) {
/* bootloader reset to reset MCU */
nvt_bootloader_reset();
/* 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;
}
#if NVT_DUMP_PARTITION
ret = nvt_dump_partition();
if (ret) {
NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret);
}
#endif
/* set ilm & dlm reg bank */
nvt_set_bld_hw_crc();
/* enable hw bld crc function */
nvt_bld_crc_enable();
/* 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;
}
}
do_gettimeofday(&end);
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;
do_gettimeofday(&start);
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;
}
#if NVT_DUMP_PARTITION
ret = nvt_dump_partition();
if (ret) {
NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret);
}
#endif
/* 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;
}
}
do_gettimeofday(&end);
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",
(end.tv_sec - start.tv_sec) * 1000000L + (end.tv_usec - start.tv_usec));
/* 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 kthread_work *work)
{
mutex_lock(&ts->lock);
nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME);
mutex_unlock(&ts->lock);
}
#endif /* BOOT_UPDATE_FIRMWARE */