/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2016 MediaTek Inc. */ #include #include #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 */