diff options
Diffstat (limited to 'drivers/input/touchscreen/gt1151/gt1x_update.c')
-rw-r--r-- | drivers/input/touchscreen/gt1151/gt1x_update.c | 1473 |
1 files changed, 1473 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/gt1151/gt1x_update.c b/drivers/input/touchscreen/gt1151/gt1x_update.c new file mode 100644 index 000000000000..b9dac3440c11 --- /dev/null +++ b/drivers/input/touchscreen/gt1151/gt1x_update.c @@ -0,0 +1,1473 @@ +/* drivers/input/touchscreen/gt1x_update.c +* +* 2010 - 2014 Goodix Technology. +* +* 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. +* +* Version: 1.4 +* Release Date: 2015/07/10 +*/ +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/namei.h> +#include <linux/mount.h> +#include <asm/uaccess.h> + +#include "gt1x_generic.h" +#if (GTP_HOTKNOT || GTP_HEADER_FW_UPDATE) +#include "gt1x_firmware.h" +#endif + +#define UPDATE_FILE_PATH_1 "/data/_goodix_update_.bin" +#define UPDATE_FILE_PATH_2 "/sdcard/_goodix_update_.bin" + +#define CONFIG_FILE_PATH_1 "/data/_gt1x_config_.cfg" +#define CONFIG_FILE_PATH_2 "/sdcard/_gt1x_config_.cfg" + +#define FOUND_FW_PATH_1 0x01 +#define FOUND_FW_PATH_2 0x02 +#define FOUND_CFG_PATH_1 0x04 +#define FOUND_CFG_PATH_2 0x08 + +#define PACK_SIZE 256 + +// hardware register define +#define _bRW_MISCTL__SRAM_BANK 0x4048 +#define _bRW_MISCTL__MEM_CD_EN 0x4049 +#define _bRW_MISCTL__CACHE_EN 0x404B +#define _bRW_MISCTL__TMR0_EN 0x40B0 +#define _rRW_MISCTL__SWRST_B0_ 0x4180 +#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184 +#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190 +#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218 +#define _rRW_MISCTL__BOOT_CTL_ 0x5094 +#define _bRW_MISCTL__DSP_MCU_PWR_ 0x4010 +#define _bRW_MISCTL__PATCH_AREA_EN_ 0x404D + +/* +1. firmware structure + header: 128b + + offset size content + 0 4 firmware length + 4 2 checksum + 6 6 target MASK name + 12 3 target MASK version + 15 6 TP subsystem PID + 21 3 TP subsystem version + 24 1 subsystem count + 25 1 chip type 0x91: GT1X, 0x92: GT2X + 26 6 reserved + 32 8 subsystem info[0] + 32 8 subsystem info[1] + ..... + 120 8 subsystem info[11] + + body: followed header + + 128 N0 subsystem[0] + 128+N0 N1 subsystem[1] + .... + +2. subsystem info structure + offset size content + 0 1 subsystem type + 1 2 subsystem length + 3 2 stored address in flash addr = value * 256 + 5 3 reserved + +*/ + +#define FW_HEAD_SIZE 128 +#define FW_HEAD_SUBSYSTEM_INFO_SIZE 8 +#define FW_HEAD_OFFSET_SUBSYSTEM_INFO_BASE 32 + +#define FW_SECTION_TYPE_SS51_ISP 0x01 +#define FW_SECTION_TYPE_SS51_PATCH 0x02 +#define FW_SECTION_TYPE_SS51_PATCH_OVERLAY 0x03 +#define FW_SECTION_TYPE_DSP 0x04 +#define FW_SECTION_TYPE_HOTKNOT 0x05 +#define FW_SECTION_TYPE_GESTURE 0x06 +#define FW_SECTION_TYPE_GESTURE_OVERLAY 0x07 +#define FW_SECTION_TYPE_FLASHLESS_FAST_POWER 0x08 + +#define UPDATE_TYPE_HEADER 0 +#define UPDATE_TYPE_FILE 1 + +#define UPDATE_STATUS_IDLE 0 +#define UPDATE_STATUS_RUNNING 1 +#define UPDATE_STATUS_ABORT 2 + +struct fw_subsystem_info { + int type; + int length; + u32 address; + int offset; +}; + +#pragma pack(1) +struct fw_info { + u32 length; + u16 checksum; + u8 target_mask[6]; + u8 target_mask_version[3]; + u8 pid[6]; + u8 version[3]; + u8 subsystem_count; + u8 chip_type; + u8 reserved[6]; + struct fw_subsystem_info subsystem[12]; +}; +#pragma pack() + +struct fw_update_info update_info = { + .status = UPDATE_STATUS_IDLE, + .progress = 0, + .max_progress = 9, + .force_update = 0 +}; + +int gt1x_update_prepare(char *filename); +int gt1x_check_firmware(void); +u8 *gt1x_get_fw_data(u32 offset, int length); +int gt1x_update_judge(void); +int gt1x_run_ss51_isp(u8 * ss51_isp, int length); +int gt1x_burn_subsystem(struct fw_subsystem_info *subsystem); +u16 gt1x_calc_checksum(u8 * fw, u32 length); +int gt1x_recall_check(u8 * chk_src, u16 start_rd_addr, u16 chk_length); +void gt1x_update_cleanup(void); +int gt1x_check_subsystem_in_flash(struct fw_subsystem_info *subsystem); +int gt1x_read_flash(u32 addr, int length); +int gt1x_error_erase(void); +void dump_to_file(u16 addr, int length, char *filepath); + +int gt1x_update_firmware(void *filename); +int gt1x_auto_update_proc(void *data); + +#if !GTP_HEADER_FW_UPDATE +static int gt1x_search_update_files(void); +#endif + +int gt1x_hold_ss51_dsp(void); +void gt1x_leave_update_mode(void); + +/** +* @return: return 0 if success, otherwise return a negative number +* which contains the error code. +*/ +s32 gt1x_check_fs_mounted(char *path_name) +{ + struct path root_path; + struct path path; + s32 err; + + err = kern_path("/", LOOKUP_FOLLOW, &root_path); + if (err) + return ERROR_PATH; + + err = kern_path(path_name, LOOKUP_FOLLOW, &path); + if (err) { + err = ERROR_PATH; + goto check_fs_fail; + } + + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) { + // not mounted + err = ERROR_PATH; + } else { + err = 0; + } + + path_put(&path); +check_fs_fail: + path_put(&root_path); + return err; +} + +int gt1x_i2c_write_with_readback(u16 addr, u8 * buffer, int length) +{ + u8 buf[100]; + int ret = gt1x_i2c_write(addr, buffer, length); + if (ret) { + return ret; + } + ret = gt1x_i2c_read(addr, buf, length); + if (ret) { + return ret; + } + if (memcmp(buf, buffer, length)) { + return ERROR_CHECK; + } + return 0; +} + +#define getU32(a) ((u32)getUint((u8 *)(a), 4)) +#define getU16(a) ((u16)getUint((u8 *)(a), 2)) +u32 getUint(u8 * buffer, int len) +{ + u32 num = 0; + int i; + for (i = 0; i < len; i++) { + num <<= 8; + num += buffer[i]; + } + return num; +} + +int gt1x_auto_update_proc(void *data) +{ + +#if GTP_HEADER_FW_UPDATE + GTP_INFO("Start auto update thread..."); + gt1x_update_firmware(NULL); +#else + int ret; + char *filename; + u8 config[GTP_CONFIG_MAX_LENGTH] = { 0 }; + + GTP_INFO("Start auto update thread..."); + ret = gt1x_search_update_files(); + if (ret & (FOUND_FW_PATH_1 | FOUND_FW_PATH_2)) { + if (ret & FOUND_FW_PATH_1) { + filename = UPDATE_FILE_PATH_1; + } else { + filename = UPDATE_FILE_PATH_2; + } + gt1x_update_firmware(filename); + } + + if (ret & (FOUND_CFG_PATH_1 | FOUND_CFG_PATH_2)) { + if (ret & FOUND_CFG_PATH_1) { + filename = CONFIG_FILE_PATH_1; + } else { + filename = CONFIG_FILE_PATH_2; + } + + if (gt1x_parse_config(filename, config) > 0) { + if (gt1x_i2c_write(GTP_REG_CONFIG_DATA, config, GTP_CONFIG_MAX_LENGTH)) { + GTP_ERROR("Update config failed!"); + } else { + GTP_INFO("Update config successfully!"); + } + } + } +#endif + return 0; +} +#if !GTP_HEADER_FW_UPDATE +static int gt1x_search_update_files(void) +{ + int retry = 20 * 2; //wait 10s(max) if fs is not ready + struct file *pfile = NULL; + mm_segment_t old_fs; + int found = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + GTP_INFO("Search firmware file..."); + while (retry-- > 0) { + msleep(500); + + // check if rootfs is ready + if (gt1x_check_fs_mounted("/data")) { + GTP_DEBUG("filesystem is not ready"); + continue; + } + // search firmware + pfile = filp_open(UPDATE_FILE_PATH_1, O_RDONLY, 0); + if (IS_ERR(pfile)) { + pfile = filp_open(UPDATE_FILE_PATH_2, O_RDONLY, 0); + if (!IS_ERR(pfile)) { + found |= FOUND_FW_PATH_2; + } + } else { + found |= FOUND_FW_PATH_1; + } + + if (!IS_ERR(pfile)) { + filp_close(pfile, NULL); + } + // search config file + pfile = filp_open(CONFIG_FILE_PATH_1, O_RDONLY, 0); + if (IS_ERR(pfile)) { + pfile = filp_open(CONFIG_FILE_PATH_2, O_RDONLY, 0); + if (!IS_ERR(pfile)) { + found |= FOUND_CFG_PATH_2; + } + } else { + found |= FOUND_CFG_PATH_1; + } + if (!IS_ERR(pfile)) { + filp_close(pfile, NULL); + } + + if (found) { + break; + } + + GTP_INFO("Not found firmware or config file, retry."); + } + set_fs(old_fs); + + return found; +} +#endif + +void gt1x_enter_update_mode(void) +{ + GTP_DEBUG("Enter FW update mode."); +#if GTP_ESD_PROTECT + gt1x_esd_switch(SWITCH_OFF); +#endif +#if GTP_CHARGER_SWITCH + gt1x_charger_switch(SWITCH_OFF); +#endif + gt1x_irq_disable(); + gt1x_irq_free(); +} + +int gt1x_update_firmware(void *filename) +{ + int i = 0; + int ret = 0; + u8 *p; + + if (update_info.status != UPDATE_STATUS_IDLE) { + GTP_ERROR("Update process is running!"); + return ERROR; + } + update_info.status = UPDATE_STATUS_RUNNING; + update_info.progress = 0; + + gt1x_enter_update_mode(); + + ret = gt1x_update_prepare(filename); + if (ret) { + update_info.status = UPDATE_STATUS_IDLE; + return ret; + } + + ret = gt1x_check_firmware(); + if (ret) { + update_info.status = UPDATE_STATUS_ABORT; + goto gt1x_update_exit; + } +#if GTP_FW_UPDATE_VERIFY + update_info.max_progress = + 6 + update_info.firmware->subsystem_count; +#else + update_info.max_progress = + 3 + update_info.firmware->subsystem_count; +#endif + update_info.progress++; // 1 + + ret = gt1x_update_judge(); + if (ret) { + update_info.status = UPDATE_STATUS_ABORT; + goto gt1x_update_exit; + } + update_info.progress++; // 2 + + p = gt1x_get_fw_data(update_info.firmware->subsystem[0].offset, update_info.firmware->subsystem[0].length); + if (p == NULL) { + GTP_ERROR("get isp fail"); + ret = ERROR_FW; + update_info.status = UPDATE_STATUS_ABORT; + goto gt1x_update_exit; + } + update_info.progress++; // 3 + + ret = gt1x_run_ss51_isp(p, update_info.firmware->subsystem[0].length); + if (ret) { + GTP_ERROR("run isp fail"); + goto gt1x_update_exit; + } + update_info.progress++; // 4 + msleep(800); + + for (i = 1; i < update_info.firmware->subsystem_count; i++) { + GTP_INFO("subsystem: %d", update_info.firmware->subsystem[i].type); + GTP_INFO("Length: %d", update_info.firmware->subsystem[i].length); + GTP_INFO("Address: %d", update_info.firmware->subsystem[i].address); + + ret = gt1x_burn_subsystem(&(update_info.firmware->subsystem[i])); + if (ret) { + GTP_ERROR("burn subsystem fail!"); + goto gt1x_update_exit; + } + update_info.progress++; + } + +#if GTP_FW_UPDATE_VERIFY + gt1x_reset_guitar(); + + p = gt1x_get_fw_data(update_info.firmware->subsystem[0].offset, update_info.firmware->subsystem[0].length); + if (p == NULL) { + GTP_ERROR("get isp fail"); + ret = ERROR_FW; + goto gt1x_update_exit; + } + update_info.progress++; + + ret = gt1x_run_ss51_isp(p, update_info.firmware->subsystem[0].length); + if (ret) { + GTP_ERROR("run isp fail"); + goto gt1x_update_exit; + } + update_info.progress++; + + GTP_INFO("Reset guitar & check firmware in flash."); + for (i = 1; i < update_info.firmware->subsystem_count; i++) { + GTP_INFO("subsystem: %d", update_info.firmware->subsystem[i].type); + GTP_INFO("Length: %d", update_info.firmware->subsystem[i].length); + GTP_INFO("Address: %d", update_info.firmware->subsystem[i].address); + + ret = gt1x_check_subsystem_in_flash(&(update_info.firmware->subsystem[i])); + if (ret) { + gt1x_error_erase(); + break; + } + } + update_info.progress++; +#endif + +gt1x_update_exit: + gt1x_update_cleanup(); + gt1x_leave_update_mode(); + gt1x_read_version(NULL); + if (ret) { + update_info.progress = 2 * update_info.max_progress; + GTP_ERROR("Update firmware failed!"); + return ret; + } else if (gt1x_init_failed) { + gt1x_read_version(>1x_version); + gt1x_init_panel(); + #if GTP_CHARGER_SWITCH + gt1x_parse_chr_cfg(gt1x_version.sensor_id); + #endif + #if GTP_SMART_COVER + gt1x_parse_sc_cfg(gt1x_version.sensor_id); + #endif + } + GTP_INFO("Update firmware succeefully!"); + return ret; +} + +int gt1x_update_prepare(char *filename) +{ + int ret = 0; + int retry = 5; + if (filename == NULL) { +#if GTP_HEADER_FW_UPDATE + update_info.fw_name = NULL; + update_info.update_type = UPDATE_TYPE_HEADER; + update_info.fw_data = gt1x_default_FW; + update_info.fw_length = sizeof(gt1x_default_FW); +#else + GTP_ERROR("No Fw in .h file!"); + return ERROR_FW; +#endif + } else { + GTP_INFO("Firmware: %s", filename); + update_info.old_fs = get_fs(); + set_fs(KERNEL_DS); + update_info.fw_name = filename; + update_info.update_type = UPDATE_TYPE_FILE; + update_info.fw_file = filp_open(update_info.fw_name, O_RDONLY, 0); + if (IS_ERR(update_info.fw_file)) { + GTP_ERROR("Open update file(%s) error!", update_info.fw_name); + set_fs(update_info.old_fs); + return ERROR_FILE; + } + update_info.fw_file->f_op->llseek(update_info.fw_file, 0, SEEK_SET); + update_info.fw_length = update_info.fw_file->f_op->llseek(update_info.fw_file, 0, SEEK_END); + } + + while (retry > 0) { + retry--; + update_info.firmware = (struct fw_info *)kzalloc(sizeof(struct fw_info), GFP_KERNEL); + if (update_info.firmware == NULL) { + GTP_INFO("Alloc %zu bytes memory fail.", sizeof(struct fw_info)); + continue; + } else { + GTP_INFO("Alloc %zu bytes memory success.", sizeof(struct fw_info)); + break; + } + } + if (retry <= 0) { + ret = ERROR_RETRY; + goto gt1x_update_pre_fail1; + } + + retry = 5; + while (retry > 0) { + update_info.buffer = (u8 *) kzalloc(1024 * 4, GFP_KERNEL); + if (update_info.buffer == NULL) { + GTP_ERROR("Alloc %d bytes memory fail.", 1024 * 4); + continue; + } else { + GTP_INFO("Alloc %d bytes memory success.", 1024 * 4); + break; + } + } + if (retry <= 0) { + ret = ERROR_RETRY; + goto gt1x_update_pre_fail0; + } + + return 0; + +gt1x_update_pre_fail0: + kfree(update_info.firmware); +gt1x_update_pre_fail1: + filp_close(update_info.fw_file, NULL); + return ret; +} + +void gt1x_update_cleanup(void) +{ + if (update_info.update_type == UPDATE_TYPE_FILE) { + if (update_info.fw_file != NULL) { + filp_close(update_info.fw_file, NULL); + update_info.fw_file = NULL; + } + set_fs(update_info.old_fs); + } + + if (update_info.buffer != NULL) { + kfree(update_info.buffer); + update_info.buffer = NULL; + } + if (update_info.firmware != NULL) { + kfree(update_info.firmware); + update_info.firmware = NULL; + } +} + +int gt1x_check_firmware(void) +{ + u16 checksum; + u16 checksum_in_header; + u8 *p; + struct fw_info *firmware; + int i; + int offset; + + // compare file length with the length field in the firmware header + if (update_info.fw_length < FW_HEAD_SIZE) { + GTP_ERROR("Bad firmware!(file length: %d)", update_info.fw_length); + return ERROR_CHECK; + } + p = gt1x_get_fw_data(0, 6); + if (p == NULL) { + return ERROR_FW; + } + + if (getU32(p) + 6 != update_info.fw_length) { + GTP_ERROR("Bad firmware!(file length: %d, header define: %d)", update_info.fw_length, getU32(p)); + return ERROR_CHECK; + } + // check firmware's checksum + checksum_in_header = getU16(&p[4]); + checksum = 0; + for (i = 6; i < update_info.fw_length; i++) { + p = gt1x_get_fw_data(i, 1); + if (p == NULL) { + return ERROR_FW; + } + checksum += p[0]; + } + + if (checksum != checksum_in_header) { + GTP_ERROR("Bad firmware!(checksum: 0x%04X, header define: 0x%04X)", checksum, checksum_in_header); + return ERROR_CHECK; + } + // parse firmware + p = gt1x_get_fw_data(0, FW_HEAD_SIZE); + if (p == NULL) { + return ERROR_FW; + } + memcpy((u8 *) update_info.firmware, p, FW_HEAD_SIZE - 8 * 12); + update_info.firmware->pid[5] = 0; + + p = &p[FW_HEAD_OFFSET_SUBSYSTEM_INFO_BASE]; + firmware = update_info.firmware; + offset = FW_HEAD_SIZE; + for (i = 0; i < firmware->subsystem_count; i++) { + firmware->subsystem[i].type = p[i * FW_HEAD_SUBSYSTEM_INFO_SIZE]; + firmware->subsystem[i].length = getU16(&p[i * FW_HEAD_SUBSYSTEM_INFO_SIZE + 1]); + firmware->subsystem[i].address = getU16(&p[i * FW_HEAD_SUBSYSTEM_INFO_SIZE + 3]) * 256; + firmware->subsystem[i].offset = offset; + offset += firmware->subsystem[i].length; + } + + // print update information + GTP_INFO("Update type: %s", update_info.update_type == UPDATE_TYPE_HEADER ? "Header" : "File"); + GTP_INFO("Firmware length: %d", update_info.fw_length); + GTP_INFO("Firmware product: GT%s", update_info.firmware->pid); + GTP_INFO("Firmware patch: %02X%02X%02X", update_info.firmware->version[0], update_info.firmware->version[1], update_info.firmware->version[2]); + GTP_INFO("Firmware chip: 0x%02X", update_info.firmware->chip_type); + GTP_INFO("Subsystem count: %d", update_info.firmware->subsystem_count); + for (i = 0; i < update_info.firmware->subsystem_count; i++) { + GTP_DEBUG("------------------------------------------"); + GTP_DEBUG("Subsystem: %d", i); + GTP_DEBUG("Type: %d", update_info.firmware->subsystem[i].type); + GTP_DEBUG("Length: %d", update_info.firmware->subsystem[i].length); + GTP_DEBUG("Address: 0x%08X", update_info.firmware->subsystem[i].address); + GTP_DEBUG("Offset: %d", update_info.firmware->subsystem[i].offset); + } + + return 0; +} + +/** +* @return: return a pointer pointed at the content of firmware +* if success, otherwise return NULL. +*/ +u8 *gt1x_get_fw_data(u32 offset, int length) +{ + int ret; + if (update_info.update_type == UPDATE_TYPE_FILE) { + update_info.fw_file->f_op->llseek(update_info.fw_file, offset, SEEK_SET); + ret = update_info.fw_file->f_op->read(update_info.fw_file, (char *)update_info.buffer, length, &update_info.fw_file->f_pos); + if (ret < 0) { + GTP_ERROR("Read data error!"); + return NULL; + } + return update_info.buffer; + } else { + return &update_info.fw_data[offset]; + } +} + +int gt1x_update_judge(void) +{ + int ret; + u8 reg_val[2] = {0}; + u8 retry = 2; + struct gt1x_version_info ver_info; + struct gt1x_version_info fw_ver_info; + + fw_ver_info.mask_id = (update_info.firmware->target_mask_version[0] << 16) + | (update_info.firmware->target_mask_version[1] << 8) + | (update_info.firmware->target_mask_version[2]); + fw_ver_info.patch_id = (update_info.firmware->version[0] << 16) + | (update_info.firmware->version[1] << 8) + | (update_info.firmware->version[2]); + memcpy(fw_ver_info.product_id, update_info.firmware->pid, 4); + fw_ver_info.product_id[4] = 0; + + /* check fw status reg */ + do { + ret = gt1x_i2c_read_dbl_check(GTP_REG_FW_CHK_MAINSYS, reg_val, 1); + if (ret < 0) { /* read reg failed */ + goto _reset; + } else if (ret > 0) { + continue; + } + + ret = gt1x_i2c_read_dbl_check(GTP_REG_FW_CHK_SUBSYS, ®_val[1], 1); + if (ret < 0) { + goto _reset; + } else if (ret > 0) { + continue; + } + + break; +_reset: + gt1x_reset_guitar(); + }while (--retry); + + if (!retry) { + GTP_INFO("Update abort because of i2c error."); + return ERROR_CHECK; + } + if (reg_val[0] != 0xBE || reg_val[1] == 0xAA) { + GTP_INFO("Check fw status reg not pass,reg[0x814E]=0x%2X,reg[0x5095]=0x%2X!", + reg_val[0], reg_val[1]); + return 0; + } + + ret = gt1x_read_version(&ver_info); + if (ret < 0) { + GTP_INFO("Get IC's version info failed, force update!"); + return 0; + } + + if (memcmp(fw_ver_info.product_id, ver_info.product_id, 4)) { + GTP_INFO("Product id is not match!"); + return ERROR_CHECK; + } + if ((fw_ver_info.mask_id & 0xFFFFFF00) != (ver_info.mask_id & 0xFFFFFF00)) { + GTP_INFO("Mask id is not match!"); + return ERROR_CHECK; + } + if ((fw_ver_info.patch_id & 0xFF0000) != (ver_info.patch_id & 0xFF0000)){ + GTP_INFO("CID is not equal, need update!"); + return 0; + } +#if GTP_DEBUG_ON + if (update_info.force_update) { + GTP_DEBUG("Debug mode, force update fw."); + return 0; + } +#endif + if ((fw_ver_info.patch_id & 0xFFFF) <= (ver_info.patch_id & 0xFFFF)) { + GTP_INFO("The version of the fw is not high than the IC's!"); + return ERROR_CHECK; + } + return 0; +} + +int __gt1x_hold_ss51_dsp_20(void) +{ + int ret = -1; + int retry = 0; + u8 buf[1]; + int hold_times = 0; + + while (retry++ < 30) { + + // Hold ss51 & dsp + buf[0] = 0x0C; + ret = gt1x_i2c_write(_rRW_MISCTL__SWRST_B0_, buf, 1); + if (ret) { + GTP_ERROR("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + // Confirm hold + buf[0] = 0x00; + ret = gt1x_i2c_read(_rRW_MISCTL__SWRST_B0_, buf, 1); + if (ret) { + GTP_ERROR("Hold ss51 & dsp I2C error,retry:%d", retry); + continue; + } + if (0x0C == buf[0]) { + if (hold_times++ < 20) { + continue; + } else { + break; + } + } + GTP_ERROR("Hold ss51 & dsp confirm 0x4180 failed,value:%d", buf[0]); + } + if (retry >= 30) { + GTP_ERROR("Hold ss51&dsp failed!"); + return ERROR_RETRY; + } + + GTP_INFO("Hold ss51&dsp successfully."); + return 0; +} + +int gt1x_hold_ss51_dsp(void) +{ + int ret = ERROR, retry = 5; + u8 buffer[2]; + + do { + gt1x_select_addr(); + ret = gt1x_i2c_read(0x4220, buffer, 1); + + } while (retry-- && ret < 0); + + if (ret < 0) + return ERROR; + + //hold ss51_dsp + ret = __gt1x_hold_ss51_dsp_20(); + if (ret) { + return ret; + } + // enable dsp & mcu power + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__DSP_MCU_PWR_, buffer, 1); + if (ret) { + GTP_ERROR("enabel dsp & mcu power fail!"); + return ret; + } + // disable watchdog + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__TMR0_EN, buffer, 1); + if (ret) { + GTP_ERROR("disable wdt fail!"); + return ret; + } + // clear cache + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__CACHE_EN, buffer, 1); + if (ret) { + GTP_ERROR("clear cache fail!"); + return ret; + } + // soft reset + buffer[0] = 0x01; + ret = gt1x_i2c_write(_bWO_MISCTL__CPU_SWRST_PULSE, buffer, 1); + if (ret) { + GTP_ERROR("software reset fail!"); + return ret; + } + // set scramble + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_rRW_MISCTL__BOOT_OPT_B0_, buffer, 1); + if (ret) { + GTP_ERROR("set scramble fail!"); + return ret; + } + + return 0; +} + +int gt1x_run_ss51_isp(u8 * ss51_isp, int length) +{ + int ret; + u8 buffer[10]; + + ret = gt1x_hold_ss51_dsp(); + if (ret) { + return ret; + } + // select bank4 + buffer[0] = 0x04; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__SRAM_BANK, buffer, 1); + if (ret) { + GTP_ERROR("select bank4 fail."); + return ret; + } + // enable patch area access + buffer[0] = 0x01; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__PATCH_AREA_EN_, buffer, 1); + if (ret) { + GTP_ERROR("enable patch area access fail!"); + return ret; + } + + GTP_INFO("ss51_isp length: %d, checksum: 0x%04X", length, gt1x_calc_checksum(ss51_isp, length)); + // load ss51 isp + ret = gt1x_i2c_write(0xC000, ss51_isp, length); + if (ret) { + GTP_ERROR("load ss51 isp fail!"); + return ret; + } + // recall compare + ret = gt1x_recall_check(ss51_isp, 0xC000, length); + if (ret) { + GTP_ERROR("recall check ss51 isp fail!"); + return ret; + } + + memset(buffer, 0xAA, 10); + ret = gt1x_i2c_write_with_readback(0x8140, buffer, 10); + + // disable patch area access + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__PATCH_AREA_EN_, buffer, 1); + if (ret) { + GTP_ERROR("disable patch area access fail!"); + return ret; + } + // set 0x8006 + memset(buffer, 0x55, 8); + ret = gt1x_i2c_write_with_readback(0x8006, buffer, 8); + if (ret) { + GTP_ERROR("set 0x8006[0~7] 0x55 fail!"); + return ret; + } + // release ss51 + buffer[0] = 0x08; + ret = gt1x_i2c_write_with_readback(_rRW_MISCTL__SWRST_B0_, buffer, 1); + if (ret) { + GTP_ERROR("release ss51 fail!"); + return ret; + } + + msleep(100); + // check run state + ret = gt1x_i2c_read(0x8006, buffer, 2); + if (ret) { + GTP_ERROR("read 0x8006 fail!"); + return ret; + } + if (!(buffer[0] == 0xAA && buffer[1] == 0xBB)) { + GTP_ERROR("ERROR: isp is not running! 0x8006: %02X %02X", buffer[0], buffer[1]); + return ERROR_CHECK; + } + + return 0; +} + +u16 gt1x_calc_checksum(u8 * fw, u32 length) +{ + u32 i = 0; + u32 checksum = 0; + + for (i = 0; i < length; i += 2) { + checksum += (((int)fw[i]) << 8); + checksum += fw[i + 1]; + } + return (checksum & 0xFFFF); +} + +int gt1x_recall_check(u8 * chk_src, u16 start_addr, u16 chk_length) +{ + u8 rd_buf[PACK_SIZE]; + s32 ret = 0; + u16 len = 0; + u32 compared_length = 0; + + while (chk_length > 0) { + len = (chk_length > PACK_SIZE ? PACK_SIZE : chk_length); + + ret = gt1x_i2c_read(start_addr + compared_length, rd_buf, len); + if (ret) { + GTP_ERROR("recall i2c error,exit!"); + return ret; + } + + if (memcmp(rd_buf, &chk_src[compared_length], len)) { + GTP_ERROR("Recall frame not equal(addr: 0x%04X)", start_addr + compared_length); + GTP_DEBUG("chk_src array:"); + GTP_DEBUG_ARRAY(&chk_src[compared_length], len); + GTP_DEBUG("recall array:"); + GTP_DEBUG_ARRAY(rd_buf, len); + return ERROR_CHECK; + } + + chk_length -= len; + compared_length += len; + } + + GTP_DEBUG("Recall check %d bytes(address: 0x%04X) success.", compared_length, start_addr); + return 0; +} + +int gt1x_burn_subsystem(struct fw_subsystem_info *subsystem) +{ + int block_len; + u16 checksum; + int burn_len = 0; + u16 cur_addr; + u32 length = subsystem->length; + u8 buffer[10]; + int ret; + int wait_time; + int burn_state; + int retry = 5; + u8 *fw; + + GTP_INFO("Subsystem: %d", subsystem->type); + GTP_INFO("Length: %d", subsystem->length); + GTP_INFO("Address: 0x%08X", subsystem->address); + + while (length > 0 && retry > 0) { + retry--; + + block_len = length > 1024 * 4 ? 1024 * 4 : length; + + GTP_INFO("Burn block ==> length: %d, address: 0x%08X", block_len, subsystem->address + burn_len); + fw = gt1x_get_fw_data(subsystem->offset + burn_len, block_len); + if (fw == NULL) { + return ERROR_FW; + } + + cur_addr = ((subsystem->address + burn_len) >> 8); + + checksum = 0; + checksum += block_len; + checksum += cur_addr; + checksum += gt1x_calc_checksum(fw, block_len); + checksum = (0 - checksum); + + buffer[0] = ((block_len >> 8) & 0xFF); + buffer[1] = (block_len & 0xFF); + buffer[2] = ((cur_addr >> 8) & 0xFF); + buffer[3] = (cur_addr & 0xFF); + + ret = gt1x_i2c_write_with_readback(0x8100, buffer, 4); + if (ret) { + GTP_ERROR("write length & address fail!"); + continue; + } + + ret = gt1x_i2c_write(0x8100 + 4, fw, block_len); + if (ret) { + GTP_ERROR("write fw data fail!"); + continue; + } + + buffer[0] = ((checksum >> 8) & 0xFF); + buffer[1] = (checksum & 0xFF); + ret = gt1x_i2c_write_with_readback(0x8100 + 4 + block_len, buffer, 2); + if (ret) { + GTP_ERROR("write checksum fail!"); + continue; + } + + buffer[0] = 0; + ret = gt1x_i2c_write_with_readback(0x8022, buffer, 1); + if (ret) { + GTP_ERROR("clear control flag fail!"); + continue; + } + + buffer[0] = subsystem->type; + buffer[1] = subsystem->type; + ret = gt1x_i2c_write_with_readback(0x8020, buffer, 2); + if (ret) { + GTP_ERROR("write subsystem type fail!"); + continue; + } + burn_state = ERROR; + wait_time = 200; + msleep(5); + + while (wait_time-- > 0) { + u8 confirm = 0x55; + + ret = gt1x_i2c_read(0x8022, buffer, 1); + if (ret < 0) { + continue; + } + msleep(5); + ret = gt1x_i2c_read(0x8022, &confirm, 1); + if (ret < 0) { + continue; + } + if (buffer[0] != confirm) { + continue; + } + + if (buffer[0] == 0xAA) { + GTP_DEBUG("burning....."); + continue; + } else if (buffer[0] == 0xDD) { + GTP_ERROR("checksum error!"); + break; + } else if (buffer[0] == 0xBB) { + GTP_INFO("burning success."); + burn_state = 0; + break; + } else if (buffer[0] == 0xCC) { + GTP_ERROR("burning failed!"); + break; + } else { + GTP_DEBUG("unknown state!(0x8022: 0x%02X)", buffer[0]); + } + } + + if (!burn_state) { + length -= block_len; + burn_len += block_len; + retry = 5; + } + } + if (length == 0) { + return 0; + } else { + return ERROR_RETRY; + } +} + +int gt1x_check_subsystem_in_flash(struct fw_subsystem_info *subsystem) +{ + int block_len; + int checked_len = 0; + u32 length = subsystem->length; + int ret; + int check_state = 0; + int retry = 5; + u8 *fw; + + GTP_INFO("Subsystem: %d", subsystem->type); + GTP_INFO("Length: %d", subsystem->length); + GTP_INFO("Address: 0x%08X", subsystem->address); + + while (length > 0) { + block_len = length > 1024 * 4 ? 1024 * 4 : length; + + GTP_INFO("Check block ==> length: %d, address: 0x%08X", block_len, subsystem->address + checked_len); + fw = gt1x_get_fw_data(subsystem->offset + checked_len, block_len); + if (fw == NULL) { + return ERROR_FW; + } + ret = gt1x_read_flash(subsystem->address + checked_len, block_len); + if (ret) { + check_state |= ret; + } + + ret = gt1x_recall_check(fw, 0x8100, block_len); + if (ret) { + GTP_ERROR("Block in flash is broken!"); + check_state |= ret; + } + + length -= block_len; + checked_len += block_len; + retry = 5; + } + if (check_state) { + GTP_ERROR("Subsystem in flash is broken!"); + } else { + GTP_INFO("Subsystem in flash is correct!"); + } + return check_state; +} + +int gt1x_read_flash(u32 addr, int length) +{ + int wait_time; + int ret = 0; + u8 buffer[4]; + u16 read_addr = (addr >> 8); + + GTP_INFO("Read flash: 0x%04X, length: %d", addr, length); + + buffer[0] = 0; + ret = gt1x_i2c_write_with_readback(0x8022, buffer, 1); + + buffer[0] = ((length >> 8) & 0xFF); + buffer[1] = (length & 0xFF); + buffer[2] = ((read_addr >> 8) & 0xFF); + buffer[3] = (read_addr & 0xFF); + ret |= gt1x_i2c_write_with_readback(0x8100, buffer, 4); + + buffer[0] = 0xAA; + buffer[1] = 0xAA; + ret |= gt1x_i2c_write(0x8020, buffer, 2); + if (ret) { + GTP_ERROR("Error occured."); //comment + return ret; + } + + wait_time = 200; + while (wait_time > 0) { + wait_time--; + msleep(5); + ret = gt1x_i2c_read_dbl_check(0x8022, buffer, 1); + if (ret) { + continue; + } + if (buffer[0] == 0xBB) { + GTP_INFO("Read success(addr: 0x%04X, length: %d)", addr, length); + break; + } + } + if (wait_time == 0) { + GTP_ERROR("Read Flash FAIL!"); + return ERROR_RETRY; + } + return 0; +} + + +int gt1x_error_erase(void) +{ + int block_len; + u16 checksum; + u16 erase_addr; + u8 buffer[10]; + int ret; + int wait_time; + int burn_state = ERROR; + int retry = 5; + u8 *fw = NULL; + + GTP_INFO("Erase flash area of ss51."); + + gt1x_reset_guitar(); + + fw = gt1x_get_fw_data(update_info.firmware->subsystem[0].offset, + update_info.firmware->subsystem[0].length); + if (fw == NULL) { + GTP_ERROR("get isp fail"); + return ERROR_FW; + } + ret = gt1x_run_ss51_isp(fw, update_info.firmware->subsystem[0].length); + if (ret) { + GTP_ERROR("run isp fail"); + return ERROR_PATH; + } + + fw = kmalloc(1024 * 4, GFP_KERNEL); + if (!fw) { + GTP_ERROR("error when alloc mem."); + return ERROR_MEM; + } + + memset(fw, 0xFF, 1024 * 4); + erase_addr = 0x00; + block_len = 1024 * 4; + + while (retry-- > 0) { + + checksum = 0; + checksum += block_len; + checksum += erase_addr; + checksum += gt1x_calc_checksum(fw, block_len); + checksum = (0 - checksum); + + buffer[0] = ((block_len >> 8) & 0xFF); + buffer[1] = (block_len & 0xFF); + buffer[2] = ((erase_addr >> 8) & 0xFF); + buffer[3] = (erase_addr & 0xFF); + + ret = gt1x_i2c_write_with_readback(0x8100, buffer, 4); + if (ret) { + GTP_ERROR("write length & address fail!"); + continue; + } + + ret = gt1x_i2c_write(0x8100 + 4, fw, block_len); + if (ret) { + GTP_ERROR("write fw data fail!"); + continue; + } + + ret = gt1x_recall_check(fw, 0x8100 + 4, block_len); + if (ret) { + continue; + } + + buffer[0] = ((checksum >> 8) & 0xFF); + buffer[1] = (checksum & 0xFF); + ret = gt1x_i2c_write_with_readback(0x8100 + 4 + block_len, buffer, 2); + if (ret) { + GTP_ERROR("write checksum fail!"); + continue; + } + + buffer[0] = 0; + ret = gt1x_i2c_write_with_readback(0x8022, buffer, 1); + if (ret) { + GTP_ERROR("clear control flag fail!"); + continue; + } + + buffer[0] = FW_SECTION_TYPE_SS51_PATCH; + buffer[1] = FW_SECTION_TYPE_SS51_PATCH; + ret = gt1x_i2c_write_with_readback(0x8020, buffer, 2); + if (ret) { + GTP_ERROR("write subsystem type fail!"); + continue; + } + burn_state = ERROR; + wait_time = 200; + while (wait_time > 0) { + wait_time--; + msleep(5); + ret = gt1x_i2c_read_dbl_check(0x8022, buffer, 1); + if (ret) { + continue; + } + + if (buffer[0] == 0xAA) { + GTP_DEBUG("burning....."); + continue; + } else if (buffer[0] == 0xDD) { + GTP_ERROR("checksum error!"); + break; + } else if (buffer[0] == 0xBB) { + GTP_INFO("burning success."); + burn_state = 0; + break; + } else if (buffer[0] == 0xCC) { + GTP_ERROR("burning failed!"); + break; + } else { + GTP_DEBUG("unknown state!(0x8022: 0x%02X)", buffer[0]); + } + } + } + + kfree(fw); + if (burn_state == 0) { + return 0; + } else { + return ERROR_RETRY; + } +} + +void gt1x_leave_update_mode(void) +{ + GTP_DEBUG("Leave FW update mode."); + if (update_info.status != UPDATE_STATUS_ABORT) + gt1x_reset_guitar(); + +#if GTP_CHARGER_SWITCH + gt1x_charger_switch(SWITCH_ON); +#endif +#if GTP_ESD_PROTECT + gt1x_esd_switch(SWITCH_ON); +#endif + + update_info.status = UPDATE_STATUS_IDLE; + gt1x_irq_request(); +} + +void dump_to_file(u16 addr, int length, char *filepath) +{ + struct file *flp = NULL; + u8 buf[128]; + const int READ_BLOCK_SIZE = 128; + int read_length = 0; + int len = 0; + + GTP_INFO("Dump(0x%04X, %d bytes) to file: %s\n", addr, length, filepath); + flp = filp_open(filepath, O_RDWR | O_CREAT, 0666); + if (IS_ERR(flp)) { + GTP_ERROR("can not open file: %s\n", filepath); + return; + } + flp->f_op->llseek(flp, 0, SEEK_SET); + + while (length > 0) { + len = (length > READ_BLOCK_SIZE ? READ_BLOCK_SIZE : length); + memset(buf, 0x33, len); + if (gt1x_i2c_read(addr + read_length, buf, len)) { + memset(buf, 0x33, len); + } + flp->f_op->write(flp, (char *)buf, len, &flp->f_pos); + read_length += len; + length -= len; + } + filp_close(flp, NULL); +} + +int gt1x_hold_ss51_dsp_no_reset(void) +{ + int ret = ERROR; + u8 buffer[2]; + + //hold ss51_dsp + ret = __gt1x_hold_ss51_dsp_20(); + if (ret) { + return ret; + } + // enable dsp & mcu power + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__DSP_MCU_PWR_, buffer, 1); + if (ret) { + GTP_ERROR("enabel dsp & mcu power fail!"); + return ret; + } + // disable watchdog + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__TMR0_EN, buffer, 1); + if (ret) { + GTP_ERROR("disable wdt fail!"); + return ret; + } + // clear cache + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__CACHE_EN, buffer, 1); + if (ret) { + GTP_ERROR("clear cache fail!"); + return ret; + } + // soft reset + buffer[0] = 0x01; + ret = gt1x_i2c_write(_bWO_MISCTL__CPU_SWRST_PULSE, buffer, 1); + if (ret) { + GTP_ERROR("software reset fail!"); + return ret; + } + // set scramble + buffer[0] = 0x00; + ret = gt1x_i2c_write_with_readback(_rRW_MISCTL__BOOT_OPT_B0_, buffer, 1); + if (ret) { + GTP_ERROR("set scramble fail!"); + return ret; + } + + return 0; +} + +#define GT1X_LOAD_PACKET_SIZE (1024 * 2) + +int gt1x_load_patch(u8 * patch, u32 patch_size, int offset, int bank_size) +{ + s32 loaded_length = 0; + s32 len = 0; + s32 ret = 0; + u8 bank = 0, tmp; + u16 address; + + GTP_INFO("Load patch code(size: %d, checksum: 0x%04X, position: 0x%04X, bank-size: %d", patch_size, gt1x_calc_checksum(patch, patch_size), 0xC000 + offset, bank_size); + while (loaded_length != patch_size) { + if (loaded_length == 0 || (loaded_length + offset) % bank_size == 0) { + // select bank + bank = 0x04 + (loaded_length + offset) / bank_size; + ret = gt1x_i2c_write(_bRW_MISCTL__SRAM_BANK, &bank, 1); + if (ret) { + GTP_ERROR("select bank%d fail!", bank); + return ret; + } + GTP_INFO("Select bank%d success.", bank); + // enable patch area access + tmp = 0x01; + ret = gt1x_i2c_write_with_readback(_bRW_MISCTL__PATCH_AREA_EN_ + bank - 4, &tmp, 1); + if (ret) { + GTP_ERROR("enable patch area access fail!"); + return ret; + } + } + + len = patch_size - loaded_length > GT1X_LOAD_PACKET_SIZE ? GT1X_LOAD_PACKET_SIZE : patch_size - loaded_length; + address = 0xC000 + (loaded_length + offset) % bank_size; + + ret = gt1x_i2c_write(address, &patch[loaded_length], len); + if (ret) { + GTP_ERROR("load 0x%04X, %dbytes fail!", address, len); + return ret; + } + ret = gt1x_recall_check(&patch[loaded_length], address, len); + if (ret) { + GTP_ERROR("Recall check 0x%04X, %dbytes fail!", address, len); + return ret; + } + GTP_INFO("load code 0x%04X, %dbytes success.", address, len); + + loaded_length += len; + } + + return 0; +} + +int gt1x_startup_patch(void) +{ + s32 ret = 0; + u8 buffer[8] = { 0x55 }; + buffer[0] = 0x00; + buffer[1] = 0x00; + ret |= gt1x_i2c_write(_bRW_MISCTL__PATCH_AREA_EN_, buffer, 2); + + memset(buffer, 0x55, 8); + ret |= gt1x_i2c_write(GTP_REG_FLASH_PASSBY, buffer, 8); + ret |= gt1x_i2c_write(GTP_REG_VERSION, buffer, 5); + + buffer[0] = 0xAA; + ret |= gt1x_i2c_write(GTP_REG_CMD, buffer, 1); + ret |= gt1x_i2c_write(GTP_REG_ESD_CHECK, buffer, 1); + + buffer[0] = 0x00; + ret |= gt1x_i2c_write(_rRW_MISCTL__SWRST_B0_, buffer, 1); + + msleep(200); + + return ret; +} |