6db4831e98
Android 14
1015 lines
25 KiB
C
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 */
|