/* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. * * 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 a reference * to you, when you are integrating the GOODiX's CTP IC into your system, * 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 "goodix_ts_core.h" #define BUS_TYPE_SPI 1 #define BUS_TYPE_I2C 0 #define GOODIX_BUS_RETRY_TIMES 3 #define FW_HEADER_SIZE 512 #define FW_SUBSYS_INFO_SIZE 10 #define FW_SUBSYS_INFO_OFFSET 42 #define FW_SUBSYS_MAX_NUM 47 #define ISP_MAX_BUFFERSIZE 4096 #define FW_PID_LEN 8 #define FW_VID_LEN 4 #define FLASH_CMD_LEN 11 #define FW_FILE_CHECKSUM_OFFSET 8 #define CONFIG_DATA_TYPE 4 #define HW_REG_CPU_RUN_FROM 0x10000 #define HOLD_CPU_REG_W 0x0002 #define HOLD_CPU_REG_R 0x2000 #define ESD_KEY_REG 0xCC58 #define FLASH_CMD_TYPE_READ 0xAA #define FLASH_CMD_TYPE_WRITE 0xBB #define FLASH_CMD_ACK_CHK_PASS 0xEE #define FLASH_CMD_ACK_CHK_ERROR 0x33 #define FLASH_CMD_ACK_IDLE 0x11 #define FLASH_CMD_W_STATUS_CHK_PASS 0x22 #define FLASH_CMD_W_STATUS_CHK_FAIL 0x33 #define FLASH_CMD_W_STATUS_ADDR_ERR 0x44 #define FLASH_CMD_W_STATUS_WRITE_ERR 0x55 #define FLASH_CMD_W_STATUS_WRITE_OK 0xEE struct update_info_t { unsigned int isp_ram_reg; unsigned int flash_cmd_reg; unsigned int isp_buffer_reg; unsigned int config_data_reg; unsigned int misctl_reg; unsigned int watch_dog_reg; unsigned int config_id_reg; unsigned int enable_misctl_val; }; /** * fw_subsys_info - subsytem firmware information * @type: sybsystem type * @size: firmware size * @flash_addr: flash address * @data: firmware data */ struct fw_subsys_info { u8 type; u32 size; u32 flash_addr; const u8 *data; }; /** * firmware_summary * @size: fw total length * @checksum: checksum of fw * @hw_pid: mask pid string * @hw_pid: mask vid code * @fw_pid: fw pid string * @fw_vid: fw vid code * @subsys_num: number of fw subsystem * @chip_type: chip type * @protocol_ver: firmware packing * protocol version * @bus_type: 0 represent I2C, 1 for SPI * @subsys: sybsystem info */ #pragma pack(1) struct firmware_summary { u32 size; u32 checksum; u8 hw_pid[6]; u8 hw_vid[3]; u8 fw_pid[FW_PID_LEN]; u8 fw_vid[FW_VID_LEN]; u8 subsys_num; u8 chip_type; u8 protocol_ver; u8 bus_type; u8 flash_protect; // u8 reserved[8]; struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; }; #pragma pack() /** * firmware_data - firmware data structure * @fw_summary: firmware information * @firmware: firmware data structure */ struct firmware_data { struct firmware_summary fw_summary; const struct firmware *firmware; struct firmware fw; }; struct config_data { u8 *data; int size; }; #pragma pack(1) struct goodix_flash_cmd { union { struct { u8 status; u8 ack; u8 len; u8 cmd; u8 fw_type; u16 fw_len; u32 fw_addr; //u16 checksum; }; u8 buf[16]; }; }; #pragma pack() enum update_status { UPSTA_NOTWORK = 0, UPSTA_PREPARING, UPSTA_UPDATING, UPSTA_SUCCESS, UPSTA_FAILED }; enum compare_status { COMPARE_EQUAL = 0, COMPARE_NOCODE, COMPARE_PIDMISMATCH, COMPARE_FW_NOTEQUAL, COMPARE_CFG_NOTEQUAL, }; /** * fw_update_ctrl - structure used to control the * firmware update process * @initialized: struct init state * @mode: indicate weather reflash config or not, fw data source, * and run on block mode or not. * @status: update status * @progress: indicate the progress of update * @fw_data: firmware data * @attr_fwimage: sysfs bin attrs, for storing fw image * @fw_data_src: firmware data source form sysfs, request or head file * @kobj: pointer to the sysfs kobject */ struct fw_update_ctrl { struct mutex mutex; int initialized; unsigned int cfg_checksum; unsigned int fw_checksum; int mode; enum update_status status; int spend_time; struct firmware_data fw_data; struct goodix_ic_config *ic_config; struct goodix_ts_core *core_data; struct update_info_t update_info; struct bin_attribute attr_fwimage; struct kobject *kobj; }; static struct fw_update_ctrl goodix_fw_update_ctrl; static int goodix_fw_update_reset(int delay) { struct goodix_ts_hw_ops *hw_ops; hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; return hw_ops->reset(goodix_fw_update_ctrl.core_data, delay); } #define SOFT_RESET_REG 0xD808 static int goodix_soft_reset(int delay) { struct goodix_ts_hw_ops *hw_ops; u8 val = 0; int ret; hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; ret = hw_ops->write(goodix_fw_update_ctrl.core_data, SOFT_RESET_REG, &val, 1); if (ret < 0) return ret; sec_delay(delay); return 0; } static int get_fw_version_info(struct goodix_fw_version *fw_version) { struct goodix_ts_hw_ops *hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; return hw_ops->read_version(goodix_fw_update_ctrl.core_data, fw_version); } static int goodix_reg_write(unsigned int addr, unsigned char *data, unsigned int len) { struct goodix_ts_hw_ops *hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; return hw_ops->write(goodix_fw_update_ctrl.core_data, addr, data, len); } static int goodix_reg_read(unsigned int addr, unsigned char *data, unsigned int len) { struct goodix_ts_hw_ops *hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; return hw_ops->read(goodix_fw_update_ctrl.core_data, addr, data, len); } /** * goodix_parse_firmware - parse firmware header information * and subsystem information from firmware data buffer * * @fw_data: firmware struct, contains firmware header info * and firmware data. * return: 0 - OK, < 0 - error */ /* sizeof(length) + sizeof(checksum) */ static int goodix_parse_firmware(struct firmware_data *fw_data) { const struct firmware *firmware; struct firmware_summary *fw_summary; unsigned int i, fw_offset, info_offset; u32 checksum; int subsys_info_offset = FW_SUBSYS_INFO_OFFSET; int header_size = FW_HEADER_SIZE; int r = 0; fw_summary = &fw_data->fw_summary; /* copy firmware head info */ firmware = &fw_data->fw; if (firmware->size < subsys_info_offset) { ts_err("Invalid firmware size:%zu", firmware->size); r = -EINVAL; goto err_size; } memcpy(fw_summary, firmware->data, sizeof(*fw_summary)); /* check firmware size */ fw_summary->size = le32_to_cpu(fw_summary->size); if (firmware->size != fw_summary->size + FW_FILE_CHECKSUM_OFFSET) { ts_err("Bad firmware, size not match, %zu != %d", firmware->size, fw_summary->size + 6); r = -EINVAL; goto err_size; } for (i = FW_FILE_CHECKSUM_OFFSET, checksum = 0; i < firmware->size; i += 2) checksum += firmware->data[i] + (firmware->data[i+1] << 8); /* byte order change, and check */ fw_summary->checksum = le32_to_cpu(fw_summary->checksum); goodix_fw_update_ctrl.fw_checksum = fw_summary->checksum; ts_info("Firmware checksum:0x%04x", goodix_fw_update_ctrl.fw_checksum + goodix_fw_update_ctrl.cfg_checksum); if (checksum != fw_summary->checksum) { ts_err("Bad firmware, cheksum error"); r = -EINVAL; goto err_size; } if (fw_summary->subsys_num > FW_SUBSYS_MAX_NUM) { ts_err("Bad firmware, invalid subsys num: %d", fw_summary->subsys_num); r = -EINVAL; goto err_size; } /* parse subsystem info */ fw_offset = header_size; for (i = 0; i < fw_summary->subsys_num; i++) { info_offset = subsys_info_offset + i * FW_SUBSYS_INFO_SIZE; fw_summary->subsys[i].type = firmware->data[info_offset]; fw_summary->subsys[i].size = le32_to_cpup((__le32 *)&firmware->data[info_offset + 1]); fw_summary->subsys[i].flash_addr = le32_to_cpup((__le32 *)&firmware->data[info_offset + 5]); if (fw_offset > firmware->size) { ts_err("Sybsys offset exceed Firmware size"); goto err_size; } fw_summary->subsys[i].data = firmware->data + fw_offset; fw_offset += fw_summary->subsys[i].size; } ts_info("Firmware package protocol: V%u", fw_summary->protocol_ver); ts_info("Firmware PID:GT%s", fw_summary->fw_pid); ts_info("Firmware VID:%*ph", 4, fw_summary->fw_vid); ts_info("Firmware chip type:0x%02X", fw_summary->chip_type); ts_info("Firmware bus type:%s", (fw_summary->bus_type & BUS_TYPE_SPI) ? "SPI" : "I2C"); ts_info("Firmware size:%u", fw_summary->size); ts_info("Firmware subsystem num:%u", fw_summary->subsys_num); for (i = 0; i < fw_summary->subsys_num; i++) { ts_debug("------------------------------------------"); ts_debug("Index:%d", i); ts_debug("Subsystem type:%02X", fw_summary->subsys[i].type); ts_debug("Subsystem size:%u", fw_summary->subsys[i].size); ts_debug("Subsystem flash_addr:%08X", fw_summary->subsys[i].flash_addr); ts_debug("Subsystem Ptr:%pK", fw_summary->subsys[i].data); } err_size: return r; } /** * goodix_fw_version_compare - compare the active version with * firmware file version. * @fwu_ctrl: firmware information to be compared * return: 0 equal, < 0 unequal */ static int goodix_fw_version_compare(struct fw_update_ctrl *fwu_ctrl) { int ret = 0; struct goodix_fw_version fw_version; struct firmware_summary *fw_summary = &fwu_ctrl->fw_data.fw_summary; u32 config_id_reg = goodix_fw_update_ctrl.update_info.config_id_reg; u32 file_cfg_id; u32 ic_cfg_id; /* compare fw_version */ ret = get_fw_version_info(&fw_version); if (ret) return -EINVAL; if (!memcmp(fw_version.rom_pid, GOODIX_NOCODE, 6) || !memcmp(fw_version.patch_pid, GOODIX_NOCODE, 6)) { ts_info("there is no code in the chip"); return COMPARE_NOCODE; } if (memcmp(fw_version.patch_pid, fw_summary->fw_pid, FW_PID_LEN)) { ts_err("Product ID mismatch:%s != %s", fw_version.patch_pid, fw_summary->fw_pid); return COMPARE_PIDMISMATCH; } ret = memcmp(fw_version.patch_vid, fw_summary->fw_vid, FW_VID_LEN); if (ret) { ts_info("active firmware version:%*ph", FW_VID_LEN, fw_version.patch_vid); ts_info("firmware file version: %*ph", FW_VID_LEN, fw_summary->fw_vid); return COMPARE_FW_NOTEQUAL; } ts_info("fw_version equal"); /* compare config id */ if (fwu_ctrl->ic_config && fwu_ctrl->ic_config->len > 0) { file_cfg_id = goodix_get_file_config_id(fwu_ctrl->ic_config->data); goodix_reg_read(config_id_reg, (u8 *)&ic_cfg_id, sizeof(ic_cfg_id)); if (ic_cfg_id != file_cfg_id) { ts_info("ic_cfg_id:0x%x != file_cfg_id:0x%x", ic_cfg_id, file_cfg_id); return COMPARE_CFG_NOTEQUAL; } ts_info("config_id equal"); } return COMPARE_EQUAL; } /** * goodix_reg_write_confirm - write register and confirm the value * in the register. * @dev: pointer to touch device * @addr: register address * @data: pointer to data buffer * @len: data length * return: 0 write success and confirm ok * < 0 failed */ static int goodix_reg_write_confirm(unsigned int addr, unsigned char *data, unsigned int len) { u8 *cfm = NULL; u8 cfm_buf[32]; int r, i; if (len > sizeof(cfm_buf)) { cfm = kzalloc(len, GFP_KERNEL); if (!cfm) return -ENOMEM; } else { cfm = &cfm_buf[0]; } for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { r = goodix_reg_write(addr, data, len); if (r < 0) goto exit; r = goodix_reg_read(addr, cfm, len); if (r < 0) goto exit; if (memcmp(data, cfm, len)) { r = -EINVAL; continue; } else { r = 0; break; } } exit: if (cfm != &cfm_buf[0]) kfree(cfm); return r; } /** * goodix_load_isp - load ISP program to device ram * @dev: pointer to touch device * @fw_data: firmware data * return 0 ok, <0 error */ static int goodix_load_isp(struct firmware_data *fw_data) { struct goodix_fw_version isp_fw_version; struct fw_subsys_info *fw_isp; u32 isp_ram_reg = goodix_fw_update_ctrl.update_info.isp_ram_reg; u8 reg_val[8] = {0x00}; int r; memset(&isp_fw_version, 0, sizeof(isp_fw_version)); fw_isp = &fw_data->fw_summary.subsys[0]; ts_info("Loading ISP start"); r = goodix_reg_write_confirm(isp_ram_reg, (u8 *)fw_isp->data, fw_isp->size); if (r < 0) { ts_err("Loading ISP error"); return r; } ts_info("Success send ISP data"); /* SET BOOT OPTION TO 0X55 */ memset(reg_val, 0x55, 8); r = goodix_reg_write_confirm(HW_REG_CPU_RUN_FROM, reg_val, 8); if (r < 0) { ts_err("Failed set REG_CPU_RUN_FROM flag"); return r; } ts_info("Success write [8]0x55 to 0x%x", HW_REG_CPU_RUN_FROM); if (goodix_soft_reset(100)) ts_err("soft reset abnormal"); /*check isp state */ if (get_fw_version_info(&isp_fw_version)) { ts_err("failed read isp version"); return -2; } ts_info("patch_pid [3]:0x%02X [4]:0x%02X [5]:0x%02X", isp_fw_version.patch_pid[3], isp_fw_version.patch_pid[4], isp_fw_version.patch_pid[5]); if (memcmp(&isp_fw_version.patch_pid[3], "ISP", 3)) { ts_err("patch id error %c%c%c != %s", isp_fw_version.patch_pid[3], isp_fw_version.patch_pid[4], isp_fw_version.patch_pid[5], "ISP"); return -3; } ts_info("ISP running successfully"); return 0; } /** * goodix_update_prepare - update prepare, loading ISP program * and make sure the ISP is running. * @fwu_ctrl: pointer to fimrware control structure * return: 0 ok, <0 error */ static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl) { u32 misctl_reg = fwu_ctrl->update_info.misctl_reg; u32 watch_dog_reg = fwu_ctrl->update_info.watch_dog_reg; u32 enable_misctl_val = fwu_ctrl->update_info.enable_misctl_val; u8 reg_val[4] = {0}; u8 temp_buf[64] = {0}; int retry = 20; int r; /*reset IC*/ ts_info("firmware update, reset"); if (goodix_fw_update_reset(5)) ts_err("reset abnormal"); retry = 100; /* Hold cpu*/ do { reg_val[0] = 0x01; reg_val[1] = 0x00; r = goodix_reg_write(HOLD_CPU_REG_W, reg_val, 2); r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[0], 4); r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[4], 4); r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[8], 4); if (!r && !memcmp(&temp_buf[0], &temp_buf[4], 4) && !memcmp(&temp_buf[4], &temp_buf[8], 4) && !memcmp(&temp_buf[0], &temp_buf[8], 4)) { break; } sec_delay(1); ts_info("retry hold cpu %d", retry); ts_debug("data:%*ph", 12, temp_buf); } while (--retry); if (!retry) { ts_err("Failed to hold CPU, return =%d", r); return -1; } ts_info("Success hold CPU"); /* enable misctl clock */ if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_D) goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 4); else goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 1); ts_info("enable misctl clock"); /* disable watch dog */ reg_val[0] = 0x00; r = goodix_reg_write(watch_dog_reg, reg_val, 1); ts_info("disable watch dog"); /* load ISP code and run form isp */ r = goodix_load_isp(&fwu_ctrl->fw_data); if (r < 0) ts_err("Failed load and run isp"); return r; } /* goodix_send_flash_cmd: send command to read or write flash data * @flash_cmd: command need to send. */ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) { int i, ret, retry; struct goodix_flash_cmd tmp_cmd; u32 flash_cmd_reg = goodix_fw_update_ctrl.update_info.flash_cmd_reg; ts_info("try send flash cmd:%*ph", (int)sizeof(flash_cmd->buf), flash_cmd->buf); memset(tmp_cmd.buf, 0, sizeof(tmp_cmd)); ret = goodix_reg_write(flash_cmd_reg, flash_cmd->buf, sizeof(flash_cmd->buf)); if (ret) { ts_err("failed send flash cmd %d", ret); return ret; } retry = 5; for (i = 0; i < retry; i++) { ret = goodix_reg_read(flash_cmd_reg, tmp_cmd.buf, sizeof(tmp_cmd.buf)); if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS) break; sec_delay(5); ts_info("flash cmd ack error retry %d, ack 0x%x, ret %d", i, tmp_cmd.ack, ret); } if (tmp_cmd.ack != FLASH_CMD_ACK_CHK_PASS) { ts_err("flash cmd ack error, ack 0x%x, ret %d", tmp_cmd.ack, ret); ts_err("data:%*ph", (int)sizeof(tmp_cmd.buf), tmp_cmd.buf); return -EINVAL; } ts_info("flash cmd ack check pass"); sec_delay(80); retry = 20; for (i = 0; i < retry; i++) { ret = goodix_reg_read(flash_cmd_reg, tmp_cmd.buf, sizeof(tmp_cmd.buf)); if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS && tmp_cmd.status == FLASH_CMD_W_STATUS_WRITE_OK) { ts_info("flash status check pass"); return 0; } ts_info("flash cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", i, tmp_cmd.ack, tmp_cmd.status, ret); sec_delay(20); } ts_err("flash cmd status error %d, ack 0x%x, status 0x%x, ret %d", i, tmp_cmd.ack, tmp_cmd.status, ret); if (ret) { ts_info("reason: bus or paltform error"); return -EINVAL; } switch (tmp_cmd.status) { case FLASH_CMD_W_STATUS_CHK_PASS: ts_err("data check pass, but failed get follow-up results"); return -EFAULT; case FLASH_CMD_W_STATUS_CHK_FAIL: ts_err("data check failed, please retry"); return -EAGAIN; case FLASH_CMD_W_STATUS_ADDR_ERR: ts_err("flash target addr error, please check"); return -EFAULT; case FLASH_CMD_W_STATUS_WRITE_ERR: ts_err("flash data write err, please retry"); return -EAGAIN; default: ts_err("unknown status"); return -EFAULT; } } static int goodix_flash_package(u8 subsys_type, u8 *pkg, u32 flash_addr, u16 pkg_len) { int ret, retry; struct goodix_flash_cmd flash_cmd; u32 isp_buffer_reg = goodix_fw_update_ctrl.update_info.isp_buffer_reg; retry = 2; do { ret = goodix_reg_write(isp_buffer_reg, pkg, pkg_len); if (ret < 0) { ts_err("Failed to write firmware packet"); return ret; } flash_cmd.status = 0; flash_cmd.ack = 0; flash_cmd.len = FLASH_CMD_LEN; flash_cmd.cmd = FLASH_CMD_TYPE_WRITE; flash_cmd.fw_type = subsys_type; flash_cmd.fw_len = cpu_to_le16(pkg_len); flash_cmd.fw_addr = cpu_to_le32(flash_addr); goodix_append_checksum(&(flash_cmd.buf[2]), 9, CHECKSUM_MODE_U8_LE); ret = goodix_send_flash_cmd(&flash_cmd); if (!ret) { ts_info("success write package to 0x%x, len %d", flash_addr, pkg_len - 4); return 0; } } while (ret == -EAGAIN && --retry); return ret; } /** * goodix_flash_subsystem - flash subsystem firmware, * Main flow of flashing firmware. * Each firmware subsystem is divided into several * packets, the max size of packet is limited to * @{ISP_MAX_BUFFERSIZE} * @dev: pointer to touch device * @subsys: subsystem information * return: 0 ok, < 0 error */ static int goodix_flash_subsystem(struct fw_subsys_info *subsys) { u32 data_size, offset; u32 total_size; //TODO: confirm flash addr ,<< 8?? u32 subsys_base_addr = subsys->flash_addr; u8 *fw_packet = NULL; int r = 0; /* * if bus(i2c/spi) error occued, then exit, we will do * hardware reset and re-prepare ISP and then retry * flashing */ total_size = subsys->size; fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 4, GFP_KERNEL); if (!fw_packet) { ts_err("Failed alloc memory"); return -EINVAL; } offset = 0; while (total_size > 0) { data_size = total_size > ISP_MAX_BUFFERSIZE ? ISP_MAX_BUFFERSIZE : total_size; ts_info("Flash firmware to %08x,size:%u bytes", subsys_base_addr + offset, data_size); memcpy(fw_packet, &subsys->data[offset], data_size); /* set checksum for package data */ goodix_append_checksum(fw_packet, data_size, CHECKSUM_MODE_U16_LE); r = goodix_flash_package(subsys->type, fw_packet, subsys_base_addr + offset, data_size + 4); if (r) { ts_err("failed flash to %08x,size:%u bytes", subsys_base_addr + offset, data_size); break; } offset += data_size; total_size -= data_size; } /* end while */ kfree(fw_packet); return r; } /** * goodix_flash_firmware - flash firmware * @dev: pointer to touch device * @fw_data: firmware data * return: 0 ok, < 0 error */ static int goodix_flash_firmware(struct fw_update_ctrl *fw_ctrl) { struct firmware_data *fw_data = &fw_ctrl->fw_data; struct firmware_summary *fw_summary; struct fw_subsys_info *fw_x; struct fw_subsys_info subsys_cfg = {0}; u32 config_data_reg = fw_ctrl->update_info.config_data_reg; int retry = GOODIX_BUS_RETRY_TIMES; int i, r = 0, fw_num; /* start from subsystem 1, * subsystem 0 is the ISP program */ fw_summary = &fw_data->fw_summary; fw_num = fw_summary->subsys_num; /* flash config data first if we have */ if (fw_ctrl->ic_config && fw_ctrl->ic_config->len) { subsys_cfg.data = fw_ctrl->ic_config->data; subsys_cfg.size = fw_ctrl->ic_config->len; subsys_cfg.flash_addr = config_data_reg; subsys_cfg.type = CONFIG_DATA_TYPE; r = goodix_flash_subsystem(&subsys_cfg); if (r) { ts_err("failed flash config with ISP, %d", r); return r; } ts_info("success flash config with ISP"); } for (i = 1; i < fw_num && retry;) { ts_info("--- Start to flash subsystem[%d] ---", i); fw_x = &fw_summary->subsys[i]; r = goodix_flash_subsystem(fw_x); if (r == 0) { ts_info("--- End flash subsystem[%d]: OK ---", i); i++; } else if (r == -EAGAIN) { retry--; ts_err("--- End flash subsystem%d: Fail, errno:%d, retry:%d ---", i, r, GOODIX_BUS_RETRY_TIMES - retry); } else if (r < 0) { /* bus error */ ts_err("--- End flash subsystem%d: Fatal error:%d exit ---", i, r); goto exit_flash; } } exit_flash: return r; } /** * goodix_update_finish - update finished, FREE resource * and reset flags--- * @fwu_ctrl: pointer to fw_update_ctrl structrue * return: 0 ok, < 0 error */ static int goodix_update_finish(struct fw_update_ctrl *fwu_ctrl) { struct goodix_ts_cmd temp_cmd; uint32_t total_checksum = fwu_ctrl->cfg_checksum + fwu_ctrl->fw_checksum; int ret; if (goodix_fw_update_reset(100)) ts_err("reset abnormal"); /* write total checksum to flash */ temp_cmd.len = 8; temp_cmd.cmd = 0xF3; temp_cmd.data[0] = total_checksum & 0xFF; temp_cmd.data[1] = (total_checksum >> 8) & 0xFF; temp_cmd.data[2] = (total_checksum >> 16) & 0xFF; temp_cmd.data[3] = (total_checksum >> 24) & 0xFF; ts_info("goodix_update_finish : write checksum = 0x%X", total_checksum); ret = fwu_ctrl->core_data->hw_ops->send_cmd(fwu_ctrl->core_data, &temp_cmd); if (ret < 0) ts_err("failed write total checksum into flash"); ret = goodix_fw_version_compare(fwu_ctrl); if (ret == COMPARE_EQUAL || ret == COMPARE_CFG_NOTEQUAL) return 0; return -EINVAL; } /** * goodix_fw_update_proc - firmware update process, the entry of * firmware update flow * @fwu_ctrl: firmware control * return: = 0 update ok, < 0 error or NO_NEED_UPDATE */ int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) { #define FW_UPDATE_RETRY 2 int retry0; int retry1 = FW_UPDATE_RETRY; int ret = 0; ret = goodix_parse_firmware(&fwu_ctrl->fw_data); if (ret < 0) return ret; start_update: fwu_ctrl->status = UPSTA_PREPARING; retry0 = FW_UPDATE_RETRY; do { ret = goodix_update_prepare(fwu_ctrl); if (ret) { ts_err("failed prepare ISP, retry %d", FW_UPDATE_RETRY - retry0); } } while (ret && --retry0 > 0); if (ret) { ts_err("Failed to prepare ISP, exit update:%d", ret); return ret; } /* progress: 20%~100% */ fwu_ctrl->status = UPSTA_UPDATING; ret = goodix_flash_firmware(fwu_ctrl); if (ret) ts_err("flash fw data enter error, ret:%d", ret); else ts_info("flash fw data success, need check version"); ret = goodix_update_finish(fwu_ctrl); if (ret && --retry1 > 0) { ts_err("update failed retry:%d", FW_UPDATE_RETRY - retry1); goto start_update; } if (!ret) ts_info("Firmware update successfully"); else ts_err("Firmware update failed, ret:%d", ret); return ret; } /** * goodix_request_firmware - request firmware data from user space * * @fw_data: firmware struct, contains firmware header info * and firmware data pointer. * return: 0 - OK, < 0 - error */ static int goodix_request_firmware(struct firmware_data *fw_data, struct fw_update_ctrl *fwu_ctrl) { int cfgPackageLen; int fwPackageLen; if (!fw_data->firmware) { ts_err("firmware is null"); return -EINVAL; } cfgPackageLen = be32_to_cpup((__be32 *)fw_data->firmware->data) + 6; if (fw_data->firmware->size <= (cfgPackageLen + 16)) { ts_err("current firmware does not contain goodix fw"); return -EINVAL; } fwu_ctrl->cfg_checksum = be16_to_cpup((__be16 *)&fw_data->firmware->data[4]); if (!(fw_data->firmware->data[cfgPackageLen + 0] == 'G' && fw_data->firmware->data[cfgPackageLen + 1] == 'X' && fw_data->firmware->data[cfgPackageLen + 2] == 'F' && fw_data->firmware->data[cfgPackageLen + 3] == 'W')) { ts_err("can't find fw package"); ts_err("Data type:%c%c%c%c != GXFW", fw_data->firmware->data[cfgPackageLen + 0], fw_data->firmware->data[cfgPackageLen + 1], fw_data->firmware->data[cfgPackageLen + 2], fw_data->firmware->data[cfgPackageLen + 3]); return -EINVAL; } fwPackageLen = be32_to_cpup((__be32 *)&fw_data->firmware->data[cfgPackageLen + 8]); ts_info("firmware package len:%d", fwPackageLen); if ((fwPackageLen + 16 + cfgPackageLen) > fw_data->firmware->size) { ts_err("bad firmware, need len[%d] != actual len[%d]", fwPackageLen + 16 + cfgPackageLen, (int)fw_data->firmware->size); return -EINVAL; } fw_data->fw.data = fw_data->firmware->data + cfgPackageLen + 16; fw_data->fw.size = fwPackageLen; ts_info("Firmware image is ready"); return 0; } /** * relase firmware resources * */ static inline void goodix_release_firmware(struct firmware_data *fw_data) { if (fw_data->firmware) { fw_data->firmware = NULL; memset(&fw_data->fw, 0, sizeof(fw_data->fw)); } } static int goodix_fw_update_thread(void *data) { struct fw_update_ctrl *fwu_ctrl = data; ktime_t start, end; int r = -EINVAL; start = ktime_get(); fwu_ctrl->spend_time = 0; fwu_ctrl->status = UPSTA_NOTWORK; mutex_lock(&fwu_ctrl->mutex); if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { ts_info("Firmware request update starts"); r = goodix_request_firmware(&fwu_ctrl->fw_data, fwu_ctrl); if (r < 0) goto out; } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { if (!fwu_ctrl->fw_data.firmware) { ts_err("Invalid firmware from sysfs"); r = -EINVAL; goto out; } } else { ts_err("unknown update mode 0x%x", fwu_ctrl->mode); r = -EINVAL; goto out; } ts_debug("notify update start"); goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); /* ready to update */ ts_debug("start update proc"); r = goodix_fw_update_proc(fwu_ctrl); /* clean */ if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { kfree(fwu_ctrl->fw_data.firmware); fwu_ctrl->fw_data.firmware = NULL; } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { goodix_release_firmware(&fwu_ctrl->fw_data); } out: fwu_ctrl->mode = UPDATE_MODE_DEFAULT; mutex_unlock(&fwu_ctrl->mutex); if (r) { ts_err("fw update failed, %d", r); fwu_ctrl->status = UPSTA_FAILED; goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); } else { ts_info("fw update success"); fwu_ctrl->status = UPSTA_SUCCESS; goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); } end = ktime_get(); fwu_ctrl->spend_time = ktime_to_ms(ktime_sub(end, start)); ts_info("spend_time: %dms", fwu_ctrl->spend_time); return r; } int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode) { struct task_struct *fwu_thrd; struct fw_update_ctrl *fwu_ctrl = &goodix_fw_update_ctrl; int ret; if (!fwu_ctrl->initialized) { ts_err("fw mode uninit"); return -EINVAL; } fwu_ctrl->mode = mode; fwu_ctrl->ic_config = ic_config; ts_debug("fw update mode 0x%x", mode); if (fwu_ctrl->mode & UPDATE_MODE_BLOCK) { ret = goodix_fw_update_thread(fwu_ctrl); ts_info("fw update return %d", ret); return ret; } /* create and run update thread */ fwu_thrd = kthread_run(goodix_fw_update_thread, fwu_ctrl, "goodix-fwu"); if (IS_ERR_OR_NULL(fwu_thrd)) { ts_err("Failed to create update thread:%ld", PTR_ERR(fwu_thrd)); return -EFAULT; } ts_info("success create fw update thread"); return 0; } int goodix_fw_update_init(struct goodix_ts_core *core_data, const struct firmware *firmware) { if (!core_data || !core_data->hw_ops) { ts_err("core_data && hw_ops cann't be null"); return -ENODEV; } if (!firmware) { ts_err("firmware is NULL"); return -EINVAL; } if (goodix_fw_update_ctrl.initialized == 0) mutex_init(&goodix_fw_update_ctrl.mutex); goodix_fw_update_ctrl.core_data = core_data; goodix_fw_update_ctrl.mode = 0; goodix_fw_update_ctrl.fw_data.firmware = firmware; goodix_fw_update_ctrl.update_info.isp_ram_reg = core_data->isp_ram_reg; goodix_fw_update_ctrl.update_info.flash_cmd_reg = core_data->flash_cmd_reg; goodix_fw_update_ctrl.update_info.isp_buffer_reg = core_data->isp_buffer_reg; goodix_fw_update_ctrl.update_info.config_data_reg = core_data->config_data_reg; goodix_fw_update_ctrl.update_info.misctl_reg = core_data->misctl_reg; goodix_fw_update_ctrl.update_info.watch_dog_reg = core_data->watch_dog_reg; goodix_fw_update_ctrl.update_info.config_id_reg = core_data->config_id_reg; goodix_fw_update_ctrl.update_info.enable_misctl_val = core_data->enable_misctl_val; goodix_fw_update_ctrl.initialized = 1; return 0; } void goodix_fw_update_uninit(void) { if (!goodix_fw_update_ctrl.initialized) return; mutex_lock(&goodix_fw_update_ctrl.mutex); goodix_fw_update_ctrl.initialized = 0; mutex_unlock(&goodix_fw_update_ctrl.mutex); mutex_destroy(&goodix_fw_update_ctrl.mutex); }