#include #include #include #include #include #include #include #include #include #include #include "fts_ts.h" #include #define FTS_DEFAULT_UMS_FW "/sdcard/Firmware/TSP/stm.fw" #define FTS_DEFAULT_SPU_FW "/spu/TSP/ffu_tsp.fw" #define FTSFILE_SIGNATURE 0xAA55AA55 enum { BUILT_IN = 0, UMS, NONE, SPU, }; /** * @brief Type definitions - header structure of firmware file (ftb) */ struct fts_header { u32 signature; u32 ftb_ver; u32 target; u32 fw_id; u32 fw_ver; u32 cfg_id; u32 cfg_ver; u8 fw_area_bs; // bs : block size u8 panel_area_bs; u8 cx_area_bs; u8 cfg_area_bs; u32 reserved1; u32 ext_release_ver; u8 project_id; u8 ic_name; u8 module_ver; u8 reserved2; u32 sec0_size; u32 sec1_size; u32 sec2_size; u32 sec3_size; u32 hdr_crc; } __packed; #define FW_HEADER_SIZE 64 #define WRITE_CHUNK_SIZE 1024 /* * FTS9CU80F : 64K * FTS5CU56A : 32K */ #define DRAM_SIZE (32 * 1024) // 32kB #define CODE_ADDR_START 0x00000000 #define CX_ADDR_START 0x00007000 #define CONFIG_ADDR_START 0x00007C00 #define SIGNEDKEY_SIZE (256) int FTS_Check_DMA_StartAndDone(struct fts_ts_info *info) { int timeout = 60; u8 regAdd[6] = { 0xFA, 0x20, 0x00, 0x00, 0x71, 0xC0 }; u8 val[1]; info->fts_write_reg(info, ®Add[0], 6); fts_delay(10); do { info->fts_read_reg(info, ®Add[0], 5, (u8 *)val, 1); if ((val[0] & 0x80) != 0x80) break; fts_delay(50); timeout--; } while (timeout != 0); if (timeout == 0) { input_err(true, &info->client->dev, "%s: Time Over\n", __func__); return -1; } return 0; } static int FTS_Check_Erase_Done(struct fts_ts_info *info) { int timeout = 60; // 3 sec timeout u8 regAdd[5] = { 0xFA, 0x20, 0x00, 0x00, 0x6A }; u8 val[1]; do { info->fts_read_reg(info, ®Add[0], 5, (u8 *)val, 1); if ((val[0] & 0x80) != 0x80) break; fts_delay(50); timeout--; } while (timeout != 0); if (timeout == 0) { input_err(true, &info->client->dev, "%s: Time Over\n", __func__); return -1; } return 0; } int fts_fw_fillFlash(struct fts_ts_info *info, u32 address, u8 *data, int size) { int remaining, index = 0; int toWrite = 0; int byteBlock = 0; int wheel = 0; u32 addr = 0; int rc; int delta; u8 buff[WRITE_CHUNK_SIZE + 5] = {0}; u8 buff2[12] = {0}; remaining = size; while (remaining > 0) { byteBlock = 0; addr = 0x00100000; while ((byteBlock < DRAM_SIZE) && remaining > 0) { if (remaining >= WRITE_CHUNK_SIZE) { if ((byteBlock + WRITE_CHUNK_SIZE) <= DRAM_SIZE) { toWrite = WRITE_CHUNK_SIZE; remaining -= WRITE_CHUNK_SIZE; byteBlock += WRITE_CHUNK_SIZE; } else { delta = DRAM_SIZE - byteBlock; toWrite = delta; remaining -= delta; byteBlock += delta; } } else { if ((byteBlock + remaining) <= DRAM_SIZE) { toWrite = remaining; byteBlock += remaining; remaining = 0; } else { delta = DRAM_SIZE - byteBlock; toWrite = delta; remaining -= delta; byteBlock += delta; } } index = 0; buff[index++] = 0xFA; buff[index++] = (u8) ((addr & 0xFF000000) >> 24); buff[index++] = (u8) ((addr & 0x00FF0000) >> 16); buff[index++] = (u8) ((addr & 0x0000FF00) >> 8); buff[index++] = (u8) ((addr & 0x000000FF)); memcpy(&buff[index], data, toWrite); rc = info->fts_write_reg(info, &buff[0], index + toWrite); if (rc <= 0) { input_err(true, &info->client->dev, "%s failed to write i2c register. ret:%d\n", __func__, rc); return -1; } fts_delay(5); addr += toWrite; data += toWrite; } input_info(true, &info->client->dev, "%s: Write %d Bytes\n", __func__, byteBlock); //configuring the DMA byteBlock = byteBlock / 4 - 1; index = 0; buff2[index++] = 0xFA; buff2[index++] = 0x20; buff2[index++] = 0x00; buff2[index++] = 0x00; buff2[index++] = 0x72; buff2[index++] = 0x00; buff2[index++] = 0x00; addr = address + ((wheel * DRAM_SIZE) / 4); buff2[index++] = (u8) ((addr & 0x000000FF)); buff2[index++] = (u8) ((addr & 0x0000FF00) >> 8); buff2[index++] = (u8) (byteBlock & 0x000000FF); buff2[index++] = (u8) ((byteBlock & 0x0000FF00) >> 8); buff2[index++] = 0x00; rc = info->fts_write_reg(info, &buff2[0], index); if (rc <= 0) { input_err(true, &info->client->dev, "%s failed to write i2c register. ret:%d\n", __func__, rc); return -1; } fts_delay(10); rc = FTS_Check_DMA_StartAndDone(info); if (rc < 0) return -1; wheel++; } return 0; } static int fts_fw_burn(struct fts_ts_info *info, u8 *fw_data) { const struct fts_header *fw_header; u8 *pFWData; int rc; int i; u8 regAdd[FTS_EVENT_SIZE] = {0}; int NumberOfMainBlock = 0; fw_header = (struct fts_header *) &fw_data[0]; if ((fw_header->fw_area_bs) > 0) NumberOfMainBlock = fw_header->fw_area_bs; else NumberOfMainBlock = 26; // original value // System Reset regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x24; regAdd[5] = 0x81; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(50); // System Reset and Hold regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x24; regAdd[5] = 0x01; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(200); // Enable UVLO regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x1B; regAdd[5] = 0x66; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(30); // Flash Auto Power Down regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x68; regAdd[5] = 0x13; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(30); // Change application mode regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x25; regAdd[5] = 0x20; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(200); // Unlock Flash regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0xDE; regAdd[5] = 0x03; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(200); regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x6B; regAdd[5] = 0x00; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(50); //==================== Erase Partial Flash ==================== input_info(true, &info->client->dev, "%s: Start Flash(Main Block) Erasing\n", __func__); for (i = 0; i < NumberOfMainBlock; i++) { regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x6A; regAdd[5] = (0x80 + i) & 0xFF; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; rc = FTS_Check_Erase_Done(info); if (rc < 0) return rc; } input_info(true, &info->client->dev, "%s: Start Flash(Config Block) Erasing\n", __func__); regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x6B; regAdd[5] = 0x00; info->fts_write_reg(info, ®Add[0], 6); fts_delay(50); regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x6A; regAdd[5] = (0x80 + 31) & 0xFF; info->fts_write_reg(info, ®Add[0], 6); rc = FTS_Check_Erase_Done(info); if (rc < 0) return rc; // Code Area if (fw_header->sec0_size > 0) { pFWData = (u8 *) &fw_data[FW_HEADER_SIZE]; input_info(true, &info->client->dev, "%s: Start Flashing for Code\n", __func__); rc = fts_fw_fillFlash(info, CODE_ADDR_START, &pFWData[0], fw_header->sec0_size); if (rc < 0) return rc; input_info(true, &info->client->dev, "%s: Finished total flashing %ld Bytes for Code\n", __func__, (long)fw_header->sec0_size); } // Config Area if (fw_header->sec1_size > 0) { input_info(true, &info->client->dev, "%s: Start Flashing for Config\n", __func__); pFWData = (u8 *) &fw_data[FW_HEADER_SIZE + fw_header->sec0_size]; rc = fts_fw_fillFlash(info, CONFIG_ADDR_START, &pFWData[0], fw_header->sec1_size); if (rc < 0) return rc; input_info(true, &info->client->dev, "%s: Finished total flashing %ld Bytes for Config\n", __func__, (long)fw_header->sec1_size); } // CX Area if (fw_header->sec2_size > 0) { input_info(true, &info->client->dev, "%s: Start Flashing for CX\n", __func__); pFWData = (u8 *) &fw_data[FW_HEADER_SIZE + fw_header->sec0_size + fw_header->sec1_size]; rc = fts_fw_fillFlash(info, CX_ADDR_START, &pFWData[0], fw_header->sec2_size); if (rc < 0) return rc; input_info(true, &info->client->dev, "%s: Finished total flashing %ld Bytes for CX\n", __func__, (long)fw_header->sec2_size); } regAdd[0] = 0xFA; regAdd[1] = 0x20; regAdd[2] = 0x00; regAdd[3] = 0x00; regAdd[4] = 0x24; regAdd[5] = 0x80; rc = info->fts_write_reg(info, ®Add[0], 6); if (rc < 0) return rc; fts_delay(200); // System Reset info->fts_systemreset(info, 0); return 0; } int fts_fw_wait_for_event(struct fts_ts_info *info, u8 *result, u8 result_cnt) { int rc = 0; int i; bool matched = false; u8 regAdd; u8 data[FTS_EVENT_SIZE]; int retry = 0; memset(data, 0x0, FTS_EVENT_SIZE); regAdd = FTS_READ_ONE_EVENT; rc = -1; while (info->fts_read_reg(info, ®Add, 1, (u8 *)data, FTS_EVENT_SIZE)) { for (i = 0; i < result_cnt; i++) { if (data[i] != result[i]) { matched = false; break; } matched = true; } if (matched == true) { rc = 0; break; } if (data[0] == FTS_EVENT_ERROR_REPORT) { input_info(true, &info->client->dev, "%s: Error detected %02X,%02X,%02X,%02X,%02X,%02X\n", __func__, data[0], data[1], data[2], data[3], data[4], data[5]); break; } if (retry++ > FTS_RETRY_COUNT * 15) { rc = -1; input_err(true, &info->client->dev, "%s: Time Over (%02X,%02X,%02X,%02X,%02X,%02X)\n", __func__, data[0], data[1], data[2], data[3], data[4], data[5]); break; } fts_delay(20); } return rc; } int fts_fw_wait_for_echo_event(struct fts_ts_info *info, u8 *cmd, u8 cmd_cnt, int delay) { int rc = 0; int i; bool matched = false; u8 regAdd; u8 data[FTS_EVENT_SIZE]; int retry = 0; mutex_lock(&info->wait_for); rc = info->fts_write_reg(info, cmd, cmd_cnt); if (rc < 0) { input_err(true, &info->client->dev, "%s: failed to write command\n", __func__); mutex_unlock(&info->wait_for); return rc; } if (delay) fts_delay(delay); memset(data, 0x0, FTS_EVENT_SIZE); regAdd = FTS_READ_ONE_EVENT; rc = -1; while (info->fts_read_reg(info, ®Add, 1, (u8 *)data, FTS_EVENT_SIZE)) { if (data[0] != 0x00) input_info(true, &info->client->dev, "%s: event %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", __func__, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); if ((data[0] == FTS_EVENT_STATUS_REPORT) && (data[1] == 0x01)) { // Check command ECHO int loop_cnt; if (cmd_cnt > 4) loop_cnt = 4; else loop_cnt = cmd_cnt; for (i = 0; i < loop_cnt; i++) { if (data[i + 2] != cmd[i]) { matched = false; break; } matched = true; } if (matched == true) { rc = 0; break; } } else if (data[0] == FTS_EVENT_ERROR_REPORT) { input_info(true, &info->client->dev, "%s: Error detected %02X,%02X,%02X,%02X,%02X,%02X\n", __func__, data[0], data[1], data[2], data[3], data[4], data[5]); if (retry >= FTS_RETRY_COUNT) break; } if (retry++ > FTS_RETRY_COUNT * 50) { rc = -1; input_err(true, &info->client->dev, "%s: Time Over (%02X,%02X,%02X,%02X,%02X,%02X)\n", __func__, data[0], data[1], data[2], data[3], data[4], data[5]); break; } fts_delay(20); } mutex_unlock(&info->wait_for); return rc; } /* block on winner model / use other tablet models */ static void fts_set_factory_history_data(struct fts_ts_info *info, u8 level) { int ret; u8 regaddr[3] = { 0 }; u8 wlevel; switch (level) { case OFFSET_FAC_NOSAVE: input_info(true, &info->client->dev, "%s: not save to flash area\n", __func__); return; case OFFSET_FAC_SUB: wlevel = OFFSET_FW_SUB; break; case OFFSET_FAC_MAIN: wlevel = OFFSET_FW_MAIN; break; default: input_info(true, &info->client->dev, "%s: wrong level %d\n", __func__, level); return; } regaddr[0] = 0xC7; regaddr[1] = 0x04; regaddr[2] = wlevel; ret = info->fts_write_reg(info, regaddr, 3); if (ret < 0) { input_err(true, &info->client->dev, "%s: failed to write factory level %d\n", __func__, wlevel); return; } regaddr[0] = 0xA4; regaddr[1] = 0x05; regaddr[2] = 0x04; /* panel configuration area */ ret = fts_fw_wait_for_echo_event(info, regaddr, 3, 200); if (ret < 0) return; input_info(true, &info->client->dev, "%s: save to flash area, level=%d\n", __func__, wlevel); return; } #ifdef TCLM_CONCEPT int fts_tclm_execute_force_calibration(struct i2c_client *client, int cal_mode) { struct fts_ts_info *info = (struct fts_ts_info *)i2c_get_clientdata(client); return fts_execute_autotune(info, true); } #endif int fts_execute_autotune(struct fts_ts_info *info, bool IsSaving) { u8 regAdd[FTS_EVENT_SIZE] = {0,}; u8 DataType = 0x00; int rc; input_info(true, &info->client->dev, "%s: start\n", __func__); fts_set_scanmode(info, FTS_SCAN_MODE_SCAN_OFF); fts_delay(10); info->fts_command(info, FTS_CMD_CLEAR_ALL_EVENT, true); fts_interrupt_set(info, INT_DISABLE); // w A4 00 03 if (IsSaving == true) { // full panel init regAdd[0] = 0xA4; regAdd[1] = 0x00; regAdd[2] = 0x03; rc = fts_fw_wait_for_echo_event(info, ®Add[0], 3, 500); #ifdef TCLM_CONCEPT if (info->tdata->nvdata.cal_fail_cnt == 0xFF) info->tdata->nvdata.cal_fail_cnt = 0; if (rc < 0) { info->tdata->nvdata.cal_fail_cnt++; info->tdata->nvdata.cal_fail_falg = 0; } else { info->tdata->nvdata.cal_fail_falg = SEC_CAL_PASS; info->is_cal_done = true; } info->tdata->tclm_write(info->tdata->client, SEC_TCLM_NVM_ALL_DATA); /* interrupt is enabled by tclm write */ fts_interrupt_set(info, INT_DISABLE); #endif if (rc < 0) { input_info(true, &info->client->dev, "%s: timeout\n", __func__); goto ERROR; } } else { // SS ATune //DataType = 0x0C; DataType = 0x3F; regAdd[0] = 0xA4; regAdd[1] = 0x03; regAdd[2] = (u8)DataType; regAdd[3] = 0x00; rc = fts_fw_wait_for_echo_event(info, ®Add[0], 4, 500); if (rc < 0) { input_info(true, &info->client->dev, "%s: timeout\n", __func__); goto ERROR; } } /* block on winner model / use other tablet models */ fts_set_factory_history_data(info, info->factory_position); if (IsSaving == true) fts_panel_ito_test(info, SAVE_MISCAL_REF_RAW); ERROR: info->factory_position = OFFSET_FAC_NOSAVE; fts_interrupt_set(info, INT_ENABLE); fts_set_scanmode(info, info->scan_mode); fts_checking_miscal(info); //temp return rc; } static const int fts_fw_updater(struct fts_ts_info *info, u8 *fw_data) { const struct fts_header *header; int retval; int retry; u16 fw_main_version; if (!fw_data) { input_err(true, &info->client->dev, "%s: Firmware data is NULL\n", __func__); return -ENODEV; } header = (struct fts_header *)fw_data; fw_main_version = (u16)header->ext_release_ver; input_info(true, &info->client->dev, "%s: Starting firmware update : 0x%04X\n", __func__, fw_main_version); retry = 0; fts_interrupt_set(info, INT_DISABLE); while (1) { retval = fts_fw_burn(info, fw_data); if (retval >= 0) { info->fts_get_version_info(info); if (fw_main_version == info->fw_main_version_of_ic) { input_info(true, &info->client->dev, "%s: Success Firmware update\n", __func__); info->fw_corruption = false; retval = info->fts_systemreset(info, 0); if (retval == -FTS_ERROR_BROKEN_OSC_TRIM) { retval = fts_osc_trim_recovery(info); if (retval < 0) input_err(true, &info->client->dev, "%s: Failed to recover osc trim\n", __func__); else info->fw_corruption = false; } fts_set_scanmode(info, info->scan_mode); retval = 0; break; } } if (retry++ > 3) { input_err(true, &info->client->dev, "%s: Fail Firmware update\n", __func__); retval = -1; break; } } fts_interrupt_set(info, INT_ENABLE); return retval; } int fts_fw_update_on_probe(struct fts_ts_info *info) { int retval = 0; const struct firmware *fw_entry = NULL; u8 *fw_data = NULL; char fw_path[FTS_MAX_FW_PATH]; const struct fts_header *header; #ifdef TCLM_CONCEPT bool restore_cal = false; int ret = 0; #endif #ifdef TCLM_CONCEPT if (info->tdata->support_tclm_test) { ret = sec_tclm_test_on_probe(info->tdata); if (ret < 0) input_info(true, &info->client->dev, "%s: SEC_TCLM_NVM_ALL_DATA i2c read fail", __func__); } #endif if (info->board->bringup == 1) return 0; if (info->board->firmware_name) { info->firmware_name = info->board->firmware_name; } else { input_err(true, &info->client->dev, "%s: firmware name does not declair in dts\n", __func__); goto exit_fwload; } snprintf(fw_path, FTS_MAX_FW_PATH, "%s", info->firmware_name); input_info(true, &info->client->dev, "%s: Load firmware : %s, TSP_ID : %d\n", __func__, fw_path, info->board->tsp_id); retval = request_firmware(&fw_entry, fw_path, &info->client->dev); if (retval) { input_err(true, &info->client->dev, "%s: Firmware image %s not available\n", __func__, fw_path); goto done; } fw_data = (u8 *)fw_entry->data; header = (struct fts_header *)fw_data; info->fw_version_of_bin = (u16)header->fw_ver; info->fw_main_version_of_bin = (u16)header->ext_release_ver; info->config_version_of_bin = (u16)header->cfg_ver; info->project_id_of_bin = header->project_id; info->ic_name_of_bin = header->ic_name; info->module_version_of_bin = header->module_ver; input_info(true, &info->client->dev, "%s: [BIN] Firmware Ver: 0x%04X, Config Ver: 0x%04X, Main Ver: 0x%04X\n", __func__, info->fw_version_of_bin, info->config_version_of_bin, info->fw_main_version_of_bin); input_info(true, &info->client->dev, "%s: [BIN] Project ID: 0x%02X, IC Name: 0x%02X, Module Ver: 0x%02X\n", __func__, info->project_id_of_bin, info->ic_name_of_bin, info->module_version_of_bin); if (info->board->bringup == 2) { input_err(true, &info->client->dev, "%s: skip fw_update for bringup\n", __func__); retval = FTS_NOT_ERROR; goto done; } if (info->checksum_result) retval = FTS_NEED_FW_UPDATE; else if (info->ic_name_of_ic == 0xFF && info->project_id_of_ic == 0xFF && info->module_version_of_ic == 0xFF) retval = FTS_NEED_FW_UPDATE; else if (info->ic_name_of_ic != info->ic_name_of_bin) retval = FTS_NOT_UPDATE; else if (info->project_id_of_ic != info->project_id_of_bin) retval = FTS_NEED_FW_UPDATE; else if (info->module_version_of_ic != info->module_version_of_bin) retval = FTS_NOT_UPDATE; else if ((info->fw_main_version_of_ic < info->fw_main_version_of_bin) || ((info->config_version_of_ic < info->config_version_of_bin)) || ((info->fw_version_of_ic < info->fw_version_of_bin))) retval = FTS_NEED_FW_UPDATE; else retval = FTS_NOT_ERROR; /* ic fw ver > bin fw ver && force is false */ if (retval != FTS_NEED_FW_UPDATE) { input_err(true, &info->client->dev, "%s: skip fw update\n", __func__); goto done; } retval = fts_fw_updater(info, fw_data); if (retval < 0) goto done; #ifdef TCLM_CONCEPT ret = info->tdata->tclm_read(info->tdata->client, SEC_TCLM_NVM_ALL_DATA); if (ret < 0) { input_info(true, &info->client->dev, "%s: SEC_TCLM_NVM_ALL_DATA i2c read fail", __func__); goto done; } input_info(true, &info->client->dev, "%s: tune_fix_ver [%04X] afe_base [%04X]\n", __func__, info->tdata->nvdata.tune_fix_ver, info->tdata->afe_base); if ((info->tdata->tclm_level > TCLM_LEVEL_CLEAR_NV) && ((info->tdata->nvdata.tune_fix_ver == 0xffff) || (info->tdata->afe_base > info->tdata->nvdata.tune_fix_ver))) { /* tune version up case */ sec_tclm_root_of_cal(info->tdata, CALPOSITION_TUNEUP); restore_cal = true; } else if (info->tdata->tclm_level == TCLM_LEVEL_CLEAR_NV) { /* firmup case */ sec_tclm_root_of_cal(info->tdata, CALPOSITION_FIRMUP); restore_cal = true; } if (restore_cal) { input_info(true, &info->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); if (sec_execute_tclm_package(info->tdata, 0) < 0) input_err(true, &info->client->dev, "%s: sec_execute_tclm_package fail\n", __func__); } #endif done: #ifdef TCLM_CONCEPT sec_tclm_root_of_cal(info->tdata, CALPOSITION_NONE); #endif if (fw_entry) release_firmware(fw_entry); exit_fwload: return retval; } EXPORT_SYMBOL(fts_fw_update_on_probe); static int fts_load_fw_from_kernel(struct fts_ts_info *info, const char *fw_path) { int retval; const struct firmware *fw_entry = NULL; u8 *fw_data = NULL; if (!fw_path) { input_err(true, &info->client->dev, "%s: Firmware name is not defined\n", __func__); return -EINVAL; } input_info(true, &info->client->dev, "%s: Load firmware : %s\n", __func__, fw_path); retval = request_firmware(&fw_entry, fw_path, &info->client->dev); if (retval) { input_err(true, &info->client->dev, "%s: Firmware image %s not available\n", __func__, fw_path); goto done; } fw_data = (u8 *)fw_entry->data; info->fts_systemreset(info, 20); #ifdef TCLM_CONCEPT sec_tclm_root_of_cal(info->tdata, CALPOSITION_TESTMODE); #endif retval = fts_fw_updater(info, fw_data); if (retval) input_err(true, &info->client->dev, "%s: failed update firmware\n", __func__); #ifdef TCLM_CONCEPT input_info(true, &info->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); if (sec_execute_tclm_package(info->tdata, 0) < 0) input_err(true, &info->client->dev, "%s: sec_execute_tclm_package fail\n", __func__); sec_tclm_root_of_cal(info->tdata, CALPOSITION_NONE); #endif info->fw_corruption = false; done: if (fw_entry) release_firmware(fw_entry); return retval; } static int fts_load_fw_from_ums(struct fts_ts_info *info, int type) { struct file *fp; mm_segment_t old_fs; long fw_size, nread; int error = 0; int spu_ret = 0; int ori_size = 0; char file_path[100]; old_fs = get_fs(); set_fs(KERNEL_DS); switch (type) { case TSP_TYPE_EXTERNAL_FW: snprintf(file_path, sizeof(file_path), TSP_PATH_EXTERNAL_FW); break; case TSP_TYPE_EXTERNAL_FW_SIGNED: snprintf(file_path, sizeof(file_path), TSP_PATH_EXTERNAL_FW_SIGNED); break; case TSP_TYPE_SPU_FW_SIGNED: snprintf(file_path, sizeof(file_path), TSP_PATH_SPU_FW_SIGNED); break; default: return -ENODEV; } fp = filp_open(file_path, O_RDONLY, 0400); if (IS_ERR(fp)) { input_err(true, &info->client->dev, "%s: failed to open %s.\n", __func__, file_path); error = -ENOENT; goto open_err; } fw_size = fp->f_path.dentry->d_inode->i_size; if (fw_size > 0) { u8 *fw_data; const struct fts_header *header; fw_data = kzalloc(fw_size, GFP_KERNEL); if (!fw_data) { error = -ENOMEM; goto alloc_err; } nread = vfs_read(fp, (char __user *)fw_data, fw_size, &fp->f_pos); input_info(true, &info->client->dev, "%s: start, file path %s, size %ld Bytes\n", __func__, file_path, fw_size); if (nread != fw_size) { input_err(true, &info->client->dev, "%s: failed to read firmware file, nread %ld Bytes\n", __func__, nread); error = -EIO; } else { header = (struct fts_header *)fw_data; if (header->signature == FTSFILE_SIGNATURE) { /* If FFU firmware version is lower than IC's version, do not run update routine */ if (type == TSP_TYPE_EXTERNAL_FW_SIGNED || type == TSP_TYPE_SPU_FW_SIGNED) { /* digest 32, signature 512 TSP 3 */ ori_size = fw_size - SPU_METADATA_SIZE(TSP); spu_ret = spu_firmware_signature_verify("TSP", fw_data, fw_size); if (ori_size != spu_ret) { error = -1; input_err(true, &info->client->dev, "%s: signature verify failed: spu_ret:%d, ori_size:%d", __func__, spu_ret, ori_size); kfree(fw_data); goto alloc_err; } } if (type == TSP_TYPE_SPU_FW_SIGNED) { if ((info->fw_main_version_of_ic < (u16)header->ext_release_ver) || (info->config_version_of_ic < (u16)header->cfg_ver) || (info->fw_version_of_ic < (u16)header->fw_ver)) { input_info(true, &info->client->dev, "%s: run spu update\n", __func__); } else { error = 0; input_info(true, &info->client->dev, "%s: skip spu update\n", __func__); kfree(fw_data); goto alloc_err; } } else if (type == TSP_TYPE_EXTERNAL_FW_SIGNED) { if ((info->ic_name_of_ic == header->ic_name) && (info->project_id_of_ic == header->project_id)) { input_info(true, &info->client->dev, "%s: run external fw update\n", __func__); } else { error = 0; input_info(true, &info->client->dev, "%s: skip external fw update\n", __func__); kfree(fw_data); goto alloc_err; } } info->fts_systemreset(info, 0); input_info(true, &info->client->dev, "%s: [UMS] Firmware Ver: 0x%04X, Main Version : 0x%04X\n", __func__, (u16)header->fw_ver, (u16)header->ext_release_ver); #ifdef TCLM_CONCEPT sec_tclm_root_of_cal(info->tdata, CALPOSITION_TESTMODE); #endif error = fts_fw_updater(info, fw_data); #ifdef TCLM_CONCEPT input_info(true, &info->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); if (sec_execute_tclm_package(info->tdata, 0) < 0) input_err(true, &info->client->dev, "%s: sec_execute_tclm_package fail\n", __func__); sec_tclm_root_of_cal(info->tdata, CALPOSITION_NONE); #endif } else { error = -1; input_err(true, &info->client->dev, "%s: File type is not match with FTS64 file. [%8x]\n", __func__, header->signature); } } if (error < 0) input_err(true, &info->client->dev, "%s: failed update firmware\n", __func__); kfree(fw_data); } alloc_err: filp_close(fp, NULL); open_err: set_fs(old_fs); return error; } int fts_fw_update_on_hidden_menu(struct fts_ts_info *info, int update_type) { int retval = 0; /* Factory cmd for firmware update * argument represent what is source of firmware like below. * * 0 : [BUILT_IN] Getting firmware which is for user. * 1 : [UMS] Getting firmware from sd card. * 2 : none */ switch (update_type) { case BUILT_IN: retval = fts_load_fw_from_kernel(info, info->firmware_name); break; case UMS: #if defined(CONFIG_SAMSUNG_PRODUCT_SHIP) retval = fts_load_fw_from_ums(info, TSP_TYPE_EXTERNAL_FW_SIGNED); #else retval = fts_load_fw_from_ums(info, TSP_TYPE_EXTERNAL_FW); #endif break; case SPU: retval = fts_load_fw_from_ums(info, TSP_TYPE_SPU_FW_SIGNED); break; default: input_err(true, &info->client->dev, "%s: Not support command[%d]\n", __func__, update_type); break; } fts_check_custom_library(info); return retval; } EXPORT_SYMBOL(fts_fw_update_on_hidden_menu);