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