From 0ab2ef53fdc613b502b32ab935e0274b18226c08 Mon Sep 17 00:00:00 2001 From: haojl2 Date: Mon, 23 Sep 2019 17:10:18 +0800 Subject: [PATCH] goodix_gtx8_v1430_mmi: Add update firmware code TP: Add update firmware code Change-Id: I2837a2b80dfaf11d265756eb07872fb9e9ce7721 Signed-off-by: haojl2 Reviewed-on: https://gerrit.mot.com/1426318 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Tao Sun Submit-Approved: Jira Key --- .../goodix_gtx8_v1430_mmi/Android.mk | 6 + .../touchscreen/goodix_gtx8_v1430_mmi/Kbuild | 3 + .../goodix_gtx8_v1430_mmi/goodix_cfg_bin.c | 20 +- .../goodix_gtx8_v1430_mmi/goodix_default_fw.h | 6 + .../goodix_gtx8_update.c | 1613 +++++++++++++++++ .../goodix_gtx8_v1430_mmi/goodix_ts_core.h | 10 +- .../goodix_gtx8_v1430_mmi/goodix_ts_i2c.c | 10 +- 7 files changed, 1650 insertions(+), 18 deletions(-) create mode 100644 drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h create mode 100644 drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk index 069c4d8fb9a3..5500f5c0fa52 100644 --- a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk @@ -7,4 +7,10 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_v1430_update_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild index d4c517cba904..e5739fb9c849 100644 --- a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild @@ -7,3 +7,6 @@ EXTRA_CFLAGS += \ obj-m := goodix_v1430_mmi.o goodix_v1430_mmi-objs = goodix_ts_i2c.o goodix_ts_core.o goodix_cfg_bin.o +obj-m += goodix_v1430_update_mmi.o +goodix_v1430_update_mmi-objs = goodix_gtx8_update.o + diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c index 17fa83fb2879..53540d4f8e22 100644 --- a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c @@ -524,7 +524,11 @@ static int goodix_later_init_thread(void *data) if (ts_dev->cfg_bin_state == CFG_BIN_STATE_TEMP) { ts_err("failed get valid config data, retry after fwupdate"); - ret = goodix_do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE| + if (!ts_core->do_fw_update) { + ts_err("fw update handler not ready."); + goto release_core; + } + ret = ts_core->do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE| UPDATE_MODE_FLASH_CFG| UPDATE_MODE_SRC_REQUEST); if (ret) { @@ -539,12 +543,14 @@ static int goodix_later_init_thread(void *data) } } else { ts_info("success parse config bin"); - ret = goodix_do_fw_update(UPDATE_MODE_BLOCK| - UPDATE_MODE_FLASH_CFG| - UPDATE_MODE_SRC_REQUEST); - if (ret) { - ts_err("fw update failed, %d[ignore]", ret); - ret = 0; + if (ts_core->do_fw_update) { + ret = ts_core->do_fw_update(UPDATE_MODE_BLOCK| + UPDATE_MODE_FLASH_CFG| + UPDATE_MODE_SRC_REQUEST); + if (ret) { + ts_err("fw update failed, %d[ignore]", ret); + ret = 0; + } } } ret = goodix_ts_stage2_init(ts_core); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h new file mode 100644 index 000000000000..1632e6ef3c76 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h @@ -0,0 +1,6 @@ +#ifndef _GOODIX_FIRMWARE_FW_H_ +#define _GOODIX_FIRMWARE_FW_H_ + +u8 goodix_default_fw[] = { 0x00}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c new file mode 100644 index 000000000000..a527396d39fb --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c @@ -0,0 +1,1613 @@ +/* + * Goodix Firmware Update Driver. + * + * Copyright (C) 2019 - 2020 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" +#include "goodix_cfg_bin.h" +#include "goodix_default_fw.h" +/* COMMON PART - START */ +#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" + +#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 goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + + char fw_name[32]; + struct bin_attribute attr_fwimage; +}; +static struct fw_update_ctrl goodix_fw_update_ctrl; + +static int goodix_do_fw_update(int mode); + +/** + * goodix_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 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 %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; +} + +/** + * goodix_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 goodix_check_update(struct goodix_ts_device *dev, + const struct firmware_info *fw_info) +{ + struct goodix_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; +} + +/** + * 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)) { + 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 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); +} + +/** + * goodix_load_isp - load ISP program to deivce 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"); + 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); + + 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"); + + 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); + + 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); + + memset(reg_val, 0x55, 8); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + 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_info("Success write [8]0x55 to backdoor"); + + 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++) { + msleep(10); + 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; + } + 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; +} + +/** + * 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; + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + 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; + + retry = 100; + do { + if (ts_dev->ic_type != IC_TYPE_YELLOWSTONE) + break; + reg_val[0] = 0x00; + r = goodix_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) { + reg_val[0] = 0x00; + r = goodix_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 = goodix_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 = 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; + } + } + 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 = 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"); + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); + } else { + 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_info("Success disable watchdog"); + + /* 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 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; + + ts_info("target fw subsys type:0x%x, len %d", type, len); + 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) { + 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 infomation + * 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; + 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 = 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, 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; +} + +/** + * 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, i = 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)); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + 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*/ + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + msleep(80); + + for (i = 0; ts_dev->ic_type == IC_TYPE_YELLOWSTONE && i < 100; i++) { + reg_val[0] = 0x00; + r = goodix_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); + + return r; +} + +static int goodix_flash_config(struct goodix_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 = goodix_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 = goodix_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; +} + +/** + * goodix_fw_update_proc - firmware update process, the entry of + * firmware update flow + * @fwu_ctrl: firmware control + * return: 0 ok, < 0 error + */ +static 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 -EINVAL; + } + + 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; + } + + fwu_ctrl->progress = 10; + if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { + r = goodix_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 = 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; + } + + if (GOODIX_FLASH_CONFIG_WITH_ISP && + fwu_ctrl->mode & UPDATE_MODE_FLASH_CFG) { + ts_info("need flash config with isp"); + goodix_flash_config(fwu_ctrl->ts_dev); + } + + /* 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% */ + ts_info("fw update ret %d", r); + return r; +} + +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; + int r = -EINVAL; + + 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(goodix_default_fw); + temp_firmware->data = goodix_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 = 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->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; + } + + goodix_register_ext_module(&goodix_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"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + ts_debug("start update proc"); + r = goodix_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) { + goodix_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; + } + goodix_unregister_ext_module(&goodix_fwu_module); +out: + fwu_ctrl->mode = UPDATE_MODE_DEFAULT; + mutex_unlock(&fwu_ctrl->mutex); + + if (r) { + ts_err("fw update failed, %d", r); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); + } else { + ts_info("fw update success"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); + } + return r; +} + +/* + * goodix_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 goodix_sysfs_update_en_store( + struct goodix_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 = goodix_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 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 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 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; + } + + /* 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 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; + 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 goodix_sysfs_force_update_store( + struct goodix_ext_module *module, + const char *buf, size_t count) +{ + return count; +} + +static struct goodix_ext_attribute goodix_fwu_attrs[] = { + __EXTMOD_ATTR(update_en, S_IWUGO, NULL, goodix_sysfs_update_en_store), + __EXTMOD_ATTR(progress, S_IRUGO, goodix_sysfs_update_progress_show, NULL), + __EXTMOD_ATTR(result, S_IRUGO, goodix_sysfs_update_result_show, NULL), + __EXTMOD_ATTR(fwversion, S_IRUGO, + goodix_sysfs_update_fwversion_show, NULL), + __EXTMOD_ATTR(fwsize, S_IRUGO | S_IWUGO, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store), + __EXTMOD_ATTR(force_update, S_IWUGO, NULL, + goodix_sysfs_force_update_store), +}; + +static int goodix_fw_sysfs_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; + } + + + ret = 0; + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs) && !ret; i++) + ret = sysfs_create_file(&module->kobj, &goodix_fwu_attrs[i].attr); + if (ret) { + ts_err("failed create fwu sysfs files"); + while (--i >= 0) + sysfs_remove_file(&module->kobj, &goodix_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 = S_IRUGO | S_IWUGO; + fw_ctrl->attr_fwimage.size = 0; + fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; + ret = sysfs_create_bin_file(&module->kobj, + &fw_ctrl->attr_fwimage); + +exit_sysfs_init: + return ret; +} + +static void goodix_fw_sysfs_remove(struct goodix_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(goodix_fwu_attrs); i++) + sysfs_remove_file(&module->kobj, + &goodix_fwu_attrs[i].attr); + + kobject_put(&module->kobj); +} + +static int goodix_do_fw_update(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; + 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; + } else { + /* 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; + } +} + +static int goodix_fw_update_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + int ret = 0; + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + + if (goodix_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(&goodix_fw_update_ctrl.mutex); + core_data->do_fw_update = goodix_do_fw_update; + module->priv_data = &goodix_fw_update_ctrl; + + goodix_fw_update_ctrl.ts_dev = core_data->ts_dev; + goodix_fw_update_ctrl.allow_reset = true; + goodix_fw_update_ctrl.allow_irq = true; + goodix_fw_update_ctrl.allow_suspend = true; + goodix_fw_update_ctrl.allow_resume = true; + goodix_fw_update_ctrl.core_data = core_data; + goodix_fw_update_ctrl.mode = 0; + /* find a valid firmware image name */ + if (ts_bdata && ts_bdata->fw_name) + strlcpy(goodix_fw_update_ctrl.fw_name, ts_bdata->fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + else + strlcpy(goodix_fw_update_ctrl.fw_name, TS_DEFAULT_FIRMWARE, + sizeof(goodix_fw_update_ctrl.fw_name)); + + ret = goodix_fw_sysfs_init(core_data, module); + if (ret) { + ts_err("failed create fwupate sysfs node"); + goto err_out; + } + + goodix_fw_update_ctrl.initialized = 1; +err_out: + mutex_unlock(&goodix_fw_update_ctrl.mutex); + return ret; +} + +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("goodix_fwupdate_module_ini IN"); + mutex_init(&goodix_fw_update_ctrl.mutex); + return goodix_register_ext_module(&goodix_fwu_module); +} + +static void __exit goodix_fwu_module_exit(void) +{ + mutex_lock(&goodix_fw_update_ctrl.mutex); + goodix_unregister_ext_module(&goodix_fwu_module); + if (goodix_fw_update_ctrl.initialized) { + goodix_fw_sysfs_remove(&goodix_fwu_module); + goodix_fw_update_ctrl.initialized = 0; + } + mutex_lock(&goodix_fw_update_ctrl.mutex); +} + +module_init(goodix_fwu_module_init); +module_exit(goodix_fwu_module_exit); + +MODULE_DESCRIPTION("Goodix FWU Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h index d010fe89c115..fc44619b544b 100644 --- a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h @@ -456,6 +456,8 @@ struct goodix_ts_core { #elif defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif + + int (*do_fw_update)(int mode); }; /* external module structures */ @@ -744,13 +746,5 @@ int goodix_ts_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); void goodix_msg_printf(const char *fmt, ...); -#ifndef CONFIG_TOUCHSCREEN_GOODIX_GTX8_UPDATE -static inline int goodix_do_fw_update(int mode) -{ - return -1111; -} -#else -int goodix_do_fw_update(int mode); -#endif #endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c index 71a27317423e..82e6bfb1f700 100644 --- a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c @@ -87,6 +87,8 @@ enum TS_SEND_CFG_REPLY { #define IRQ_HEAD_LEN_YS 8 #define IRQ_HEAD_LEN_NOR 2 + +static struct platform_device *goodix_pdev; int goodix_ts_core_init(void); #ifdef CONFIG_OF /** @@ -1303,6 +1305,7 @@ int goodix_hw_reset(struct goodix_ts_device *dev) */ static int goodix_request_handler(struct goodix_ts_device *dev) { + struct goodix_ts_core *core_data = platform_get_drvdata(goodix_pdev); unsigned char buffer[1]; int r; @@ -1328,7 +1331,10 @@ static int goodix_request_handler(struct goodix_ts_device *dev) break; case REQUEST_RELOADFW: ts_info("HW request reload fw"); - goodix_do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); + if (core_data && core_data->do_fw_update) + core_data->do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); + else + ts_err("fw update handler not ready."); break; case REQUEST_IDLE: ts_info("HW request idle"); @@ -1795,8 +1801,6 @@ static const struct goodix_ts_hw_ops hw_i2c_ops = { .check_hw = goodix_esd_check, }; -static struct platform_device *goodix_pdev; - static void goodix_pdev_release(struct device *dev) { ts_info("goodix pdev released");