/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include "goodix_ts_core.h" #include "goodix_cfg_bin.h" #include "goodix_default_fw.h" #define TAG_FWU "" #define FW_HEADER_SIZE 256 #define FW_SUBSYS_INFO_SIZE 8 #define FW_SUBSYS_INFO_OFFSET 32 #define FW_SUBSYS_MAX_NUM 28 #define ISP_MAX_BUFFERSIZE (1024 * 4) #define HW_REG_CPU_CTRL 0x2180 #define HW_REG_DSP_MCU_POWER 0x2010 #define HW_REG_RESET 0x2184 #define HW_REG_SCRAMBLE 0x2218 #define HW_REG_BANK_SELECT 0x2048 #define HW_REG_ACCESS_PATCH0 0x204D #define HW_REG_EC_SRM_START 0x204F #define HW_REG_CPU_RUN_FROM 0x4506 /* for nor_L is 0x4006 */ #define HW_REG_ISP_RUN_FLAG 0x6006 #define HW_REG_ISP_ADDR 0xC000 #define HW_REG_ISP_BUFFER 0x6100 #define HW_REG_SUBSYS_TYPE 0x6020 #define HW_REG_FLASH_FLAG 0x6022 #define HW_REG_CACHE 0x204B #define HW_REG_ESD_KEY 0x2318 #define HW_REG_WTD_TIMER 0x20B0 #define CPU_CTRL_PENDING 0x00 #define CPU_CTRL_RUNNING 0x01 #define ISP_STAT_IDLE 0xFF #define ISP_STAT_READY 0xAA #define ISP_STAT_WRITING 0xAA #define ISP_FLASH_SUCCESS 0xBB #define ISP_FLASH_ERROR 0xCC #define ISP_FLASH_CHECK_ERROR 0xDD #define ISP_CMD_PREPARE 0x55 #define ISP_CMD_FLASH 0xAA #define TS_CHECK_ISP_STATE_RETRY_TIMES 200 #define TS_READ_FLASH_STATE_RETRY_TIMES 200 /*0: Header update */ /*1: request firmware update*/ atomic_t fw_update_mode = ATOMIC_INIT(1); /** * 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; u16 flash_addr; const u8 *data; }; #pragma pack(1) /** * firmware_info * @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 * @subsys: sybsystem info */ struct firmware_info { u32 size; u16 checksum; u8 hw_pid[6]; u8 hw_vid[3]; u8 fw_pid[8]; u8 fw_vid[4]; u8 subsys_num; u8 chip_type; u8 protocol_ver; u8 reserved[2]; struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; }; #pragma pack() /** * firmware_data - firmware data structure * @fw_info: firmware information * @firmware: firmware data structure */ struct firmware_data { struct firmware_info fw_info; const struct firmware *firmware; }; enum update_status { UPSTA_NOTWORK = 0, UPSTA_PREPARING, UPSTA_UPDATING, UPSTA_ABORT, UPSTA_SUCCESS, UPSTA_FAILED }; /** * fw_update_ctrl - structure used to control the * firmware update process * @status: update status * @progress: indicate the progress of update * @allow_reset: control the reset callback * @allow_irq: control the irq callback * @allow_suspend: control the suspend callback * @allow_resume: allow resume callback * @fw_data: firmware data * @ts_dev: touch device * @fw_name: firmware name * @attr_fwimage: sysfs bin attrs, for storing fw image * @fw_from_sysfs: whether the firmware image is loadind * from sysfs */ struct fw_update_ctrl { enum update_status status; unsigned int progress; bool force_update; bool allow_reset; bool allow_irq; bool allow_suspend; bool allow_resume; struct firmware_data fw_data; struct goodix_ts_device *ts_dev; struct goodix_ts_core *core_data; char fw_name[32]; struct bin_attribute attr_fwimage; bool fw_from_sysfs; }; /** * 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 */ static int goodix_parse_firmware(struct firmware_data *fw_data) { const struct firmware *firmware; struct firmware_info *fw_info; unsigned int i, fw_offset, info_offset; u16 checksum; int r = 0; if (!fw_data || !fw_data->firmware) { ts_err("Invalid firmware data"); return -EINVAL; } fw_info = &fw_data->fw_info; /* copy firmware head info */ firmware = fw_data->firmware; if (firmware->size < FW_SUBSYS_INFO_OFFSET) { ts_err("Invalid firmware size:%zu", firmware->size); r = -EINVAL; goto err_size; } memcpy(fw_info, firmware->data, FW_SUBSYS_INFO_OFFSET); /* check firmware size */ fw_info->size = be32_to_cpu(fw_info->size); if (firmware->size != fw_info->size + 6) { ts_err("Bad firmware, size not match"); r = -EINVAL; goto err_size; } /* calculate checksum, note: sum of bytes, but check*/ /* by u16 checksum */ for (i = 6, checksum = 0; i < firmware->size; i++) checksum += firmware->data[i]; /* byte order change, and check */ fw_info->checksum = be16_to_cpu(fw_info->checksum); if (checksum != fw_info->checksum) { ts_err("Bad firmware, cheksum error"); r = -EINVAL; goto err_size; } if (fw_info->subsys_num > FW_SUBSYS_MAX_NUM) { ts_err("Bad firmware, invalid subsys num: %d", fw_info->subsys_num); r = -EINVAL; goto err_size; } /* parse subsystem info */ fw_offset = FW_HEADER_SIZE; for (i = 0; i < fw_info->subsys_num; i++) { info_offset = FW_SUBSYS_INFO_OFFSET + i * FW_SUBSYS_INFO_SIZE; fw_info->subsys[i].type = firmware->data[info_offset]; fw_info->subsys[i].size = be32_to_cpup((__be32 *)&firmware->data[info_offset + 1]); fw_info->subsys[i].flash_addr = be16_to_cpup((__be16 *)&firmware->data[info_offset + 5]); if (fw_offset > firmware->size) { ts_err("Sybsys offset exceed Firmware size"); goto err_size; } fw_info->subsys[i].data = firmware->data + fw_offset; fw_offset += fw_info->subsys[i].size; } ts_info("Firmware package protocol: V%u", fw_info->protocol_ver); ts_info("Firmware PID:GT%s", fw_info->fw_pid); ts_info("Firmware VID:%02X%02X%02X", fw_info->fw_vid[0], fw_info->fw_vid[1], fw_info->fw_vid[2]); ts_info("Firmware chip type:%02X", fw_info->chip_type); ts_info("Firmware size:%u", fw_info->size); ts_info("Firmware subsystem num:%u", fw_info->subsys_num); #ifdef CONFIG_GOODIX_DEBUG for (i = 0; i < fw_info->subsys_num; i++) { ts_debug("------------------------------------------"); ts_debug("Index:%d", i); ts_debug("Subsystem type:%02X", fw_info->subsys[i].type); ts_debug("Subsystem size:%u", fw_info->subsys[i].size); ts_debug("Subsystem flash_addr:%08X", fw_info->subsys[i].flash_addr); ts_debug("Subsystem Ptr:%p", fw_info->subsys[i].data); } ts_debug("------------------------------------------"); #endif err_size: return r; } /** * goodix_check_update - compare the version of firmware running in * touch device with the version getting from the firmware file. * @fw_info: firmware information to be compared * return: 0 firmware in the touch device needs to be updated * < 0 no need to update firmware */ static int goodix_check_update(struct goodix_ts_device *dev, const struct firmware_info *fw_info) { struct goodix_ts_version fw_ver; /*u8 fwimg_cid;*/ int r = 0; int res = 0; /* read version from chip, if we got invalid*/ /* firmware version, maybe firmware in flash is*/ /* incorrect, so we need to update firmware */ r = dev->hw_ops->read_version(dev, &fw_ver); if (r == -EBUS) return r; if (fw_ver.valid) { if (memcmp(fw_ver.pid, fw_info->fw_pid, dev->reg.pid_len)) { ts_err("Product ID is not match"); return -EPERM; } /*fwimg_cid = fw_info->fw_vid[0];*/ res = memcmp(fw_ver.vid, fw_info->fw_vid, dev->reg.vid_len); if (res == 0) { ts_err("FW version is equal to the IC's"); return -EPERM; } else if (res > 0) { ts_info("Warning: fw version is lower the IC's"); } } /* else invalid firmware, update firmware */ ts_info("Firmware needs to be updated"); return 0; } /** * 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(struct goodix_ts_device *dev, unsigned int addr, unsigned char *data, unsigned int len) { u8 *cfm, cfm_buf[32]; int r, i; if (len > sizeof(cfm_buf)) { cfm = kzalloc(len, GFP_KERNEL); if (!cfm) { ts_err("Mem alloc failed"); return -ENOMEM; } } else { cfm = &cfm_buf[0]; } for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { r = dev->hw_ops->write_trans(dev, addr, data, len); if (r < 0) goto exit; r = dev->hw_ops->read_trans(dev, addr, cfm, len); if (r < 0) goto exit; if (memcmp(data, cfm, len)) { r = -EMEMCMP; continue; } else { r = 0; break; } } exit: if (cfm != &cfm_buf[0]) kfree(cfm); return r; } static inline int goodix_reg_write(struct goodix_ts_device *dev, unsigned int addr, unsigned char *data, unsigned int len) { return dev->hw_ops->write_trans(dev, addr, data, len); } static inline int goodix_reg_read(struct goodix_ts_device *dev, unsigned int addr, unsigned char *data, unsigned int len) { return dev->hw_ops->read_trans(dev, addr, data, len); } #if 0 #define MAX_MASK_BUF_SIZE (16*1024) static int goodix_load_mask(struct goodix_ts_device *ts_dev) { const struct firmware *mask_fw; const u8 *mask_name = "goodix_mask.bin"; u8 reg_val[10] = {0}; u32 total_size = 0, data_size = 0, offset = 0; int r, i; int index; ts_debug("Start load mask"); r = request_firmware(&mask_fw, mask_name, ts_dev->dev); if (r < 0) { ts_err("Firmware image [%s] not available,errno:%d", mask_name, r); return r; } else { ts_info("Firmware image [%s] is ready, size = %zu", mask_name, mask_fw->size); } /* enable AHB access */ reg_val[0] = 0x01; r = goodix_reg_write(ts_dev, 0x2049, reg_val, 1); if (r) { ts_err("Failed enbale AHB access"); goto mask_exit; } ts_debug("Success enable AHB access, Set 0x2049 --> 0x01"); /* switch to bank4 */ reg_val[0] = 0x04; r = goodix_reg_write(ts_dev, 0x2048, reg_val, 1); if (r) { ts_err("Failed switch to bank4"); goto mask_exit; } ts_debug("Success switch to bank4, Set 0x2048 -->0x04"); total_size = mask_fw->size; offset = 0; index = 1; while (total_size > 0) { data_size = total_size > MAX_MASK_BUF_SIZE ? MAX_MASK_BUF_SIZE : total_size; ts_info("Flash firmware to %08x,size:%u bytes", 0xC000 + offset, data_size); for (i = 0; i < 3; i++) { r = goodix_reg_write_confirm(ts_dev, 0xC000, (u8 *)mask_fw->data + offset, data_size); if (!r) break; else { ts_info("Failed write mask data retry.."); msleep(20); } } if (r) { ts_err("Failed send mask"); goto mask_exit; } offset += data_size; total_size -= data_size; /* switch to bank5 */ if (index == 1) { reg_val[0] = 0x05; r = goodix_reg_write(ts_dev, 0x2048, reg_val, 1); if (r) { ts_err("Failed switch to bank5"); goto mask_exit; } ts_debug("Success switch to bank5, Set 0x2048-->0x05"); } index++; } /* disable AHB access */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, 0x2049, reg_val, 1); if (r) { ts_err("Failed disbale AHB access"); goto mask_exit; } ts_debug("Success disable AHB access, Set 0x2049-->0x00"); ts_info("Success loak mask"); mask_exit: release_firmware(mask_fw); return r; } #endif /** * 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 goodix_ts_device *ts_dev, struct firmware_data *fw_data) { struct fw_subsys_info *fw_isp; u8 reg_val[8] = {0x00}; int r; int i; fw_isp = &fw_data->fw_info.subsys[0]; ts_info("Loading ISP start"); /* select bank0 */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_BANK_SELECT, reg_val, 1); if (r < 0) { ts_err("Failed to select bank0"); return r; } ts_debug("Success select bank0, Set 0x%x -->0x00", HW_REG_BANK_SELECT); /* enable bank0 access */ reg_val[0] = 0x01; r = goodix_reg_write(ts_dev, HW_REG_ACCESS_PATCH0, reg_val, 1); if (r < 0) { ts_err("Failed to enable patch0 access"); return r; } ts_debug("Success select bank0,Set 0x%x -->0x01", HW_REG_ACCESS_PATCH0); r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_ADDR, (u8 *)fw_isp->data, fw_isp->size); if (r < 0) { ts_err("Loading ISP error"); return r; } ts_debug("Success send ISP data to IC"); /* forbid patch access */ reg_val[0] = 0x00; r = goodix_reg_write_confirm(ts_dev, HW_REG_ACCESS_PATCH0, reg_val, 1); if (r < 0) { ts_err("Failed to disable patch0 access"); return r; } ts_debug("Success forbit bank0 accedd, set 0x%x -->0x00", HW_REG_ACCESS_PATCH0); /*clear 0x6006*/ reg_val[0] = 0x00; reg_val[1] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_ISP_RUN_FLAG, reg_val, 2); if (r < 0) { ts_err("Failed to clear 0x%x", HW_REG_ISP_RUN_FLAG); return r; } ts_debug("Success clear 0x%x", HW_REG_ISP_RUN_FLAG); /* TODO: change address 0xBDE6 set backdoor flag HW_REG_CPU_RUN_FROM */ memset(reg_val, 0x55, 8); r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, reg_val, 8); if (r < 0) { ts_err("Failed set backdoor flag"); return r; } ts_debug("Success write [8]0x55 to 0x%x", HW_REG_CPU_RUN_FROM); /* Emulation code SRAM start */ /* TODO: change reg_val 0x08---> 0x00 release ss51 */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); if (r < 0) { ts_err("Failed to run isp"); return r; } ts_debug("Success run isp, set 0x%x-->0x00", HW_REG_CPU_CTRL); /* check isp work state */ for (i = 0; i < TS_CHECK_ISP_STATE_RETRY_TIMES; i++) { r = goodix_reg_read(ts_dev, HW_REG_ISP_RUN_FLAG, reg_val, 2); if (r < 0 || (reg_val[0] == 0xAA && reg_val[1] == 0xBB)) break; usleep_range(5000, 5100); } if (reg_val[0] == 0xAA && reg_val[1] == 0xBB) { ts_info("ISP working OK"); return 0; } else { ts_err("ISP not work,0x%x=0x%x, 0x%x=0x%x", HW_REG_ISP_RUN_FLAG, reg_val[0], HW_REG_ISP_RUN_FLAG + 1, reg_val[1]); return -EFAULT; } } /** * 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) { struct goodix_ts_device *ts_dev = fwu_ctrl->ts_dev; u8 reg_val[4] = { 0x00 }; u8 temp_buf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int retry = 20; int r; ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM, temp_buf, 8); /*reset IC*/ fwu_ctrl->allow_reset = true; ts_info("normandy firmware update, reset"); gpio_direction_output(ts_dev->board_data->reset_gpio, 0); udelay(2000); gpio_direction_output(ts_dev->board_data->reset_gpio, 1); usleep_range(10000, 11000); fwu_ctrl->allow_reset = false; retry = 20; do { reg_val[0] = 0x24; r = goodix_reg_write_confirm(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); if (r < 0) { ts_info("Failed to hold ss51, retry"); msleep(20); } else { break; } } while (--retry); if (!retry) { ts_err("Failed hold ss51,return =%d", r); return -EINVAL; } ts_debug("Success hold ss51"); /* enable DSP & MCU power */ reg_val[0] = 0x00; r = goodix_reg_write_confirm(ts_dev, HW_REG_DSP_MCU_POWER, reg_val, 1); if (r < 0) { ts_err("Failed enable DSP&MCU power"); return r; } ts_debug("Success enabled DSP&MCU power,set 0x%x-->0x00", HW_REG_DSP_MCU_POWER); /* disable watchdog timer */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_CACHE, reg_val, 1); if (r < 0) { ts_err("Failed to clear cache"); return r; } ts_debug("Success clear cache"); reg_val[0] = 0x95; r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); reg_val[0] = 0x00; r |= goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); reg_val[0] = 0x27; r |= goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); if (r < 0) { ts_err("Failed to disable watchdog"); return r; } ts_debug("Success disable watchdog"); /* soft reset */ /* set scramble */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_SCRAMBLE, reg_val, 1); if (r < 0) { ts_err("Failed to set scramble"); return r; } ts_debug("Succcess set scramble"); /* load mask for emulation IC */ /* *r = goodix_load_mask(ts_dev); *if (r < 0) { * ts_err("Failed load mask"); * return r; *} *ts_debug("Success load mask"); */ /* load ISP code and run form isp */ r = goodix_load_isp(ts_dev, &fwu_ctrl->fw_data); if (r < 0) ts_err("Failed lode and run isp"); return r; } /** * goodix_format_fw_packet - formate one flash packet * @pkt: target firmware packet * @flash_addr: flash address * @size: packet size * @data: packet data */ static int goodix_format_fw_packet(u8 *pkt, u32 flash_addr, u16 len, const u8 *data) { u16 checksum; if (!pkt || !data) return -EINVAL; /* * checksum rule:sum of data in one format is equal to zero * data format: byte/le16/be16/le32/be32/le64/be64 */ pkt[0] = (len >> 8) & 0xff; pkt[1] = len & 0xff; /* u16 >> 16bit seems nosense but really important */ pkt[2] = (flash_addr >> 16) & 0xff; pkt[3] = (flash_addr >> 8) & 0xff; memcpy(&pkt[4], data, len); checksum = checksum_be16(pkt, len + 4); checksum = 0 - checksum; pkt[len + 4] = (checksum >> 8) & 0xff; pkt[len + 5] = checksum & 0xff; return 0; } /** * goodix_send_fw_packet - send one firmware packet to ISP * @dev: target touch device * @pkt: firmware packet * return> 0 ok, <0 error */ static int goodix_send_fw_packet(struct goodix_ts_device *dev, u8 type, u8 *pkt, u32 len) { u8 reg_val[4]; int r, i; if (!pkt) return -EINVAL; r = goodix_reg_write_confirm(dev, HW_REG_ISP_BUFFER, pkt, len); if (r < 0) { ts_err("Failed to write firmware packet"); return r; } reg_val[0] = 0; reg_val[1] = 0; /* clear flash flag 0X6022 */ r = goodix_reg_write_confirm(dev, HW_REG_FLASH_FLAG, reg_val, 2); if (r < 0) { ts_err("Faile to clear flash flag"); return r; } /* write subsystem type 0X8020*/ reg_val[0] = type; reg_val[1] = type; r = goodix_reg_write_confirm(dev, HW_REG_SUBSYS_TYPE, reg_val, 2); if (r < 0) { ts_err("Failed write subsystem type to IC"); return r; } for (i = 0; i < TS_READ_FLASH_STATE_RETRY_TIMES; i++) { r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); if (r < 0) { ts_err("Failed read flash state"); return r; } /* flash haven't end */ if (reg_val[0] == ISP_STAT_WRITING && reg_val[1] == ISP_STAT_WRITING) { ts_debug("Flash not ending..."); usleep_range(55000, 56000); continue; } if (reg_val[0] == ISP_FLASH_SUCCESS && reg_val[1] == ISP_FLASH_SUCCESS) { /* read twice to confirm the result */ r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); if (!r && reg_val[0] == ISP_FLASH_SUCCESS && reg_val[1] == ISP_FLASH_SUCCESS) { ts_info("Flash subsystem ok"); return 0; } } if (reg_val[0] == ISP_FLASH_ERROR && reg_val[1] == ISP_FLASH_ERROR) { ts_err(" Flash subsystem failed"); return -EAGAIN; } if (reg_val[0] == ISP_FLASH_CHECK_ERROR) { ts_err("Subsystem checksum err"); return -EAGAIN; } usleep_range(250, 260); } ts_err("Wait for flash end timeout, 0x6022= %x %x", reg_val[0], reg_val[1]); return -EAGAIN; } /** * 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 goodix_ts_device *dev, struct fw_subsys_info *subsys) { u16 data_size, offset; u32 total_size; u32 subsys_base_addr = subsys->flash_addr << 8; u8 *fw_packet; int r = 0, i; /* * 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 + 6, 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); /* format one firmware packet */ r = goodix_format_fw_packet(fw_packet, subsys_base_addr + offset, data_size, &subsys->data[offset]); if (r < 0) { ts_err("Invalid packet params"); goto exit; } /* send one firmware packet, retry 3 time if send failed */ for (i = 0; i < 3; i++) { r = goodix_send_fw_packet(dev, subsys->type, fw_packet, data_size + 6); if (!r) break; } if (r) { ts_err("Failed flash subsystem"); goto exit; } offset += data_size; total_size -= data_size; } /* end while */ exit: 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 goodix_ts_device *dev, struct firmware_data *fw_data) { struct fw_update_ctrl *fw_ctrl; struct firmware_info *fw_info; struct fw_subsys_info *fw_x; int retry = GOODIX_BUS_RETRY_TIMES; int i, r = 0, fw_num, prog_step; /* start from subsystem 1,*/ /* subsystem 0 is the ISP program */ fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data); fw_info = &fw_data->fw_info; fw_num = fw_info->subsys_num; /* we have 80% work here */ prog_step = 80 / (fw_num - 1); for (i = 1; i < fw_num && retry;) { ts_info("--- Start to flash subsystem[%d] ---", i); fw_x = &fw_info->subsys[i]; r = goodix_flash_subsystem(dev, fw_x); if (r == 0) { ts_info("--- End flash subsystem[%d]: OK ---", i); fw_ctrl->progress += prog_step; i++; } else if (r == -EAGAIN) { retry--; ts_err("--- End flash subsystem%d: Fail," TAG_FWU " errno:%d, retry:%d ---", i, r, GOODIX_BUS_RETRY_TIMES - retry); } else if (r < 0) { /* bus error */ ts_err("--- End flash subsystem%d:" TAG_FWU " Fatal error:%d exit ---", i, r); goto exit_flash; } } /*------Following is debug code---------*/ /*-------------------------------------*/ 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 goodix_ts_device *ts_dev, struct fw_update_ctrl *fwu_ctrl) { u8 reg_val[8] = {0}; int r = 0; /* hold ss51 */ reg_val[0] = 0x24; r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); if (r < 0) ts_err("Failed to hold ss51"); /* clear back door flag */ memset(reg_val, 0, sizeof(reg_val)); r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, reg_val, 8); if (r) { ts_err("Failed set CPU run from normal firmware"); return r; } /* release ss51 */ reg_val[0] = 0x00; r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); if (r < 0) ts_err("Failed to run ss51"); /*reset*/ r = ts_dev->hw_ops->reset(ts_dev); return r; } /** * goodix_fw_update_proc - firmware update process, the entry of * firmware update flow * @fwu_ctrl: firmware control * return: 0 ok, < 0 error */ int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) { #define FW_UPDATE_RETRY 2 int retry0 = FW_UPDATE_RETRY; int retry1 = FW_UPDATE_RETRY; int r = 0; if (fwu_ctrl->status == UPSTA_PREPARING || fwu_ctrl->status == UPSTA_UPDATING) { ts_err("Firmware update already in progress"); return 0; } fwu_ctrl->progress = 0; fwu_ctrl->status = UPSTA_PREPARING; r = goodix_parse_firmware(&fwu_ctrl->fw_data); if (r < 0) { fwu_ctrl->status = UPSTA_ABORT; goto err_parse_fw; } /* TODO: set force update flag*/ //fwu_ctrl->force_update = true; fwu_ctrl->progress = 10; if (fwu_ctrl->force_update == false) { r = goodix_check_update(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data.fw_info); if (r < 0) { fwu_ctrl->status = UPSTA_ABORT; goto err_check_update; } } start_update: fwu_ctrl->progress = 20; fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */ r = goodix_update_prepare(fwu_ctrl); if ((r == -EBUS || r == -EAGAIN) && --retry0 > 0) { ts_err("Bus error, retry prepare ISP:%d", FW_UPDATE_RETRY - retry0); goto start_update; } else if (r < 0) { ts_err("Failed to prepare ISP, exit update:%d", r); fwu_ctrl->status = UPSTA_FAILED; goto err_fw_prepare; } /* progress: 20%~100% */ r = goodix_flash_firmware(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data); if ((r == -EBUS || r == -ETIMEOUT) && --retry1 > 0) { /* we will retry[twice] if returns bus error[i2c/spi] * we will do hardware reset and re-prepare ISP and then retry * flashing */ ts_err("Bus error, retry firmware update:%d", FW_UPDATE_RETRY - retry1); goto start_update; } else if (r < 0) { ts_err("Fatal error, exit update:%d", r); fwu_ctrl->status = UPSTA_FAILED; goto err_fw_flash; } fwu_ctrl->status = UPSTA_SUCCESS; err_fw_flash: err_fw_prepare: goodix_update_finish(fwu_ctrl->ts_dev, fwu_ctrl); err_check_update: err_parse_fw: if (fwu_ctrl->status == UPSTA_SUCCESS) ts_info("Firmware update successfully"); else if (fwu_ctrl->status == UPSTA_FAILED) ts_err("Firmware update failed"); fwu_ctrl->progress = 100; /* 100% */ return r; } /* COMMON PART - END */ static struct goodix_ext_module goodix_fwu_module; /** * 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, const char *name) { struct fw_update_ctrl *fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data); struct device *dev = fw_ctrl->ts_dev->dev; int r; ts_info("Request firmware image [%s]", name); r = request_firmware(&fw_data->firmware, name, dev); if (r < 0) ts_err("Firmware image [%s] not available,errno:%d", name, r); else ts_info("Firmware image [%s] is ready", name); return r; } /** * relase firmware resources */ static inline void goodix_release_firmware(struct firmware_data *fw_data) { if (fw_data->firmware) { release_firmware(fw_data->firmware); fw_data->firmware = NULL; } } static int goodix_fw_update_thread(void *data) { struct fw_update_ctrl *fwu_ctrl = data; struct firmware *temp_firmware = NULL; static DEFINE_MUTEX(fwu_lock); int r; if (!fwu_ctrl) { ts_err("Invaild thread params"); goodix_unregister_ext_module(&goodix_fwu_module); return 0; } mutex_lock(&fwu_lock); if (!fwu_ctrl->fw_from_sysfs) { if (atomic_read(&fw_update_mode) == 0) { ts_info("Firmware header update starts"); temp_firmware = kzalloc(sizeof(struct firmware), GFP_KERNEL); if (!temp_firmware) { ts_err("Failed to allocate memory" TAG_FWU " for firmware"); goto out; } temp_firmware->size = sizeof(goodix_default_fw); temp_firmware->data = goodix_default_fw; fwu_ctrl->fw_data.firmware = temp_firmware; } else if (atomic_read(&fw_update_mode) == 1) { ts_info("Firmware request update starts"); r = goodix_request_firmware(&fwu_ctrl->fw_data, fwu_ctrl->fw_name); if (r < 0) { fwu_ctrl->status = UPSTA_ABORT; fwu_ctrl->progress = 100; goto out; } } } else { if (!fwu_ctrl->fw_data.firmware) { ts_err("Invalid firmware from sysfs"); fwu_ctrl->status = UPSTA_ABORT; fwu_ctrl->progress = 100; goto out; } } /* DONT allow reset/irq/suspend/resume during update */ fwu_ctrl->allow_irq = false; fwu_ctrl->allow_suspend = false; fwu_ctrl->allow_resume = false; fwu_ctrl->allow_reset = false; goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); /* ready to update */ goodix_fw_update_proc(fwu_ctrl); goodix_ts_blocking_notify(NOTIFY_FWUPDATE_END, NULL); fwu_ctrl->allow_reset = true; fwu_ctrl->allow_irq = true; fwu_ctrl->allow_suspend = true; fwu_ctrl->allow_resume = true; /* clean */ if (!fwu_ctrl->fw_from_sysfs) { if (atomic_read(&fw_update_mode) == 0) { kfree(fwu_ctrl->fw_data.firmware); fwu_ctrl->fw_data.firmware = NULL; temp_firmware = NULL; } else if (atomic_read(&fw_update_mode) == 1) { goodix_release_firmware(&fwu_ctrl->fw_data); } } else { fwu_ctrl->fw_from_sysfs = false; vfree(fwu_ctrl->fw_data.firmware); fwu_ctrl->fw_data.firmware = NULL; if (atomic_read(&fw_update_mode) == 0) temp_firmware = NULL; } /*parse cfg_group.bin*/ if (!fwu_ctrl->core_data->cfg_group_parsed) goodix_cfg_bin_proc(fwu_ctrl->core_data); out: goodix_unregister_ext_module(&goodix_fwu_module); mutex_unlock(&fwu_lock); atomic_set(&fw_update_mode, 0); return 0; } static ssize_t goodix_sysfs_update_progress_show( struct goodix_ext_module *module, char *buf) { struct fw_update_ctrl *fw_ctrl = module->priv_data; return scnprintf(buf, PAGE_SIZE, "%d\n", fw_ctrl->progress); } static ssize_t goodix_sysfs_update_result_show( struct goodix_ext_module *module, char *buf) { char *result = NULL; struct fw_update_ctrl *fw_ctrl = module->priv_data; ts_info("result show"); switch (fw_ctrl->status) { case UPSTA_NOTWORK: result = "notwork"; break; case UPSTA_PREPARING: result = "preparing"; break; case UPSTA_UPDATING: result = "upgrading"; break; case UPSTA_ABORT: result = "abort"; break; case UPSTA_SUCCESS: result = "success"; break; case UPSTA_FAILED: result = "failed"; break; default: break; } return scnprintf(buf, PAGE_SIZE, "%s\n", result); } static ssize_t goodix_sysfs_update_fwversion_show( struct goodix_ext_module *module, char *buf) { struct goodix_ts_version fw_ver; struct fw_update_ctrl *fw_ctrl = module->priv_data; int r = 0; char str[5]; /* read version from chip */ r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev, &fw_ver); if (!r) { memcpy(str, fw_ver.pid, 4); str[4] = '\0'; return scnprintf(buf, PAGE_SIZE, "PID:%s" TAG_FWU "VID:%02x %02x %02x %02x ID:%d\n", str, fw_ver.vid[0], fw_ver.vid[1], fw_ver.vid[2], fw_ver.vid[3], fw_ver.sensor_id); } return 0; } static ssize_t goodix_sysfs_fwsize_show(struct goodix_ext_module *module, char *buf) { struct fw_update_ctrl *fw_ctrl = module->priv_data; int r = -EINVAL; if (fw_ctrl && fw_ctrl->fw_data.firmware) r = snprintf(buf, PAGE_SIZE, "%zu\n", fw_ctrl->fw_data.firmware->size); return r; } static ssize_t goodix_sysfs_fwsize_store(struct goodix_ext_module *module, const char *buf, size_t count) { struct fw_update_ctrl *fw_ctrl = module->priv_data; struct firmware *fw; u8 **data; size_t size = 0; if (!fw_ctrl) return -EINVAL; if (sscanf(buf, "%zu", &size) < 0 || !size) { ts_err("Failed to get fwsize"); return -EFAULT; } fw = vmalloc(sizeof(*fw) + size); if (fw == NULL) { ts_err("Failed to alloc memory,size:%zu", size + sizeof(*fw)); return -ENOMEM; } memset(fw, 0x00, sizeof(*fw) + size); data = (u8 **)&fw->data; *data = (u8 *)fw + sizeof(struct firmware); fw->size = size; fw_ctrl->fw_data.firmware = fw; fw_ctrl->fw_from_sysfs = true; return count; } static ssize_t goodix_sysfs_fwimage_store(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { struct fw_update_ctrl *fw_ctrl; struct firmware_data *fw_data; fw_ctrl = container_of(attr, struct fw_update_ctrl, attr_fwimage); fw_data = &fw_ctrl->fw_data; if (!fw_data->firmware) { ts_err("Need set fw image size first"); return -ENOMEM; } if (fw_data->firmware->size == 0) { ts_err("Invalid firmware size"); return -EINVAL; } if (pos + count > fw_data->firmware->size) return -EFAULT; memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); fw_ctrl->force_update = true; return count; } static ssize_t goodix_sysfs_force_update_store( struct goodix_ext_module *module, const char *buf, size_t count) { struct fw_update_ctrl *fw_ctrl = module->priv_data; int val = 0, r; r = sscanf(buf, "%d", &val); if (r < 0) return r; if (r) fw_ctrl->force_update = true; else fw_ctrl->force_update = false; return count; } static struct goodix_ext_attribute goodix_fwu_attrs[] = { __EXTMOD_ATTR(progress, 0444, goodix_sysfs_update_progress_show, NULL), __EXTMOD_ATTR(result, 0444, goodix_sysfs_update_result_show, NULL), __EXTMOD_ATTR(fwversion, 0444, goodix_sysfs_update_fwversion_show, NULL), __EXTMOD_ATTR(fwsize, 0666, goodix_sysfs_fwsize_show, goodix_sysfs_fwsize_store), __EXTMOD_ATTR(force_update, 0222, NULL, goodix_sysfs_force_update_store), }; static int goodix_syfs_init(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct fw_update_ctrl *fw_ctrl = module->priv_data; struct kobj_type *ktype; int ret = 0, i; ktype = goodix_get_default_ktype(); ret = kobject_init_and_add(&module->kobj, ktype, &core_data->pdev->dev.kobj, "fwupdate"); if (ret) { ts_err("Create fwupdate sysfs node error!"); goto exit_sysfs_init; } for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) { if (sysfs_create_file(&module->kobj, &goodix_fwu_attrs[i].attr)) { ts_err("Create sysfs attr file error"); kobject_put(&module->kobj); ret = -EINVAL; goto exit_sysfs_init; } } fw_ctrl->attr_fwimage.attr.name = "fwimage"; fw_ctrl->attr_fwimage.attr.mode = 0666; fw_ctrl->attr_fwimage.size = 0; fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; #ifdef CONFIG_DEBUG_LOCK_ALLOC fw_ctrl->attr_fwimage.attr.ignore_lockdep = 1; #endif ret = sysfs_create_bin_file(&module->kobj, &fw_ctrl->attr_fwimage); exit_sysfs_init: return ret; } static int goodix_fw_update_init(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct goodix_ts_board_data *ts_bdata; struct task_struct *fwu_thrd; struct fw_update_ctrl *fwu_ctrl; static bool init_sysfs = true; int ret; char firmware_bin_name[32] = {0}; if (!core_data || !core_data->ts_dev) return -ENODEV; ts_bdata = board_data(core_data); if (!module->priv_data) { module->priv_data = kzalloc(sizeof(struct fw_update_ctrl), GFP_KERNEL); if (!module->priv_data) { ts_err("Failed to alloc memory for fwu_ctrl"); return -ENOMEM; } } fwu_ctrl = module->priv_data; fwu_ctrl->ts_dev = core_data->ts_dev; fwu_ctrl->allow_reset = true; fwu_ctrl->allow_irq = true; fwu_ctrl->allow_suspend = true; fwu_ctrl->allow_resume = true; fwu_ctrl->core_data = core_data; /*get firmware_bin_name*/ ret = snprintf(firmware_bin_name, sizeof(firmware_bin_name), "%s%s.bin", TS_DEFAULT_FIRMWARE, gt9886_firmware_buf); if (ret >= sizeof(firmware_bin_name)) ts_err("get firmware_bin_name name FAILED!!!"); ts_info("firmware_bin_name: %s", firmware_bin_name); /* find a valid firmware image name */ if (ts_bdata && ts_bdata->fw_name) strlcpy(fwu_ctrl->fw_name, ts_bdata->fw_name, sizeof(fwu_ctrl->fw_name)); else strlcpy(fwu_ctrl->fw_name, firmware_bin_name, sizeof(fwu_ctrl->fw_name)); /* create sysfs interface */ if (init_sysfs) { if (!goodix_syfs_init(core_data, module)) init_sysfs = false; } /* create and run update thread */ fwu_thrd = kthread_run(goodix_fw_update_thread, module->priv_data, "goodix-fwu"); if (IS_ERR_OR_NULL(fwu_thrd)) { ts_err("Failed to create update thread:%ld", PTR_ERR(fwu_thrd)); return -EFAULT; } return 0; } static int goodix_fw_update_exit(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { return 0; } static int goodix_fw_before_suspend(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_suspend ? EVT_HANDLED : EVT_CANCEL_SUSPEND; } static int goodix_fw_before_resume(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_resume ? EVT_HANDLED : EVT_CANCEL_RESUME; } static int goodix_fw_after_resume(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { return 0; } static int goodix_fw_irq_event(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_irq ? EVT_HANDLED : EVT_CANCEL_IRQEVT; } static int goodix_fw_before_reset(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_reset ? EVT_HANDLED : EVT_CANCEL_RESET; } static const struct goodix_ext_module_funcs goodix_ext_funcs = { .init = goodix_fw_update_init, .exit = goodix_fw_update_exit, .before_reset = goodix_fw_before_reset, .after_reset = NULL, .before_suspend = goodix_fw_before_suspend, .after_suspend = NULL, .before_resume = goodix_fw_before_resume, .after_resume = goodix_fw_after_resume, .irq_event = goodix_fw_irq_event, }; static struct goodix_ext_module goodix_fwu_module = { .name = "goodix-fwu", .funcs = &goodix_ext_funcs, .priority = EXTMOD_PRIO_FWUPDATE, }; static int __init goodix_fwu_module_init(void) { ts_info("run goodix_fwu_module\n"); return goodix_register_ext_module(&goodix_fwu_module); } static void __exit goodix_fwu_module_exit(void) { ; } late_initcall(goodix_fwu_module_init); module_exit(goodix_fwu_module_exit); MODULE_DESCRIPTION("Goodix FWU Module"); MODULE_AUTHOR("Goodix, Inc."); MODULE_LICENSE("GPL v2");