blob: efaf1dc7c79f5a8119fd7fe731bbcc4103a262c5 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 - 2021 Novatek, Inc.
*
* $Revision: 77624 $
* $Date: 2021-02-05 10:03:05 +0800 (週五, 05 二月 2021) $
*
* 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 SPI_FLASH
#if BOOT_UPDATE_FIRMWARE
const struct firmware *fw_entry;
size_t fw_need_write_size;
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;
/* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */
for (i = total_sectors_to_check; i > 0; i--) {
/* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */
/* 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)\n", fw_need_write_size, fw_need_write_size);
return 0;
}
}
NVT_ERR("end flag \"NVT\" not found!\n");
return -EINVAL;
}
/*******************************************************
Description:
Novatek touchscreen request update firmware function.
return:
Executive outcomes. 0---succeed. -1,-22---failed.
*******************************************************/
int32_t update_firmware_request(const char *filename)
{
int32_t ret = 0;
if (filename == NULL) {
return -EINVAL;
}
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);
return ret;
}
// 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");
return -EINVAL;
}
// 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));
return -EINVAL;
}
return 0;
}
/*******************************************************
Description:
Novatek touchscreen release update firmware function.
return:
n.a.
*******************************************************/
void update_firmware_release(void)
{
if (fw_entry) {
release_firmware(fw_entry);
}
fw_entry = NULL;
}
/*******************************************************
Description:
Novatek touchscreen check firmware version function.
return:
Executive outcomes. 0---need update. 1---need not
update.
*******************************************************/
int32_t Check_FW_Ver(void)
{
uint8_t buf[16] = {0};
int32_t ret = 0;
//write i2c index to EVENT BUF ADDR
ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
if (ret < 0) {
NVT_ERR("i2c write error!(%d)\n", ret);
return ret;
}
//read Firmware Version
buf[0] = EVENT_MAP_FWINFO;
buf[1] = 0x00;
buf[2] = 0x00;
ret = CTP_SPI_READ(ts->client, buf, 3);
if (ret < 0) {
NVT_ERR("i2c read error!(%d)\n", ret);
return ret;
}
NVT_LOG("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]);
NVT_LOG("Bin 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]);
// check IC FW_VER + FW_VER_BAR equals 0xFF or not, need to update if not
if ((buf[1] + buf[2]) != 0xFF) {
NVT_ERR("IC FW_VER + FW_VER_BAR not equals to 0xFF!\n");
return 0;
}
// compare IC and binary FW version
if (buf[1] == fw_entry->data[FW_BIN_VER_OFFSET]) {
NVT_LOG("firmware version match\n");
return 1;
} else {
return 0;
}
}
int32_t Check_CheckSum(const struct firmware *fw_entry)
{
return Check_CheckSum_GCM(fw_entry);
}
int32_t Update_Firmware(const struct firmware *fw_entry)
{
return Update_Firmware_GCM(fw_entry);
}
int32_t nvt_check_flash_end_flag(void)
{
return nvt_check_flash_end_flag_gcm();
}
/*******************************************************
Description:
Novatek touchscreen update firmware when booting
function.
return:
n.a.
*******************************************************/
void Boot_Update_Firmware(struct work_struct *work)
{
int32_t ret = 0;
// request bin file in "/etc/firmware"
ret = update_firmware_request(get_fw_name());
if (ret) {
NVT_ERR("update_firmware_request failed. (%d)\n", ret);
complete_all(&ts->fwu_done);
return;
}
mutex_lock(&ts->lock);
ts->fw_update_in_process = true;
#if NVT_TOUCH_ESD_PROTECT
nvt_esd_check_enable(false);
#endif /* #if NVT_TOUCH_ESD_PROTECT */
ret = Check_CheckSum(fw_entry);
if (ts->force_fw_update) {
NVT_LOG("force fw update\n");
Update_Firmware(fw_entry);
} else if (ret < 0) { // read firmware checksum failed
NVT_ERR("read firmware checksum failed\n");
Update_Firmware(fw_entry);
} else if ((ret == 0) || (Check_FW_Ver() == 0)) { // fw checksum not match || fw version not match
NVT_LOG("firmware version not match\n");
Update_Firmware(fw_entry);
} else if (nvt_check_flash_end_flag()) {
NVT_LOG("check flash end flag failed\n");
Update_Firmware(fw_entry);
} else {
// Bootloader Reset
nvt_clear_fw_reset_state();
nvt_bootloader_reset();
ret = nvt_check_fw_reset_state(RESET_STATE_INIT);
if (ret) {
NVT_LOG("check fw reset state failed\n");
Update_Firmware(fw_entry);
} else {
NVT_LOG("do not need to update firmware\n");
}
}
ts->fw_update_in_process = false;
mutex_unlock(&ts->lock);
complete_all(&ts->fwu_done);
update_firmware_release();
}
#endif /* BOOT_UPDATE_FIRMWARE */
#else /* SPI_FLASH */
#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)
#undef NVT_FW_UPDATE_PROFILING
static ktime_t start, end;
static bool is_mp_fw;
const struct firmware *mp_fw_entry;
const struct firmware *ts_fw_entry;
const struct firmware *fw_entry;
static size_t fw_need_write_size;
static uint8_t *fwbuf;
static uint8_t cascade_2nd_header_info;
static uint8_t spi_dma_div_cnt_val;
static uint32_t partition;
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;
/* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */
for (i = total_sectors_to_check; i > 0; i--) {
/* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */
/* 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 -EPERM;
}
/*******************************************************
Description:
Novatek touchscreen init variable and allocate buffer
for download firmware function.
return:
n.a.
*******************************************************/
static inline 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 *)devm_kzalloc(&ts->client->dev,
(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 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;
uint8_t ilm_dlm_num = 2;
/* 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
#if defined(CONFIG_SOC_GOOGLE)
if (nvt_ts_check_tid(ts, tid_nt36523n))
spi_dma_div_cnt_val = fwdata[0x29] & 0x01;
#endif
} 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 *)devm_kzalloc(&ts->client->dev,
(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)
snprintf(bin_map[list].name, sizeof(bin_map[list].name), "ILM");
else if (list == 1)
snprintf(bin_map[list].name, sizeof(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)) {
snprintf(bin_map[list].name, sizeof(bin_map[list].name), "Header");
find_bin_header = 1;
} else {
snprintf(bin_map[list].name, sizeof(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
snprintf(bin_map[list].name, sizeof(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.
*******************************************************/
void update_firmware_release(void)
{
if (fw_entry) {
NVT_LOG("Release %s FW.\n", (is_mp_fw) ? "MP" : "TS");
release_firmware(fw_entry);
fw_entry = NULL;
if (is_mp_fw)
mp_fw_entry = NULL;
else
ts_fw_entry = NULL;
}
if (!IS_ERR_OR_NULL(bin_map)) {
NVT_LOG("Free bin_map.\n");
devm_kfree(&ts->client->dev, bin_map);
bin_map = NULL;
}
}
/*******************************************************
Description:
Novatek touchscreen request update firmware function.
return:
Executive outcomes. 0---succeed. -1,-22---failed.
*******************************************************/
static int32_t update_firmware_request(const char *filename)
{
uint8_t retry = 0;
int32_t ret = 0;
if (NULL == filename) {
return -ENOENT;
}
while (1) {
NVT_LOG("filename is %s\n", filename);
/* Reuse fw_entry if possible. */
if (is_mp_fw)
fw_entry = mp_fw_entry;
else
fw_entry = ts_fw_entry;
/* FW loading. */
if (!fw_entry) {
NVT_LOG("Request %s FW.\n", (is_mp_fw) ? "MP" : "TS");
ret = request_firmware(&fw_entry, filename, &ts->client->dev);
if (ret) {
NVT_ERR("firmware load failed, ret=%d\n", ret);
goto request_fail;
}
/* Store fw_entry to reuse later. */
if (is_mp_fw)
mp_fw_entry = fw_entry;
else
ts_fw_entry = fw_entry;
}
// 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;
}
/* Free bin_map to reallocate for new FW if exist. */
if (!IS_ERR_OR_NULL(bin_map)) {
NVT_LOG("Free bin_map for new FW.\n");
devm_kfree(&ts->client->dev, bin_map);
bin_map = NULL;
}
/* 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();
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, uint8_t full)
{
uint32_t list = 0;
char *name;
uint32_t BIN_addr, SRAM_addr, size;
int32_t ret = 0;
NVT_LOG("full download = %d\n", full);
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;
/* ignore ilm (list-0) if not full download */
if ((full == false) && (list == 0))
continue;
// 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_LOG("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_LOG("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_LOG("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n",
bin_map[1].crc, g_crc, r_crc);
return;
}
/*******************************************************
Description:
Novatek touchscreen check DMA hw crc function.
This function will check hw crc result is pass or not.
return:
n.a.
*******************************************************/
static int32_t nvt_check_dma_hw_crc(void)
{
uint8_t retry = 0, crc_flag = 0, retry_max = 20;
int32_t ret = 0;
/* write register bank */
nvt_set_bld_crc_bank(ts->mmap->BLD_DES_ADDR, bin_map[0].SRAM_addr,
ts->mmap->BLD_LENGTH_ADDR, bin_map[0].size,
ts->mmap->G_DLM_CHECKSUM_ADDR, bin_map[0].crc);
/* bld dma crc enable */
nvt_set_page(ts->mmap->DMA_CRC_EN_ADDR);
fwbuf[0] = ts->mmap->DMA_CRC_EN_ADDR & 0x7F;
fwbuf[1] = 0x01; //enable
CTP_SPI_WRITE(ts->client, fwbuf, 2);
/* polling for dma crc check finish */
while (1) {
nvt_set_page(ts->mmap->DMA_CRC_FLAG_ADDR);
fwbuf[0] = ts->mmap->DMA_CRC_FLAG_ADDR & 0x7F;
fwbuf[1] = 0xFF;
ret = CTP_SPI_READ(ts->client, fwbuf, 2);
if (ret) {
NVT_ERR("Read dma crc flag failed\n");
return ret;
}
mdelay(1);
/*
* [Bit-0] DMA_CRC_FLAG: 0-DMA CRC pass, 1-DMA CRC error
* [Bit-1] DMA_CRC_DONE: 0-CRC checking, 1-finish
*/
crc_flag = (fwbuf[1] & 0x03);
if ((crc_flag == 0x02) || (crc_flag == 0x03))
break;
retry++;
if (retry > retry_max)
break;
}
if (crc_flag != 0x02) {
NVT_ERR("[0][%s] dma crc error 0x%02X, retry %d\n",
bin_map[0].name, crc_flag, retry);
ret = -EIO;
}
return ret;
}
static void nvt_spi_dma_setup(void)
{
uint8_t buf[33] = {0};
if (ts->mmap->SPI_DMA_VAL_ADDR) {
nvt_set_page(bin_map[1].SRAM_addr);
buf[0] = bin_map[1].SRAM_addr & 0x7F;
CTP_SPI_WRITE(ts->client, buf, 33);
nvt_set_page(ts->mmap->SPI_DMA_VAL_ADDR);
buf[0] = ts->mmap->SPI_DMA_VAL_ADDR & 0x7F;
buf[1] = 0x35;
buf[2] = 0x32;
buf[3] = 0xAA;
buf[4] = 0x00;
CTP_SPI_WRITE(ts->client, buf, 5);
NVT_LOG("set spi dma val finish\n");
} else {
NVT_ERR("spi dma val addr is NULL\n");
}
}
/*******************************************************
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(uint8_t full)
{
uint8_t retry = 0;
int32_t ret = 0;
start = ktime_get();
while (1) {
/* bootloader reset to reset MCU */
nvt_bootloader_reset();
/* check ilm crc if not doing full download */
if (!full) {
if (nvt_check_dma_hw_crc()) {
NVT_ERR("ILM crc is not correct. Enable full download\n");
full = true;
}
}
/* set ilm & dlm reg bank */
nvt_set_bld_hw_crc();
/* Start to write firmware process */
if (cascade_2nd_header_info) {
/* for cascade */
#if defined(CONFIG_SOC_GOOGLE)
if (nvt_ts_check_tid(ts, tid_nt36523n) && spi_dma_div_cnt_val)
nvt_spi_dma_setup();
#endif
nvt_tx_auto_copy_mode();
ret = nvt_write_firmware(fw_entry->data, fw_entry->size, full);
if (ret) {
NVT_ERR("Write_Firmware failed. (%d)\n", ret);
goto fail;
}
ret = nvt_check_spi_dma_tx_info();
if (ret) {
NVT_ERR("spi dma tx info failed. (%d)\n", ret);
goto fail;
}
} else {
ret = nvt_write_firmware(fw_entry->data, fw_entry->size, full);
if (ret) {
NVT_ERR("Write_Firmware failed. (%d)\n", ret);
goto fail;
}
}
/* 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:
full = true;
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(uint8_t full)
{
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
if (gpio_is_valid(ts->reset_gpio)) {
gpio_set_value(ts->reset_gpio, 0);
mdelay(1); //wait 1ms
}
#endif
nvt_eng_reset();
#if NVT_TOUCH_SUPPORT_HW_RST
if (gpio_is_valid(ts->reset_gpio)) {
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, full);
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(const char *firmware_name, uint8_t full)
{
int32_t ret = 0;
#if defined(NVT_FW_UPDATE_PROFILING)
ktime_t profile_start = ktime_get();
ktime_t profile_end;
#endif
ts->fw_update_in_process = true;
if (strncmp(firmware_name, get_fw_name(),
strlen(get_fw_name())) == 0) {
is_mp_fw = false;
} else {
is_mp_fw = true;
}
/* request bin file in "/vendor/firmware" */
ret = update_firmware_request(firmware_name);
if (ret) {
NVT_ERR("update_firmware_request failed. (%d)\n", ret);
goto request_firmware_fail;
}
#if defined(NVT_FW_UPDATE_PROFILING)
profile_end = ktime_get();
NVT_LOG("update_firmware_request() time us delta %lld\n",
ktime_us_delta(profile_end, profile_start));
profile_start = profile_end;
#endif
/* initial buffer and variable */
ret = nvt_download_init();
if (ret) {
NVT_ERR("Download Init failed. (%d)\n", ret);
goto download_fail;
}
#if defined(NVT_FW_UPDATE_PROFILING)
profile_end = ktime_get();
NVT_LOG("nvt_download_init() time us delta %lld\n",
ktime_us_delta(profile_end, profile_start));
profile_start = profile_end;
#endif
/* download firmware process */
if (ts->hw_crc)
ret = nvt_download_firmware_hw_crc(full);
else
ret = nvt_download_firmware(full);
if (ret) {
NVT_ERR("Download Firmware failed. (%d)\n", ret);
goto download_fail;
}
#if defined(NVT_FW_UPDATE_PROFILING)
profile_end = ktime_get();
NVT_LOG("nvt_download_firmware/_hw_crc() time us delta %lld\n",
ktime_us_delta(profile_end, profile_start));
profile_start = profile_end;
#endif
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 (ts->force_release_fw)
update_firmware_release();
request_firmware_fail:
ts->fw_update_in_process = false;
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(get_fw_name(), 1);
mutex_unlock(&ts->lock);
}
#endif /* BOOT_UPDATE_FIRMWARE */
#endif /* !SPI_FLASH*/