| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2014 Goodix Technology. |
| */ |
| |
| #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 <linux/uaccess.h> |
| #include "gt1x_tpd_common.h" |
| #ifdef GTP_REQUEST_FW_UPDATE |
| #include <linux/firmware.h> |
| #endif |
| |
| #ifdef CONFIG_GTP_HEADER_FW_UPDATE |
| #include "gt1x_firmware.h" |
| #endif |
| |
| #ifdef GTP_REQUEST_FW_UPDATE |
| #define GT1151_DEFAULT_FW "gt1151_default_" |
| #endif |
| #undef CONFIG_GTP_FOPEN_FW_UPDATE |
| |
| #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 |
| |
| 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 |
| }; |
| |
| static int flag_need_update; |
| |
| /** |
| * @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) |
| 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] = {0}; |
| 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) |
| { |
| |
| #ifdef CONFIG_GTP_HEADER_FW_UPDATE |
| GTP_INFO("Start auto update thread 0..."); |
| gt1x_update_firmware(NULL); |
| #elif defined(GTP_REQUEST_FW_UPDATE) |
| GTP_INFO("Start auto update thread 1..."); |
| gt1x_update_firmware(NULL); |
| #endif |
| gt1x_auto_update_done(); |
| return 0; |
| } |
| |
| |
| void gt1x_enter_update_mode(void) |
| { |
| #ifdef CONFIG_GTP_ESD_PROTECT |
| gt1x_esd_switch(SWITCH_OFF); |
| #endif |
| gt1x_irq_disable(); |
| } |
| |
| int gt1x_update_prepare(char *filename) |
| { |
| int ret = 0; |
| int retry = 5; |
| #ifdef GTP_REQUEST_FW_UPDATE |
| const struct firmware *fw_entry; |
| char buf[64] = {0}; |
| #endif |
| |
| if (filename == NULL) { |
| #ifdef GTP_REQUEST_FW_UPDATE |
| update_info.fw_name = NULL; |
| update_info.update_type = UPDATE_TYPE_HEADER; |
| |
| ret = sprintf(buf, "%s%s.img", |
| GT1151_DEFAULT_FW, |
| GT1151_FIRMWARE); |
| if (ret > 0) |
| GTP_INFO("Request default firmware version: %s\n", buf); |
| |
| ret = request_firmware(&fw_entry, buf, >1x_i2c_client->dev); |
| if (ret) { |
| GTP_ERROR("load %s fail, error: %d\n", |
| GT1151_DEFAULT_FW, ret); |
| return ret; |
| } |
| GTP_INFO("firmware size: 0x%x\n", (unsigned int)fw_entry->size); |
| |
| update_info.fw_data = |
| (u8 *)devm_kzalloc(>1x_i2c_client->dev, |
| fw_entry->size, GFP_KERNEL); |
| if (!update_info.fw_data) { |
| GTP_ERROR("Alloca memory fail\n"); |
| release_firmware(fw_entry); |
| return ERROR_MEM; |
| } |
| memcpy(update_info.fw_data, fw_entry->data, fw_entry->size); |
| update_info.fw_length = fw_entry->size; |
| release_firmware(fw_entry); |
| #elif defined(CONFIG_GTP_HEADER_FW_UPDATE) |
| update_info.fw_data = gt1x_default_FW; |
| update_info.fw_length = sizeof(gt1x_default_FW); |
| #endif |
| } |
| |
| while (retry > 0) { |
| retry--; |
| update_info.firmware = |
| kzalloc(sizeof(struct fw_info), GFP_KERNEL); |
| if (update_info.firmware == NULL) { |
| GTP_INFO("Alloc %d bytes memory fail.", |
| (int)(sizeof(struct fw_info))); |
| continue; |
| } else { |
| GTP_INFO("Alloc %d bytes memory success.", |
| (int)(sizeof(struct fw_info))); |
| break; |
| } |
| } |
| if (retry <= 0) { |
| ret = ERROR_RETRY; |
| goto gt1x_update_pre_fail1; |
| } |
| |
| retry = 5; |
| while (retry > 0) { |
| retry--; |
| update_info.buffer = 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: |
| return ret; |
| } |
| /** |
| * @return: return a pointer pointed at the content of firmware |
| * if success, otherwise return NULL. |
| */ |
| u8 *gt1x_get_fw_data(u32 offset, int length) |
| { |
| return &update_info.fw_data[offset]; |
| } |
| |
| 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= 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_INFO("------------------------------------------"); |
| GTP_INFO("Subsystem: %d", i); |
| GTP_INFO("Type: %d", |
| update_info.firmware->subsystem[i].type); |
| GTP_INFO("Length: %d", |
| update_info.firmware->subsystem[i].length); |
| GTP_INFO("Address: 0x%08X", |
| update_info.firmware->subsystem[i].address); |
| GTP_INFO("Offset: %d", |
| update_info.firmware->subsystem[i].offset); |
| } |
| |
| return 0; |
| } |
| |
| |
| int gt1x_update_judge(void) |
| { |
| int ret; |
| u8 reg_val[1] = {0}; |
| u8 retry = 3; |
| 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 register 0x41E4 */ |
| do { |
| ret = gt1x_i2c_read_dbl_check(0x41E4, reg_val, 1); |
| if (ret != 0) { /* read reg failed */ |
| gt1x_reset_guitar(); |
| } else { |
| break; |
| } |
| } |
| while (--retry) |
| ; |
| if (reg_val[0] != 0xBE) { |
| GTP_ERROR("check 0x41E4 failed, force update!!!"); |
| return 0; |
| } |
| |
| ret = gt1x_read_version(&ver_info); |
| if (ret < 0) { |
| GTP_ERROR("Get IC's version info failed, force update!!!"); |
| return 0; |
| } |
| |
| if (memcmp(fw_ver_info.product_id, ver_info.product_id, 4)) { |
| GTP_ERROR("Product id is not match!"); |
| return ERROR_CHECK; |
| } |
| if ((fw_ver_info.mask_id & 0xFFFFFF00) != |
| (ver_info.mask_id & 0xFFFFFF00)) { |
| GTP_ERROR("Mask id is not match!"); |
| return ERROR_CHECK; |
| } |
| if (fw_ver_info.patch_id <= ver_info.patch_id) { |
| GTP_ERROR("The version of the fw is not high than the IC's!"); |
| flag_need_update = 0; |
| return ERROR_CHECK; |
| } |
| flag_need_update = 1; |
| 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]; |
| } |
| checksum &= 0xFFFF; |
| return checksum; |
| } |
| |
| int gt1x_recall_check(u8 *chk_src, u16 start_addr, u16 chk_length) |
| { |
| u8 rd_buf[PACK_SIZE] = {0}; |
| 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_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; |
| } |
| |
| 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] = {0}; |
| 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; |
| } |
| |
| 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] = 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; |
| while (wait_time > 0) { |
| wait_time--; |
| msleep(20); |
| ret = gt1x_i2c_read(0x8022, buffer, 1); |
| if (ret) |
| continue; |
| if (buffer[0] == 0xAA) { |
| GTP_INFO("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; |
| } |
| GTP_INFO("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_read_flash(u32 addr, int length) |
| { |
| int wait_time; |
| int ret = 0; |
| u8 buffer[4] = {0}; |
| 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 occurred."); |
| return ret; |
| } |
| |
| wait_time = 200; |
| while (wait_time > 0) { |
| wait_time--; |
| msleep(20); |
| ret = gt1x_i2c_read(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_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; |
| } |
| |
| void gt1x_update_cleanup(void) |
| { |
| 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_update_firmware(char *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.max_progress = 9; |
| update_info.progress = 0; |
| |
| gt1x_enter_update_mode(); |
| |
| ret = gt1x_update_prepare(filename); |
| if (ret) { |
| update_info.status = UPDATE_STATUS_IDLE; |
| goto gt1x_update_exit; |
| } |
| update_info.progress = 1; |
| |
| ret = gt1x_check_firmware(); |
| if (ret) |
| goto gt1x_update_exit; |
| update_info.progress = 2; |
| |
| ret = gt1x_update_judge(); |
| if (ret) |
| goto gt1x_update_exit; |
| update_info.progress = 3; |
| |
| 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 = 4; |
| |
| 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 = 5; |
| 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 = 6; |
| |
| GTP_INFO("flag_need_update = %d!", flag_need_update); |
| if (flag_need_update) |
| 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 = 7; |
| |
| 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 = 8; |
| |
| GTP_INFO("Reset guitar & check firmware in flash."); |
| /*heck_state = SUCCESS;*/ |
| 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) |
| break; |
| } |
| update_info.progress = 9; |
| |
| gt1x_update_exit: |
| gt1x_update_cleanup(); |
| gt1x_leave_update_mode(); |
| gt1x_read_version(NULL); |
| |
| update_info.status = UPDATE_STATUS_IDLE; |
| if (ret) { |
| GTP_ERROR("Update firmware failed, ret = %d!", ret); |
| } else if (gt1x_init_failed) { |
| gt1x_read_version(>1x_version); |
| gt1x_init_panel(); |
| } |
| GTP_INFO("Update firmware succeefully!"); |
| return ret; |
| } |
| |
| int __gt1x_hold_ss51_dsp_20(void) |
| { |
| int ret = -1; |
| int retry = 0; |
| u8 buf[1]; |
| int hold_times = 0; |
| |
| while (retry++ < 5) { |
| /*Hold ss51 & dsp*/ |
| buf[0] = 0x0C; |
| ret = gt1x_i2c_write(_rRW_MISCTL__SWRST_B0_, buf, 1); |
| if (ret) { |
| GTP_DEBUG("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_DEBUG("Hold ss51 & dsp I2C error,retry:%d", retry); |
| continue; |
| } |
| if (buf[0] == 0x0C) { |
| if (hold_times++ < 20) |
| continue; |
| else |
| break; |
| } |
| GTP_DEBUG("Hold ss51 & dsp confirm 0x4180 failed,value:%d", |
| buf[0]); |
| } |
| if (retry >= 5) { |
| 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; |
| u8 buffer[2]; |
| |
| gt1x_select_addr(); |
| msleep(20); |
| /*old 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; |
| } |
| |
| void gt1x_leave_update_mode(void) |
| { |
| GTP_DEBUG("[leave_update_mode]reset chip."); |
| if (flag_need_update) |
| gt1x_reset_guitar(); |
| |
| #ifdef CONFIG_GTP_ESD_PROTECT |
| gt1x_esd_switch(SWITCH_ON); |
| #endif |
| gt1x_irq_enable(); |
| } |
| |
| void read_reg(u16 addr, int len) |
| { |
| int i; |
| u8 buffer[16] = {0}; |
| int read_len = 0; |
| int cur_len; |
| |
| GTP_INFO("Read address: 0x%04X, Length: %d", addr, len); |
| while (len > 0) { |
| cur_len = (len > 16 ? 16 : len); |
| gt1x_i2c_read(addr + read_len, buffer, cur_len); |
| GTP_INFO("<<GTP-INF>> "); |
| for (i = 0; i < cur_len; i++) |
| GTP_INFO("%02X ", buffer[i]); |
| GTP_INFO("\n"); |
| len -= cur_len; |
| read_len += cur_len; |
| } |
| } |
| int gt1x_hold_ss51_dsp_no_reset(void) |
| { |
| int ret = ERROR; |
| u8 buffer[2]; |
| |
| /*old 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; |
| } |
| /*t reset*/ |
| buffer[0] = 0x01; |
| ret = gt1x_i2c_write(_bWO_MISCTL__CPU_SWRST_PULSE, buffer, 1); |
| if (ret) { |
| GTP_ERROR("software reset fail!"); |
| return ret; |
| } |
| /*et 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("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) { |
| /*lect 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; |
| } |