/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2016 MediaTek Inc. */ #include "goodix_ts_core.h" #include "goodix_cfg_bin.h" #include "goodix_default_fw.h" /* COMMON PART - START */ #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_GIO_YS 0x2014 #define HW_REG_ESD_KEY_EN 0x2318 #define HW_REG_ESD_KEY_DIS 0x2324 #define HW_REG_CPU_RUN_FROM 0x4506 /* for nor_L is 0x4006 */ #define HW_REG_CPU_RUN_FROM_YS 0x4000 #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 FLASH_ADDR_CONFIG_DATA 0x1E000 #define FLASH_SUBSYS_TYPE_CONFIG 0x03 #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 /** * fw_subsys_info - subsytem firmware infomation * @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 infomation * @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 - sturcture 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 * @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_data_src: firmware data source form sysfs, request or head file */ struct fw_update_ctrl { struct mutex mutex; int initialized; int mode; enum update_status status; unsigned int progress; bool allow_reset; bool allow_irq; bool allow_suspend; bool allow_resume; struct firmware_data fw_data; struct gt9896s_ts_device *ts_dev; struct gt9896s_ts_core *core_data; char fw_name[64]; struct bin_attribute attr_fwimage; }; static struct fw_update_ctrl gt9896s_fw_update_ctrl; /** * gt9896s_parse_firmware - parse firmware header infomation * and subsystem infomation from firmware data buffer * * @fw_data: firmware struct, contains firmware header info * and firmware data. * return: 0 - OK, < 0 - error */ static int gt9896s_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 %x(file) != %x(cal)", fw_info->checksum, checksum); 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("Fimware PID:GT%s", fw_info->fw_pid); ts_info("Fimware VID:%02X%02X%02X%02x", fw_info->fw_vid[0], fw_info->fw_vid[1], fw_info->fw_vid[2], fw_info->fw_vid[3]); 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; } /** * gt9896s_check_update - compare the version of firmware running in * touch device with the version getting from the firmware file. * @fw_info: firmware infomation to be compared * return: 0 no need do update, * otherwise need do update */ static int gt9896s_check_update(struct gt9896s_ts_device *dev, const struct firmware_info *fw_info) { struct gt9896s_ts_version fw_ver; int ret = -EINVAL; /* read version from chip, if we got invalid * firmware version, maybe fimware in flash is * incorrect, so we need to update firmware */ ret = dev->hw_ops->read_version(dev, &fw_ver); if (ret) { ts_info("failed get active pid"); return -EINVAL; } if (fw_ver.valid) { // should we compare PID before fw update? // if fw patch demage the PID may unmatch but // we should de update to recover it. // TODO skip PID check /*if (memcmp(fw_ver.pid, fw_info->fw_pid, dev->reg.pid_len)) { ts_err("Product ID is not match %s != %s", fw_ver.pid, fw_info->fw_pid); return -EPERM; }*/ ret = memcmp(fw_ver.vid, fw_info->fw_vid, dev->reg.vid_len); if (ret == 0) { ts_info("FW version is equal to the IC's"); return 0; } else if (ret > 0) { ts_info("Warning: fw version is lower the IC's"); } } /* else invalid firmware, update firmware */ ts_info("Firmware needs to be updated"); return ret; } /** * gt9896s_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 gt9896s_reg_write_confirm(struct gt9896s_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)) { ts_info("data[0]:0x%02x, data[1]:0x%02x," "read cfm[0]:0x%02x, cfm[1]:0x%02x", data[0], data[1], cfm[0], cfm[1]); dev->hw_ops->read_trans(dev, 0x6022, cfm, 2); ts_info("read 0x6022 data[0]:0x%02x, data[1]:0x%02x", cfm[0], cfm[1]); r = -EMEMCMP; continue; } else { r = 0; break; } } exit: if (cfm != &cfm_buf[0]) kfree(cfm); return r; } static inline int gt9896s_reg_write(struct gt9896s_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 gt9896s_reg_read(struct gt9896s_ts_device *dev, unsigned int addr, unsigned char *data, unsigned int len) { return dev->hw_ops->read_trans(dev, addr, data, len); } /** * gt9896s_load_isp - load ISP program to deivce ram * @dev: pointer to touch device * @fw_data: firmware data * return 0 ok, <0 error */ static int gt9896s_load_isp(struct gt9896s_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"); reg_val[0] = 0x00; r = gt9896s_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); reg_val[0] = 0x01; r = gt9896s_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 = gt9896s_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"); reg_val[0] = 0x00; r = gt9896s_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); reg_val[0] = 0x00; reg_val[1] = 0x00; r = gt9896s_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); memset(reg_val, 0x55, 8); if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE || ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) r = gt9896s_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, reg_val, 8); else r = gt9896s_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, reg_val, 8); if (r < 0) { ts_err("Failed set backdoor flag"); return r; } ts_info("Success write [8]0x55 to backdoor"); reg_val[0] = 0x00; r = gt9896s_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++) { msleep(10); r = gt9896s_reg_read(ts_dev, HW_REG_ISP_RUN_FLAG, reg_val, 2); if (r < 0 || (reg_val[0] == 0xAA && reg_val[1] == 0xBB)) break; } if (reg_val[0] == 0xAA && reg_val[1] == 0xBB) { ts_info("ISP working OK"); return 0; } 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; } int gt9896s_reset_ic_init(struct gt9896s_ts_device *ts_dev); /** * gt9896s_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 gt9896s_update_prepare(struct fw_update_ctrl *fwu_ctrl) { struct gt9896s_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; if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE || ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM_YS, temp_buf, 8); else ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM, temp_buf, 8); /*reset IC*/ fwu_ctrl->allow_reset = true; ts_info("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; if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) { r = gt9896s_reset_ic_init(ts_dev); if (r < 0) { ts_err("Failed to reset ic init!"); return -EINVAL; } } else if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { retry = 100; do { reg_val[0] = 0x00; r = gt9896s_reg_write_confirm(ts_dev, HW_REG_GIO_YS, reg_val, 1); if (r < 0) ts_info("Failed to remove GIO hold flag, retry %d", retry); else break; } while (--retry); if (!retry) { ts_err("Failed to remove GIO flag"); return -EINVAL; } } retry = 20; for (retry = 0; retry < 20; retry++) { if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE || ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) { reg_val[0] = 0x00; r = gt9896s_reg_write(ts_dev, HW_REG_ESD_KEY_DIS, reg_val, 1); if (r < 0) { ts_info("failed dis esd key"); continue; } reg_val[0] = 0x95; r = gt9896s_reg_write(ts_dev, HW_REG_ESD_KEY_EN, reg_val, 1); if (r < 0) { ts_info("failed open esd key"); continue; } } reg_val[0] = 0x24; r = gt9896s_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; } } if (retry >= 20) { ts_err("Failed hold ss51,return =%d", r); return -EINVAL; } ts_debug("Success hold ss51"); /* enable DSP & MCU power */ reg_val[0] = 0x00; r = gt9896s_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 = gt9896s_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"); if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE || ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) { reg_val[0] = 0x00; r = gt9896s_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); } else { reg_val[0] = 0x95; r = gt9896s_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); reg_val[0] = 0x00; r |= gt9896s_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); reg_val[0] = 0x27; r |= gt9896s_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); } if (r < 0) { ts_err("Failed to disable watchdog"); return r; } ts_info("Success disable watchdog"); /* set scramble */ reg_val[0] = 0x00; r = gt9896s_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 ISP code and run form isp */ r = gt9896s_load_isp(ts_dev, &fwu_ctrl->fw_data); if (r < 0) ts_err("Failed lode and run isp"); return r; } /** * gt9896s_format_fw_packet - formate one flash packet * @pkt: target firmware packet * @flash_addr: flash address * @size: packet size * @data: packet data */ static int gt9896s_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; } /** * gt9896s_send_fw_packet - send one firmware packet to ISP * @dev: target touch device * @pkt: firmware packet * return:0 ok, <0 error */ static int gt9896s_send_fw_packet(struct gt9896s_ts_device *dev, u8 type, u8 *pkt, u32 len) { u8 reg_val[4]; int r, i; if (!pkt) return -EINVAL; ts_info("target fw subsys type:0x%x, len %d", type, len); r = gt9896s_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 = gt9896s_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 = gt9896s_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 = gt9896s_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) { r = gt9896s_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; } /** * gt9896s_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 infomation * return: 0 ok, < 0 error */ static int gt9896s_flash_subsystem(struct gt9896s_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 = gt9896s_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 = gt9896s_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; } /** * gt9896s_flash_firmware - flash firmware * @dev: pointer to touch device * @fw_data: firmware data * return: 0 ok, < 0 error */ static int gt9896s_flash_firmware(struct gt9896s_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; u8 *fw_packet = NULL; u8 *flash_cfg = NULL; /* 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 = gt9896s_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, 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); break; } } kfree(fw_packet); fw_packet = NULL; kfree(flash_cfg); flash_cfg = NULL; return r; } /** * gt9896s_update_finish - update finished, free resource * and reset flags--- * @fwu_ctrl: pointer to fw_update_ctrl structrue * return: 0 ok, < 0 error */ static int gt9896s_update_finish(struct gt9896s_ts_device *ts_dev, struct fw_update_ctrl *fwu_ctrl) { u8 reg_val[8] = {0}; int r = 0, i = 0; /* hold ss51 */ reg_val[0] = 0x24; r = gt9896s_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)); if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE || ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) r = gt9896s_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, reg_val, 8); else r = gt9896s_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 = gt9896s_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); if (r < 0) ts_err("Failed to run ss51"); /*reset*/ gpio_direction_output(ts_dev->board_data.reset_gpio, 0); udelay(2000); gpio_direction_output(ts_dev->board_data.reset_gpio, 1); msleep(80); if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { for (i = 0; i < 100; i++) { reg_val[0] = 0x00; r = gt9896s_reg_write_confirm(ts_dev, HW_REG_GIO_YS, reg_val, 1); if (!r) break; ts_info("failed set GIO flag, r %d", r); } if (i >= 100) ts_err("failed set GIO flag, %d", r); } else if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE_SPI) { r = gt9896s_reset_ic_init(ts_dev); if (r < 0) ts_err("Failed to reset ic init!"); } return r; } static int gt9896s_flash_config(struct gt9896s_ts_device *ts_dev) { int ret; u8 *fw_packet = NULL; u8 *temp_data = NULL; if (!ts_dev || !ts_dev->cfg_bin_state || !ts_dev->normal_cfg.initialized) { ts_err("no valid config data for flash"); return -EINVAL; } temp_data = kzalloc(ISP_MAX_BUFFERSIZE, GFP_KERNEL); fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 6, GFP_KERNEL); if (!temp_data || !fw_packet) { ts_err("Failed alloc memory"); ret = -EINVAL; goto exit; } memset(temp_data, 0xFF, ISP_MAX_BUFFERSIZE); ts_info("normal config length %d", ts_dev->normal_cfg.length); memcpy(temp_data, ts_dev->normal_cfg.data, ts_dev->normal_cfg.length); /* format one firmware packet */ ret = gt9896s_format_fw_packet(fw_packet, FLASH_ADDR_CONFIG_DATA, ISP_MAX_BUFFERSIZE, temp_data); if (ret < 0) { ts_err("Invalid packet params"); goto exit; } ts_debug("fw_pack:%*ph", 10, fw_packet); ts_info("try flash config"); ret = gt9896s_send_fw_packet(ts_dev, FLASH_SUBSYS_TYPE_CONFIG, fw_packet, ISP_MAX_BUFFERSIZE + 6); if (ret) ts_err("failed flash config, ret %d", ret); else ts_info("success flash config with isp"); exit: kfree(temp_data); kfree(fw_packet); return ret; } /** * gt9896s_fw_update_proc - firmware update process, the entry of * firmware update flow * @fwu_ctrl: firmware control * return: 0 ok, < 0 error */ static int gt9896s_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 -EINVAL; } fwu_ctrl->progress = 0; fwu_ctrl->status = UPSTA_PREPARING; r = gt9896s_parse_firmware(&fwu_ctrl->fw_data); if (r < 0) { fwu_ctrl->status = UPSTA_ABORT; goto err_parse_fw; } fwu_ctrl->progress = 10; if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { r = gt9896s_check_update(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data.fw_info); if (!r) { fwu_ctrl->status = UPSTA_ABORT; ts_info("fw update skiped"); goto err_check_update; } } else { ts_info("force update mode"); } start_update: fwu_ctrl->progress = 20; fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */ r = gt9896s_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; } if (GOODIX_FLASH_CONFIG_WITH_ISP && fwu_ctrl->mode & UPDATE_MODE_FLASH_CFG) { ts_info("need flash config with isp"); gt9896s_flash_config(fwu_ctrl->ts_dev); } /* progress: 20%~100% */ r = gt9896s_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: gt9896s_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% */ ts_info("fw update ret %d", r); return r; } static struct gt9896s_ext_module gt9896s_fwu_module; /** * gt9896s_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 gt9896s_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 gt9896s_release_firmware(struct firmware_data *fw_data) { if (fw_data->firmware) { release_firmware(fw_data->firmware); fw_data->firmware = NULL; } } static int gt9896s_fw_update_thread(void *data) { struct fw_update_ctrl *fwu_ctrl = data; struct firmware *temp_firmware = NULL; int r = -EINVAL; struct fw_update_ctrl *fw_ctrl = >9896s_fw_update_ctrl; mutex_lock(&fwu_ctrl->mutex); if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { ts_info("Firmware header update starts"); temp_firmware = kzalloc(sizeof(struct firmware), GFP_KERNEL); if (!temp_firmware) { ts_err("Failed to allocate memory for firmware"); goto out; } temp_firmware->size = sizeof(gt9896s_default_fw); temp_firmware->data = gt9896s_default_fw; fwu_ctrl->fw_data.firmware = temp_firmware; } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { ts_info("Firmware request update starts"); r = gt9896s_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->mode & UPDATE_MODE_SRC_SYSFS) { if (!fwu_ctrl->fw_data.firmware) { ts_err("Invalid firmware from sysfs"); fwu_ctrl->status = UPSTA_ABORT; fwu_ctrl->progress = 100; r = -EINVAL; goto out; } } else { ts_err("unknown update mode 0x%x", fwu_ctrl->mode); r = -EINVAL; goto out; } if (!fw_ctrl->initialized) gt9896s_register_ext_module(>9896s_fwu_module); /* 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; ts_debug("notify update start"); gt9896s_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); /* ready to update */ ts_debug("start update proc"); r = gt9896s_fw_update_proc(fwu_ctrl); fwu_ctrl->allow_reset = true; fwu_ctrl->allow_irq = true; fwu_ctrl->allow_suspend = true; fwu_ctrl->allow_resume = true; /* clean */ if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { kfree(fwu_ctrl->fw_data.firmware); fwu_ctrl->fw_data.firmware = NULL; temp_firmware = NULL; } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { gt9896s_release_firmware(&fwu_ctrl->fw_data); } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { vfree(fwu_ctrl->fw_data.firmware); fwu_ctrl->fw_data.firmware = NULL; } out: fwu_ctrl->mode = UPDATE_MODE_DEFAULT; mutex_unlock(&fwu_ctrl->mutex); gt9896s_unregister_ext_module(>9896s_fwu_module); if (r) { ts_err("fw update failed, %d", r); gt9896s_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); } else { ts_info("fw update success"); gt9896s_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); } return r; } /* * gt9896s_sysfs_update_en_store: start fw update manually * @buf: '1'[001] update in blocking mode with fwdata from sysfs * '2'[010] update in blocking mode with fwdata from request * '5'[101] update in unblocking mode with fwdata from sysfs * '6'[110] update in unblocking mode with fwdata from request */ static ssize_t gt9896s_sysfs_update_en_store( struct gt9896s_ext_module *module, const char *buf, size_t count) { int ret = 0; int mode = 0; struct fw_update_ctrl *fw_ctrl = module->priv_data; if (!buf || count <= 0) { ts_err("invalid params"); return -EINVAL; } if (!fw_ctrl || !fw_ctrl->initialized) { ts_err("fw module uninit"); return -EINVAL; } ts_info("set update mode:0x%x", buf[0]); if (buf[0] == '1') { mode = UPDATE_MODE_FORCE|UPDATE_MODE_BLOCK|UPDATE_MODE_SRC_SYSFS; } else if (buf[0] == '2') { mode = UPDATE_MODE_FORCE|UPDATE_MODE_BLOCK|UPDATE_MODE_SRC_REQUEST; } else if (buf[0] == '5') { mode = UPDATE_MODE_FORCE|UPDATE_MODE_SRC_SYSFS; } else if (buf[0] == '6') { mode = UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST; } else { ts_err("invalid update mode:0x%x", buf[0]); return -EINVAL; } ret = gt9896s_do_fw_update(mode); if (!ret) { ts_info("success start update work"); return count; } ts_err("failed start fw update work"); return -EINVAL; } static ssize_t gt9896s_sysfs_update_progress_show( struct gt9896s_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 gt9896s_sysfs_update_result_show( struct gt9896s_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 gt9896s_sysfs_update_fwversion_show( struct gt9896s_ext_module *module, char *buf) { struct gt9896s_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 VID:%02x %02x %02x %02x SENSOR_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 gt9896s_sysfs_fwsize_show(struct gt9896s_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 gt9896s_sysfs_fwsize_store(struct gt9896s_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; } /* use vmalloc to alloc huge memory */ fw = vmalloc(sizeof(*fw) + size); if (fw == NULL) { ts_err("Failed to alloc memory,size:%zu", size + sizeof(*fw)); return -ENOMEM; } mutex_lock(&fw_ctrl->mutex); 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->mode = UPDATE_MODE_SRC_SYSFS; mutex_unlock(&fw_ctrl->mutex); return count; } static ssize_t gt9896s_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; if(IS_ERR_OR_NULL(attr) || IS_ERR_OR_NULL(kobj)) { ts_err("attr or kobj is invalid or NULL!!!\n"); return -EINVAL; } fw_ctrl = container_of(attr, struct fw_update_ctrl, attr_fwimage); fw_data = &fw_ctrl->fw_data; if ((!fw_data->firmware) || (!buf)) { 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; if(IS_ERR_OR_NULL(buf)) { ts_err("The buf is invalid!!!\n"); return -ENOMEM; } if (IS_ERR_OR_NULL(&fw_data->firmware->data[pos])) { ts_err("fw_data->firmware->data[pos] is invalid or NULL!!!"); return -ENOMEM; } mutex_lock(&fw_ctrl->mutex); memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); mutex_unlock(&fw_ctrl->mutex); return count; } /* this interface has ben deprecated */ static ssize_t gt9896s_sysfs_force_update_store( struct gt9896s_ext_module *module, const char *buf, size_t count) { return count; } static struct gt9896s_ext_attribute gt9896s_fwu_attrs[] = { __EXTMOD_ATTR(update_en, 0220, NULL, gt9896s_sysfs_update_en_store), __EXTMOD_ATTR(progress, S_IRUGO, gt9896s_sysfs_update_progress_show, NULL), __EXTMOD_ATTR(result, S_IRUGO, gt9896s_sysfs_update_result_show, NULL), __EXTMOD_ATTR(fwversion, S_IRUGO, gt9896s_sysfs_update_fwversion_show, NULL), __EXTMOD_ATTR(fwsize, 0660, gt9896s_sysfs_fwsize_show, gt9896s_sysfs_fwsize_store), __EXTMOD_ATTR(force_update, 0220, NULL, gt9896s_sysfs_force_update_store), }; static int gt9896s_fw_sysfs_init(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { struct fw_update_ctrl *fw_ctrl = module->priv_data; struct kobj_type *ktype; int ret = 0, i; ktype = gt9896s_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; } ret = 0; for (i = 0; i < ARRAY_SIZE(gt9896s_fwu_attrs) && !ret; i++) ret = sysfs_create_file(&module->kobj, >9896s_fwu_attrs[i].attr); if (ret) { ts_err("failed create fwu sysfs files"); while (--i >= 0) sysfs_remove_file(&module->kobj, >9896s_fwu_attrs[i].attr); kobject_put(&module->kobj); ret = -EINVAL; goto exit_sysfs_init; } fw_ctrl->attr_fwimage.attr.name = "fwimage"; fw_ctrl->attr_fwimage.attr.mode = 0660; fw_ctrl->attr_fwimage.size = 0; fw_ctrl->attr_fwimage.write = gt9896s_sysfs_fwimage_store; ret = sysfs_create_bin_file(&module->kobj, &fw_ctrl->attr_fwimage); exit_sysfs_init: return ret; } static void gt9896s_fw_sysfs_remove(struct gt9896s_ext_module *module) { struct fw_update_ctrl *fw_ctrl = module->priv_data; int i; sysfs_remove_bin_file(&module->kobj, &fw_ctrl->attr_fwimage); for (i = 0; i < ARRAY_SIZE(gt9896s_fwu_attrs); i++) sysfs_remove_file(&module->kobj, >9896s_fwu_attrs[i].attr); kobject_put(&module->kobj); } int gt9896s_do_fw_update(int mode) { struct task_struct *fwu_thrd; struct fw_update_ctrl *fwu_ctrl = >9896s_fw_update_ctrl; int ret; if (!fwu_ctrl->initialized) { ts_err("fw mode uninit"); return -EINVAL; } fwu_ctrl->mode = mode; ts_debug("fw update mode 0x%x", mode); if (fwu_ctrl->mode & UPDATE_MODE_BLOCK) { ret = gt9896s_fw_update_thread(fwu_ctrl); ts_info("fw update return %d", ret); return ret; } else { /* create and run update thread */ fwu_thrd = kthread_run(gt9896s_fw_update_thread, fwu_ctrl, "gt9896s-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; } } static int gt9896s_fw_update_init(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { int ret = 0; struct gt9896s_ts_board_data *ts_bdata = board_data(core_data); if (gt9896s_fw_update_ctrl.initialized) { ts_info("no need reinit"); return ret; } if (!core_data || !ts_bdata || !core_data->ts_dev) { ts_err("core_data && ts_dev cann't be null"); return -ENODEV; } mutex_lock(>9896s_fw_update_ctrl.mutex); module->priv_data = >9896s_fw_update_ctrl; gt9896s_fw_update_ctrl.ts_dev = core_data->ts_dev; gt9896s_fw_update_ctrl.allow_reset = true; gt9896s_fw_update_ctrl.allow_irq = true; gt9896s_fw_update_ctrl.allow_suspend = true; gt9896s_fw_update_ctrl.allow_resume = true; gt9896s_fw_update_ctrl.core_data = core_data; gt9896s_fw_update_ctrl.mode = 0; /* find a valid firmware image name */ if (ts_bdata && ts_bdata->fw_name) strlcpy(gt9896s_fw_update_ctrl.fw_name, ts_bdata->fw_name, sizeof(gt9896s_fw_update_ctrl.fw_name)); else { if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2280) { ret = snprintf(gt9896s_fw_update_ctrl.fw_name, sizeof(gt9896s_fw_update_ctrl.fw_name), "%s%s_1080x2280.bin", TS_DEFAULT_FIRMWARE, gt9896s_firmware_buf); } else if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2300) { ret = snprintf(gt9896s_fw_update_ctrl.fw_name, sizeof(gt9896s_fw_update_ctrl.fw_name), "%s%s_1080x2300.bin", TS_DEFAULT_FIRMWARE, gt9896s_firmware_buf); } else { ret = snprintf(gt9896s_fw_update_ctrl.fw_name, sizeof(gt9896s_fw_update_ctrl.fw_name), "%s%s.bin", TS_DEFAULT_FIRMWARE, gt9896s_firmware_buf); } ts_info("firmware_bin_name %s!!!", gt9896s_fw_update_ctrl.fw_name); if (ret >= sizeof(gt9896s_fw_update_ctrl.fw_name)) ts_err("get firmware_bin_name name FAILED!!!"); } ret = gt9896s_fw_sysfs_init(core_data, module); if (ret) { ts_err("failed create fwupate sysfs node"); goto err_out; } gt9896s_fw_update_ctrl.initialized = 1; err_out: mutex_unlock(>9896s_fw_update_ctrl.mutex); return ret; } static int gt9896s_fw_update_exit(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { return 0; } static int gt9896s_fw_before_suspend(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_suspend ? EVT_HANDLED : EVT_CANCEL_SUSPEND; } static int gt9896s_fw_before_resume(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_resume ? EVT_HANDLED : EVT_CANCEL_RESUME; } static int gt9896s_fw_after_resume(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { return 0; } static int gt9896s_fw_irq_event(struct gt9896s_ts_core *core_data, struct gt9896s_ext_module *module) { struct fw_update_ctrl *fwu_ctrl = module->priv_data; return fwu_ctrl->allow_irq ? EVT_HANDLED : EVT_CANCEL_IRQEVT; } static int gt9896s_fw_before_reset(struct gt9896s_ts_core *core_data, struct gt9896s_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 gt9896s_ext_module_funcs gt9896s_ext_funcs = { .init = gt9896s_fw_update_init, .exit = gt9896s_fw_update_exit, .before_reset = gt9896s_fw_before_reset, .after_reset = NULL, .before_suspend = gt9896s_fw_before_suspend, .after_suspend = NULL, .before_resume = gt9896s_fw_before_resume, .after_resume = gt9896s_fw_after_resume, .irq_event = gt9896s_fw_irq_event, }; static struct gt9896s_ext_module gt9896s_fwu_module = { .name = "gt9896s-fwu", .funcs = >9896s_ext_funcs, .priority = EXTMOD_PRIO_FWUPDATE, }; static int __init gt9896s_fwu_module_init(void) { ts_info("gt9896s_fwupdate_module_ini IN"); mutex_init(>9896s_fw_update_ctrl.mutex); return gt9896s_register_ext_module(>9896s_fwu_module); } static void __exit gt9896s_fwu_module_exit(void) { mutex_lock(>9896s_fw_update_ctrl.mutex); gt9896s_unregister_ext_module(>9896s_fwu_module); if (gt9896s_fw_update_ctrl.initialized) { gt9896s_fw_sysfs_remove(>9896s_fwu_module); gt9896s_fw_update_ctrl.initialized = 0; } mutex_lock(>9896s_fw_update_ctrl.mutex); } late_initcall(gt9896s_fwu_module_init); module_exit(gt9896s_fwu_module_exit); MODULE_DESCRIPTION("Goodix FWU Module"); MODULE_AUTHOR("Goodix, Inc."); MODULE_LICENSE("GPL v2");