blob: 8d1dc7f8ccb5cbe2b7efd010715fc26abdc6d4c6 [file] [log] [blame]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
* *
* FTS API for Flashing the IC *
* *
**************************************************************************
**************************************************************************
*
*/
/*!
* \file ftsFlash.c
* \brief Contains all the functions to handle the FW update process
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <stdarg.h>
#include <linux/serio.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include "fts_io.h"
#include "fts_flash.h"
#include "fts_error.h"
#ifdef FW_H_FILE
#include "../fts_fw.h"
#endif
/* decleration of global variable containing System Info Data */
struct sys_info system_info;
/**
* calculate crc for the given data
* @param message pointer to data to perform crc
* @param size varaible for size of data
* @return OK if success or an error code which specify the type of error
*/
unsigned int calculate_crc(unsigned char *message, int size)
{
int i, j;
unsigned int byte, crc, mask;
i = 0;
crc = 0xFFFFFFFF;
while (i < size) {
byte = message[i];
crc = crc ^ byte;
for (j = 7; j >= 0; j--) {
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
i = i + 1;
}
return ~crc;
}
/** @addtogroup system_info
* @{
*/
/**
* Read the System Info data from memory.FW Request is send to load system info
* to memory and then read from there.
* @return OK if success or an error code which specify the type of error
*/
int read_sys_info(void)
{
int res;
u8 data[SYS_INFO_SIZE] = { 0 };
int index = 0;
int i = 0;
res = fts_request_hdm(HDM_REQ_SYS_INFO);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = fts_read_hdm(FRAME_BUFFER_ADDR, data, SYS_INFO_SIZE);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
pr_info("%s: type: %02X, cnt: %02X, len: %d words\n",
__func__, data[0], data[1],
(u16)((data[3] << 8) + data[2]));
if (data[0] != HDM_REQ_SYS_INFO) {
pr_err("%s: parsing ERROR %08X\n", __func__, ERROR_TIMEOUT);
return ERROR_TIMEOUT;
}
index += 4;
u8_to_u16(&data[index], &system_info.u16_api_ver_rev);
index += 1;
system_info.u8_api_ver_major = data[++index];
system_info.u8_api_ver_minor = data[++index];
index++;
u8_to_u16(&data[index], &system_info.u16_chip0_id);
index += 2;
u8_to_u16(&data[index], &system_info.u16_chip0_ver);
index += 2;
u8_to_u16(&data[index], &system_info.u16_chip1_id);
index += 2;
u8_to_u16(&data[index], &system_info.u16_chip1_ver);
index += 2;
u8_to_u16(&data[index], &system_info.u16_fw_ver);
index += 2;
u8_to_u16(&data[index], &system_info.u16_svn_rev);
index += 2;
u8_to_u16(&data[index], &system_info.u16_pe_ver);
index += 2;
u8_to_u16(&data[index], &system_info.u16_reg_ver);
index += 2;
u8_to_u16(&data[index], &system_info.u16_scr_x_res);
index += 2;
u8_to_u16(&data[index], &system_info.u16_scr_y_res);
index += 2;
system_info.u8_scr_tx_len = data[index];
system_info.u8_scr_rx_len = data[++index];
index += 3;
for (i = 0; i < DIE_INFO_SIZE; i++)
system_info.u8_die_info[i] = data[index++];
for (i = 0; i < RELEASE_INFO_SIZE; i++)
system_info.u8_release_info[i] = data[index++];
u8_to_u32(&data[index], &system_info.u32_flash_org_info);
index += 8;
system_info.u8_cfg_afe_ver = data[index++];
index += 1;
system_info.u8_ms_scr_afe_ver = data[index++];
system_info.u8_ms_scr_gv_ver = data[index++];
system_info.u8_ms_scr_lp_afe_ver = data[index++];
system_info.u8_ms_scr_lp_gv_ver = data[index++];
system_info.u8_ss_tch_afe_ver = data[index++];
system_info.u8_ss_tch_gv_ver = data[index++];
system_info.u8_ss_det_afe_ver = data[index++];
system_info.u8_ss_det_gv_ver = data[index++];
index += 54;
u8_to_u16(&data[index], &system_info.u16_dbg_info_addr);
index += 8;
u8_to_u16(&data[index], &system_info.u16_ms_scr_raw_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ms_scr_filter_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ms_scr_strength_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ms_scr_baseline_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_tx_raw_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_tx_filter_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_tx_strength_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_tx_baseline_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_rx_raw_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_rx_filter_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_rx_strength_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_tch_rx_baseline_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_tx_raw_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_tx_filter_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_tx_strength_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_tx_baseline_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_rx_raw_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_rx_filter_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_rx_strength_addr);
index += 2;
u8_to_u16(&data[index], &system_info.u16_ss_det_rx_baseline_addr);
index += 18;
u8_to_u32(&data[index], &system_info.u32_reg_default_sect_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_misc_sect_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_cx_ms_scr_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_cx_ms_scr_lp_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_cx_ss_tch_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_cx_ss_det_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_ioff_ms_scr_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_ioff_ms_scr_lp_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_ioff_ss_tch_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_ioff_ss_det_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_pure_raw_ms_scr_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_pure_raw_ms_scr_lp_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_pure_raw_ss_tch_flash_addr);
index += 4;
u8_to_u32(&data[index], &system_info.u32_pure_raw_ss_det_flash_addr);
pr_info("%s: API Version: 0x%04X\n",
__func__, system_info.u16_api_ver_rev);
pr_info("%s: API Major Version: 0x%02X\n",
__func__, system_info.u8_api_ver_major);
pr_info("%s: API Minor Version: 0x%02X\n",
__func__, system_info.u8_api_ver_minor);
pr_info("%s: ChipId0: 0x%04X\n",
__func__, system_info.u16_chip0_id);
pr_info("%s: ChipVer0: 0x%04X\n",
__func__, system_info.u16_chip0_ver);
pr_info("%s: ChipId1: 0x%04X\n",
__func__, system_info.u16_chip1_id);
pr_info("%s: ChipVer1: 0x%04X\n",
__func__, system_info.u16_chip1_ver);
pr_info("%s: FW Version: 0x%04X\n",
__func__, system_info.u16_fw_ver);
pr_info("%s: SVN Revision: 0x%04X\n",
__func__, system_info.u16_svn_rev);
pr_info("%s: PE Version: 0x%04X\n",
__func__, system_info.u16_pe_ver);
pr_info("%s: REG Revision: 0x%04X\n",
__func__, system_info.u16_reg_ver);
pr_info("%s: Scr-X Resolution: %d\n",
__func__, system_info.u16_scr_x_res);
pr_info("%s: Scr-Y Resolution: %d\n",
__func__, system_info.u16_scr_y_res);
pr_info("%s: Tx Length: %d\n",
__func__, system_info.u8_scr_tx_len);
pr_info("%s: Rx Length: %d\n",
__func__, system_info.u8_scr_rx_len);
pr_info("%s: DIE Info: ", __func__);
for (i = 0; i < DIE_INFO_SIZE; i++)
printk("%02X ", system_info.u8_die_info[i]);
printk("\n");
pr_info("%s: External Release Info: ", __func__);
for (i = 0; i < RELEASE_INFO_SIZE; i++)
printk("%02X ", system_info.u8_release_info[i]);
printk("\n");
pr_info("%s: Flash Org Info: 0x%08X\n",
__func__, system_info.u32_flash_org_info);
pr_info("%s: Config Afe Ver: 0x%02X\n",
__func__, system_info.u8_cfg_afe_ver);
pr_info("%s: Mutual Afe Ver: 0x%02X\n",
__func__, system_info.u8_ms_scr_afe_ver);
pr_info("%s: Mutual GV Ver: 0x%02X\n",
__func__, system_info.u8_ms_scr_gv_ver);
pr_info("%s: Mutual LP Afe Ver: 0x%02X\n",
__func__, system_info.u8_ms_scr_lp_afe_ver);
pr_info("%s: Mutual LP GV Ver: 0x%02X\n",
__func__, system_info.u8_ms_scr_lp_gv_ver);
pr_info("%s: Self Afe Ver: 0x%02X\n",
__func__, system_info.u8_ss_tch_afe_ver);
pr_info("%s: Self GV Ver: 0x%02X\n",
__func__, system_info.u8_ss_tch_gv_ver);
pr_info("%s: Self Detect Afe Ver: 0x%02X\n",
__func__, system_info.u8_ss_det_afe_ver);
pr_info("%s: Self Detect GV Ver: 0x%02X\n",
__func__, system_info.u8_ss_det_gv_ver);
pr_info("%s: Debug Info Address: 0x%04X\n",
__func__, system_info.u16_dbg_info_addr);
pr_info("%s: Mutual Raw Address: 0x%04X\n",
__func__, system_info.u16_ms_scr_raw_addr);
pr_info("%s: Mutual Filter Address: 0x%04X\n",
__func__, system_info.u16_ms_scr_filter_addr);
pr_info("%s: Mutual Strength Address: 0x%04X\n",
__func__, system_info.u16_ms_scr_strength_addr);
pr_info("%s: Mutual Baseline Address: 0x%04X\n",
__func__, system_info.u16_ms_scr_baseline_addr);
pr_info("%s: Self Tx Raw Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_tx_raw_addr);
pr_info("%s: Self Tx Filter Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_tx_filter_addr);
pr_info("%s: Self Tx Strength Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_tx_strength_addr);
pr_info("%s: Self Tx Baseline Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_tx_baseline_addr);
pr_info("%s: Self Rx Raw Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_rx_raw_addr);
pr_info("%s: Self Rx Filter Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_rx_filter_addr);
pr_info("%s: Self Rx Strength Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_rx_strength_addr);
pr_info("%s: Self Rx Baseline Address: 0x%04X\n",
__func__, system_info.u16_ss_tch_rx_baseline_addr);
pr_info("%s: Self Detect Tx Raw Address: 0x%04X\n",
__func__, system_info.u16_ss_det_tx_raw_addr);
pr_info("%s: Self Detect Tx Filter Address: 0x%04X\n",
__func__, system_info.u16_ss_det_tx_filter_addr);
pr_info("%s: Self Detect Tx Strength Address: 0x%04X\n",
__func__, system_info.u16_ss_det_tx_strength_addr);
pr_info("%s: Self Detect Tx Baseline Address: 0x%04X\n",
__func__, system_info.u16_ss_det_tx_baseline_addr);
pr_info("%s: Self Detect Rx Raw Address: 0x%04X\n",
__func__, system_info.u16_ss_det_rx_raw_addr);
pr_info("%s: Self Detect Rx Filter Address: 0x%04X\n",
__func__, system_info.u16_ss_det_rx_filter_addr);
pr_info("%s: Self Detect Rx Strength Address: 0x%04X\n",
__func__, system_info.u16_ss_det_rx_strength_addr);
pr_info("%s: Self Detect Rx Baseline Address: 0x%04X\n",
__func__, system_info.u16_ss_det_rx_baseline_addr);
pr_info("%s: Default Flash Address: 0x%08X\n",
__func__, system_info.u32_reg_default_sect_flash_addr);
pr_info("%s: Misc Flash Address: 0x%08X\n",
__func__, system_info.u32_misc_sect_flash_addr);
pr_info("%s: Cx Mutual Flash Address: 0x%08X\n",
__func__, system_info.u32_cx_ms_scr_flash_addr);
pr_info("%s: Cx Mutual LP Flash Address: 0x%08X\n",
__func__, system_info.u32_cx_ms_scr_lp_flash_addr);
pr_info("%s: Cx Self Flash Address: 0x%08X\n",
__func__, system_info.u32_cx_ss_tch_flash_addr);
pr_info("%s: Cx Self Detect Flash Address: 0x%08X\n",
__func__, system_info.u32_cx_ss_det_flash_addr);
pr_info("%s: Ioff Mutual Flash Address: 0x%08X\n",
__func__, system_info.u32_ioff_ms_scr_flash_addr);
pr_info("%s: Ioff Mutual LP Flash Address: 0x%08X\n",
__func__, system_info.u32_ioff_ms_scr_lp_flash_addr);
pr_info("%s: Ioff Self LP Flash Address: 0x%08X\n",
__func__, system_info.u32_ioff_ss_tch_flash_addr);
pr_info("%s: Ioff Self Detect Flash Address: 0x%08X\n",
__func__, system_info.u32_ioff_ss_det_flash_addr);
pr_info("%s: Pure Raw Mutual Flash Address: 0x%08X\n",
__func__, system_info.u32_pure_raw_ms_scr_flash_addr);
pr_info("%s: Pure Raw Mutual Lp Flash Address: 0x%08X\n",
__func__, system_info.u32_pure_raw_ms_scr_lp_flash_addr);
pr_info("%s: Pure Raw Self Flash Address: 0x%08X\n",
__func__, system_info.u32_pure_raw_ss_tch_flash_addr);
pr_info("%s: Pure Raw Self Detect Flash Address: 0x%08X\n",
__func__, system_info.u32_pure_raw_ss_det_flash_addr);
return res;
}
/** @}*/
/**
* Retrieve the actual FW data from the system (ubin file or header file)
* @param path_to_file name of FW file to load or "NULL" if the FW data should
* be loaded by a .h file
* @param data pointer to the pointer which will contains the FW data
* @param size pointer to a variable which will contain the size of the loaded
* data
* @return OK if success or an error code which specify the type of error
*/
int get_fw_file_data(const char *path_to_file, u8 **data, int *size)
{
const struct firmware *fw = NULL;
struct device *dev = NULL;
int res = 0;
int from = 0;
char *path = (char *)path_to_file;
pr_info("%s: Getting FW file data...\n", __func__);
if (strncmp(path_to_file, "NULL", 4) == 0) {
from = 1;
path = PATH_FILE_FW;
pr_info("%s: Getting FW file data...\n", __func__);
}
/* keep the switch case because if the argument passed is null but
* the option from .h is not set we still try to load from bin */
switch (from) {
#ifdef FW_H_FILE
case 1:
pr_info("%s: Read FW from .h file!\n", __func__);
*size = FW_SIZE_NAME;
*data = (u8 *)kmalloc((*size) * sizeof(u8), GFP_KERNEL);
if (*data == NULL) {
pr_err("%s: Impossible to allocate memory! ERROR %08X\n",
__func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size));
break;
#endif
default:
pr_info("%s: Read FW from BIN file %s !\n", __func__, path);
dev = get_dev();
if (dev != NULL) {
res = request_firmware(&fw, path, dev);
if (res == 0) {
*size = fw->size;
*data = (u8 *)kmalloc((*size) * sizeof(u8),
GFP_KERNEL);
if (*data == NULL) {
pr_err("%s: Impossible to allocate memory! ERROR %08X\n",
__func__, ERROR_ALLOC);
release_firmware(fw);
return ERROR_ALLOC;
}
memcpy(*data, (u8 *)fw->data, (*size));
release_firmware(fw);
} else {
pr_err("%s: No File found! ERROR %08X\n",
__func__ , ERROR_FILE_NOT_FOUND);
return ERROR_FILE_NOT_FOUND;
}
} else {
pr_err("%s: No device found! ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
}
pr_info("%s: get fw file data finished!\n", __func__);
return OK;
}
/**
* Parse the raw data read from a FW file in order to fill properly the fields
* of a Firmware variable
* @param ubin_data raw FW data loaded from system
* @param ubin_size size of ubin_data
* @param fw_data pointer to a Firmware variable which will contain the
*processed data
* @return OK if success or an error code which specify the type of error
*/
int parse_bin_file(u8 *ubin_data, int ubin_size,
struct firmware_file *fw_data)
{
int index = 0;
u32 temp = 0;
u16 u16_temp = 0;
u8 sec_index = 0;
int code_data_found = 0;
u32 crc = 0;
crc = calculate_crc(ubin_data + 4, ubin_size - 4);
if (crc == (u32)((ubin_data[0] << 24) + (ubin_data[1] << 16) +
(ubin_data[2] << 8) + ubin_data[3]))
pr_info("%s: BIN CRC OK\n", __func__);
else {
pr_err("%s: BIN CRC error... ERROR %08X\n", __func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
index += 4;
if (ubin_size <= (BIN_HEADER_SIZE + SECTION_HEADER_SIZE) ||
ubin_data == NULL) {
pr_err("%s: Read only %d instead of %d... ERROR %08X\n",
__func__, ubin_size, BIN_HEADER_SIZE, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
u8_to_u32_be(&ubin_data[index], &temp);
if (temp != BIN_HEADER) {
pr_err("%s: Wrong Signature 0x%08X ... ERROR %08X\n",
__func__, temp, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
index += 5;
u8_to_u16_be(&ubin_data[index], &u16_temp);
if (u16_temp != CHIP_ID) {
pr_err("%s: Wrong Chip ID 0x%04X ... ERROR %08X\n",
__func__, u16_temp, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
pr_info("%s: Chip ID: 0x%04X\n", __func__, u16_temp);
index += 27;
while (index < ubin_size) {
u8_to_u32_be(&ubin_data[index], &temp);
if (temp != SECTION_HEADER) {
pr_err("%s: Wrong Section Signature %08X ... ERROR %08X\n",
__func__, temp, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
index += 4;
u8_to_u16_be(&ubin_data[index], &u16_temp);
if (u16_temp == FINGERTIP_FW_CODE) {
if (code_data_found) {
pr_err("%s: Cannot have more than one code memh ... ERROR %08X\n",
__func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
code_data_found = 1;
index += 4;
u8_to_u32_be(&ubin_data[index], &temp);
fw_data->fw_code_size = temp;
if (fw_data->fw_code_size == 0) {
pr_err("%s: Code data cannot be empty ... ERROR %08X\n",
__func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
fw_data->fw_code_data =
(u8 *)kmalloc(fw_data->fw_code_size *
sizeof(u8), GFP_KERNEL);
if (fw_data->fw_code_data == NULL) {
pr_err("%s: Error allocating memory... ERROR %08X\n",
__func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
fw_data->num_code_pages =
(fw_data->fw_code_size / FLASH_PAGE_SIZE);
if (fw_data->fw_code_size % FLASH_PAGE_SIZE)
fw_data->num_code_pages++;
pr_info("%s: code pages: %d\n",
__func__, fw_data->num_code_pages);
pr_info("%s: code size: %d bytes\n",
__func__, fw_data->fw_code_size);
index += 12;
memcpy(fw_data->fw_code_data,
&ubin_data[index], fw_data->fw_code_size);
index += fw_data->fw_code_size;
fw_data->fw_ver =
(u16)((fw_data->fw_code_data[209] << 8) +
fw_data->fw_code_data[208]);
pr_info("%s: FW version: 0x%04X\n",
__func__, fw_data->fw_ver);
pr_info("%s: SVN revision: 0x%04X\n",
__func__,
(u16)((fw_data->fw_code_data[211]
<< 8) + fw_data->fw_code_data[210]));
fw_data->flash_code_pages =
fw_data->fw_code_data[216];
fw_data->panel_info_pages =
fw_data->fw_code_data[217];
pr_info("%s: Code Pages(in org info): %02X,Panel Info "
"Pages(in org info): %02X\n",
__func__, fw_data->flash_code_pages,
fw_data->panel_info_pages);
if ((fw_data->flash_code_pages +
fw_data->panel_info_pages) > NUM_FLASH_PAGES) {
pr_err("%s: FW code + panel Info pages(%d) is "
"more the maximum flash pages(%d)\n",
__func__, (fw_data->flash_code_pages +
fw_data->panel_info_pages),
NUM_FLASH_PAGES);
return ERROR_FILE_PARSE;
}
if (fw_data->num_code_pages >
fw_data->flash_code_pages) {
pr_err("%s: FW code size in the bin file(%d) is "
"more than the FW code pages(%d) allocated by FW\n",
__func__, fw_data->num_code_pages,
fw_data->flash_code_pages);
return ERROR_FILE_PARSE;
}
} else {
fw_data->num_sections++;
fw_data->sections[sec_index].sec_id = u16_temp;
index += 4;
u8_to_u32_be(&ubin_data[index], &temp);
fw_data->sections[sec_index].sec_size = temp;
if (fw_data->sections[sec_index].
sec_size == 0) {
pr_err("%s: section data cannot be empty ... ERROR %08X\n",
__func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
fw_data->sections[sec_index].sec_data =
(u8 *)kmalloc(fw_data->
sections[sec_index].sec_size *
sizeof(u8), GFP_KERNEL);
if (fw_data->sections[sec_index].
sec_data == NULL) {
pr_err("%s: Error allocating memory... ERROR %08X\n",
__func__, ERROR_FILE_PARSE);
return ERROR_FILE_PARSE;
}
pr_info("%s: section%d type : 0x%02X\n",
__func__, sec_index,
fw_data->sections[sec_index].sec_id);
pr_info("%s: section%d size : %d bytes\n",
__func__, sec_index,
fw_data->sections[sec_index].sec_size);
index += 12;
memcpy(fw_data->sections[sec_index].sec_data,
&ubin_data[index],
fw_data->sections[sec_index].sec_size);
index += fw_data->sections[sec_index].sec_size;
if (fw_data->sections[sec_index].sec_id ==
FINGERTIP_FW_REG) {
fw_data->sections[sec_index].sec_ver =
(u16)((fw_data->sections[sec_index].
sec_data[15] << 8) +
fw_data->sections[sec_index].
sec_data[14]);
pr_info("%s: section version : 0x%04X\n",
__func__,
fw_data->sections[sec_index].sec_ver);
}
sec_index++;
}
}
pr_info("%s: Total number of sections : %d\n", __func__,
fw_data->num_sections);
return OK;
}
/**
* Perform all the steps to read the FW that should be burnt in the IC from
* the system and parse it in order to fill a Firmware struct with the relevant
* info
* @param path name of FW file to load or "NULL" if the FW data should be
*loaded by a .h file
* @param fw_file pointer to a Firmware variable which will contains
* the FW data and info
* @return OK if success or an error code which specify the type of error
*/
int read_fw_file(const char *path, struct firmware_file *fw_file)
{
int orig_size;
u8 *orig_data = NULL;
int res = OK;
res = get_fw_file_data(path, &orig_data, &orig_size);
if (res < OK) {
pr_err("%s: Impossible to retrieve FW file data... ERROR %08X\n",
__func__, ERROR_MEMH_READ);
res |= ERROR_MEMH_READ;
goto goto_end;
}
res = parse_bin_file(orig_data, orig_size, fw_file);
if (res < OK) {
pr_err("%s: BIN file parse ERROR %08X\n", __func__,
ERROR_MEMH_READ);
res |= ERROR_MEMH_READ;
goto goto_end;
}
goto_end:
if (orig_data != NULL) {
kfree(orig_data);
orig_data = NULL;
}
return res;
}
/**
* To configure spi mode to 4 wire ,by default spi configuration is in 3
* wire mode up on power up
* @return OK if success or an error code which specify the type of error
*/
int configure_spi4(void)
{
int res = OK;
u8 data = 0x02;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, GPIO_GPIO_PU_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x07;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE,
GPIO_MISO_CONFIG_ADDR, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x02;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, SPI4_CONFIG_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
return res;
}
/**
* Initaite the mandatory steps to perform flash erase/program
* including system reset,enable UVLO,flash unlock
* @return OK if success or an error code which specify the type of error
*/
int flash_update_preset(void)
{
int res = OK;
u8 data = 0x01;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, SYS_RST_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
#ifndef I2C_INTERFACE
#ifdef SPI4_WIRE
pr_info("%s: Configuring SPI4..\n", __func__);
res = configure_spi4();
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
#endif
#endif
data = 0x66;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, UVLO_CTRL_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x13;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE,
FLASH_FSM_CTRL_ADDR, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x20;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, BOOT_OPT_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x00;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, PAGE_SEL_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = fts_write_read_u8ux(FTS_CMD_HW_REG_R, HW_ADDR_SIZE,
FLASH_CTRL_ADDR, &data, 1, DUMMY_BYTE);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data |= 0x03;
/*Bit 7 in FLASH_CTRL_ADDR should be set if GPIO6 not to be used,
by default its 0, so gpio6 should be used*/
#ifdef FTS_GPIO6_UNUSED
data |= 0x80;
#endif
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, FLASH_CTRL_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
return res;
}
/**
* Poll the Flash Status Registers after the execution of a command to check
* if the Flash becomes ready within a timeout
* @param type register to check according to the previous command sent
* @return OK if success or an error code which specify the type of error
*/
int wait_for_flash_ready(u8 type)
{
u8 cmd[5] = { FTS_CMD_HW_REG_R, 0x20, 0x00, 0x00, type };
u8 read_data[2] = { 0 };
int i, res = -1;
pr_info("%s: Waiting for flash ready ...\n", __func__);
for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) {
res = fts_write_read(cmd, 5, read_data, 2);
if (res < OK)
pr_err("%s: ERROR %08X\n",
__func__, ERROR_BUS_W);
else {
#ifdef I2C_INTERFACE
res = read_data[0] & 0x80;
#else
res = read_data[1] & 0x80;
#endif
pr_info("%s: flash status = %d\n", __func__, res);
}
msleep(FLASH_WAIT_BEFORE_RETRY);
}
if (i == FLASH_RETRY_COUNT && res != 0) {
pr_err("%s: Wait for flash TIMEOUT! ERROR %08X\n", __func__,
ERROR_TIMEOUT);
return ERROR_TIMEOUT;
}
pr_info("%s: Flash READY!\n", __func__);
return OK;
}
/**
* Erase the multiple pages in flash
* @param flash_pages total page number to be erased
* @return OK if success or an error code which specify the type of error
*/
int flash_erase(int flash_pages)
{
u8 i = 0;
u8 mask[6] = { 0 };
u8 mask_cnt = 6;
int res = OK;
u8 data = 0x00;
for (i = 0; i < flash_pages; i++) {
res = from_id_to_mask(i, mask, mask_cnt);
if (res != OK)
return res;
}
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE,
FLASH_PAGE_MASK_ADDR, mask, mask_cnt);
if (res < OK) {
pr_err("%s: mask set ERROR %08X\n", __func__, res);
return res;
}
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, PAGE_SEL_ADDR,
&data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = fts_write_read_u8ux(FTS_CMD_HW_REG_R, HW_ADDR_SIZE,
FLASH_MULTI_PAGE_ERASE_ADDR, &data, 1, DUMMY_BYTE);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data |= 0x80;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE,
FLASH_MULTI_PAGE_ERASE_ADDR, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
data = 0x80;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE,
FLASH_ERASE_CTRL_ADDR, &data, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = wait_for_flash_ready(FLASH_ERASE_READY_VAL);
if (res != OK) {
pr_err("%s: ERROR %08X\n", __func__, res | ERROR_FLASH_NOT_READY);
return res | ERROR_FLASH_NOT_READY;
/* Flash not ready within the chosen time, better exit! */
}
pr_info("%s: Erase flash page by page DONE!\n", __func__);
return OK;
}
/**
* Start the DMA procedure which actually transfer and burn the data loaded
* from memory into the Flash
* @return OK if success or an error code which specify the type of error
*/
int start_flash_dma(void)
{
int res;
u8 cmd[12] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
0x6B, 0x00, 0xFF, 0x1C, 0x10, 0x00, 0x00,
FLASH_DMA_CODE_VAL };
/* write the command to erase the flash */
pr_info("%s: Command flash DMA ...\n", __func__);
if (fts_write(cmd, 12) < OK) {
pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
return ERROR_BUS_W;
}
res = wait_for_flash_ready(FLASH_PGM_READY_VAL);
if (res != OK) {
pr_err("%s: ERROR %08X\n", __func__, res | ERROR_FLASH_NOT_READY);
return res | ERROR_FLASH_NOT_READY;
/* Flash not ready within the chosen time, better exit! */
}
pr_info("%s: flash DMA DONE!\n", __func__);
return OK;
}
/**
* Prepare DMA procedure by loading data to dram and Start the DMA procedure
* @return OK if success or an error code which specify the type of error
*/
int flash_dma(u32 address, u8 *data, int size)
{
int res = 0;
u16 word_address = (u16)(address / 4);
u16 write_count = (u16)((size / 4) - 1);
u8 cmd[7] = { 0x00, 0x00,
(word_address & 0xFF),
((word_address & 0xFF00) >> 8),
(write_count & 0xFF),
((write_count & 0xFF00) >> 8),
0x00 };
u32 dram_address = 0x00100000;
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, dram_address,
data, size);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = fts_write_u8ux(FTS_CMD_HW_REG_W, HW_ADDR_SIZE, FLASH_DMA_ADDR,
cmd, 7);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
res = start_flash_dma();
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
return res;
}
/**
* Copy the FW data that should be burnt in the Flash into the memory and then
* the DMA will take care about burning it into the Flash
* @param address address in memory where to copy the data for flashing code
* @param data pointer to an array of byte which contain the data that should
* be copied into the memory
* @param size size of data
* @return OK if success or an error code which specify the type of error
*/
int fill_flash(u32 address, u8 *data, int size)
{
int remaining = size;
int to_write = 0;
int res;
u32 start_address = address;
int written_already = 0;
while (remaining > 0) {
if (remaining >= FLASH_CHUNK) {
to_write = FLASH_CHUNK;
remaining -= FLASH_CHUNK;
} else {
to_write = remaining;
remaining = 0;
}
pr_info("%s: Flash address: 0x%08X, write_count: %d bytes\n",
__func__, start_address, to_write);
res = flash_dma(start_address, data + written_already,
to_write);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res;
}
start_address += to_write;
written_already += to_write;
}
return OK;
}
/**
* Copy the FW section data via hdm write ,then request to fw for save in to flash
* @param fw struture includes info and data of fw bin for section update
* @param section section id for specific section
* @param save_to_flash flag to enable request for saving to flash
* @return OK if success or an error code which specify the type of error
*/
int flash_section_burn(struct firmware_file fw,
fw_section_t section, u8 save_to_flash)
{
int res = 0;
int i = 0;
for (i = 0; i < fw.num_sections; i++) {
if (fw.sections[i].sec_id == section) {
res = fts_write_hdm(FRAME_BUFFER_ADDR,
fw.sections[i].sec_data, fw.sections[i].sec_size);
if (res < OK) {
pr_err("%s: ERROR %08X\n",
__func__, res | ERROR_FLASH_SEC_UPDATE);
return res | ERROR_FLASH_SEC_UPDATE;
}
res = fts_hdm_write_request(0);
if (res < OK) {
pr_err("%s: ERROR %08X\n",
__func__, res | ERROR_FLASH_SEC_UPDATE);
return res | ERROR_FLASH_SEC_UPDATE;
}
break;
}
}
if (save_to_flash) {
res = fts_fw_request(FLASH_SAVE_ADDR, 7, 1,
TIMEOUT_FW_REG_STATUS);
if (res < OK) {
res |= ERROR_FLASH_SEC_UPDATE;
pr_err("%s: ERROR while saving to flash: %08X\n",
__func__, res);
}
}
return res;
}
/**
* Execute the procedure to burn a FW in FTM4/FTI IC
* @param info pointer to fts_ts_info which contains info about the device and
* its hw setup
* @param fw structure which contain the FW to be burnt
* @param force_burn if >0, the flashing procedure will be forced and executed
* regardless the additional info, otherwise the FW in the file will be burnt
* only if it is newer than the one running in the IC
* @return OK if success or an error code which specify the type of error
*/
int flash_burn(struct fts_ts_info *info, struct firmware_file fw,
struct force_update_flag *force_burn)
{
int res = OK;
u8 data[4] = { 0x00 };
int section_updated = 0;
pr_info("%s: FW code version: Current FW|Bin FW: 0x%04X|0x%04X\n",
__func__, system_info.u16_fw_ver, fw.fw_ver);
if (!force_burn->code_update) {
if (system_info.u16_fw_ver != fw.fw_ver) {
pr_info("%s: Different FW version: force updating the FW..\n",
__func__);
force_burn->code_update = 1;
} else
pr_debug("%s: FW version is same.. No need to update FW..\n",
__func__);
}
pr_info("%s: flash code pages allocated: Current|Bin: %d|%d\n",
__func__, (system_info.u32_flash_org_info & 0xFF),
fw.flash_code_pages);
pr_info("%s: flash panel info pages allocated: Current|Bin: %d|%d\n",
__func__, ((system_info.u32_flash_org_info & 0xFF00) >> 8),
fw.panel_info_pages);
if (fw.flash_code_pages > (system_info.u32_flash_org_info & 0xFF))
pr_info("%s: WARNING!! No FW or There is change in the number of "
"pages allocated for FW code. Flashing the new FW will "
"delete the CX/Reg/Panel config data already saved in "
"the flash..Touch may not work\n",
__func__);
if (force_burn->code_update) {
res = flash_update_preset();
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_CODE_UPDATE);
return res | ERROR_FLASH_CODE_UPDATE;
}
pr_info("%s: Erasing flash..\n", __func__);
res = flash_erase(fw.num_code_pages);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_CODE_UPDATE);
return res | ERROR_FLASH_CODE_UPDATE;
}
pr_info("%s: Updating Flash FW Code..\n", __func__);
res = fill_flash(FLASH_START_ADDR, fw.fw_code_data,
fw.fw_code_size);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_CODE_UPDATE);
return res | ERROR_FLASH_CODE_UPDATE;
}
pr_info("%s: Flash Code update finished..\n", __func__);
res = fts_system_reset(info, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_CODE_UPDATE);
return res | ERROR_FLASH_CODE_UPDATE;
}
res = read_sys_info();
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_CODE_UPDATE);
return res | ERROR_FLASH_CODE_UPDATE;
}
pr_info("%s: FW version after FW code update, New FW|Bin FW: 0x%04X|0x%04X\n",
__func__, system_info.u16_fw_ver, fw.fw_ver);
if (system_info.u16_fw_ver != fw.fw_ver) {
pr_err("%s: Different FW version after FW code update\n",
__func__);
return ERROR_FLASH_CODE_UPDATE;
}
}
res = fts_read_fw_reg(SYS_ERROR_ADDR + 4, data, 4);
if (res < OK) {
pr_err("%s: ERROR reading system error registers %08X\n",
__func__, res);
return ERROR_FLASH_UPDATE;
}
pr_info("%s: Section System Errors: reg section: %02X, ms_section: %02X, ss_section: %02X\n",
__func__, (data[0] & REG_CRC_MASK), (data[1] & MS_CRC_MASK),
(data[1] & SS_CRC_MASK));
pr_info("%s: System Crc Errors: misc: %02X, ioff: %02X, pure_raw_ms: %02X\n",
__func__, (data[0] & REG_MISC_MASK), (data[2] & IOFF_CRC_MASK),
(data[3] & RAWMS_CRC_MASK));
force_burn->section_update[0] = (force_burn->section_update[0] == 1) ?
force_burn->section_update[0] : ((data[0] & REG_CRC_MASK) != 0);
force_burn->section_update[1] = (force_burn->section_update[1] == 1) ?
force_burn->section_update[1] : ((data[1] & MS_CRC_MASK) != 0);
force_burn->section_update[2] = (force_burn->section_update[2] == 1) ?
force_burn->section_update[2] : ((data[1] & SS_CRC_MASK) != 0);
force_burn->panel_init = (force_burn->panel_init == 1) ?
force_burn->panel_init : (((data[0] & REG_MISC_MASK) != 0) ||
((data[2] & IOFF_CRC_MASK) != 0) ||
((data[3] & RAWMS_CRC_MASK) != 0));
pr_info("%s: Force update flags: reg section: %02X, ms_section:%02X, "
"ss_section: %02X, panel_init: %02X\n",
__func__, force_burn->section_update[0],
force_burn->section_update[1], force_burn->section_update[2],
force_burn->panel_init);
pr_info("%s: Reg version before update, Current reg|Bin reg: 0x%04X|0x%04X\n",
__func__, system_info.u16_reg_ver, fw.sections[0].sec_ver);
if ((force_burn->section_update[0]) ||
(system_info.u16_reg_ver != fw.sections[0].sec_ver)) {
section_updated = 1;
pr_info("%s: Updating reg section..\n", __func__);
res = flash_section_burn(fw, FINGERTIP_FW_REG, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res);
return res | ERROR_FLASH_SEC_UPDATE;
}
pr_info("%s: Flash Reg update done..checking for errors..\n",
__func__);
} else
pr_debug("%s: No need to update reg section..\n", __func__);
#ifdef MS_GV_METHOD
/*check cfg_afe_ver with ms_scr_gv_ver/ms_scr_lp_gv_ver
MSCX - Golden Value - ToDo
#endif
#ifdef SS_GV_METHOD
check cfg_afe_ver with ss_tch_gv_ver/ss_det_gv_ver
SSCX - Golden Value - ToDo */
#endif
if (section_updated) {
res = fts_system_reset(info, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_UPDATE);
return res | ERROR_FLASH_UPDATE;
}
res = read_sys_info();
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__,
res | ERROR_FLASH_UPDATE);
return res | ERROR_FLASH_UPDATE;
}
res = fts_read_fw_reg(SYS_ERROR_ADDR + 4, data, 2);
if (res < OK) {
pr_err("%s: ERROR reading system error registers %08X\n",
__func__, res);
return ERROR_FLASH_UPDATE;
}
pr_info("%s: Section System Errors After section update: reg section: "
"%02X, ms_section: %02X, ss_section: %02X\n",
__func__, (data[0] & REG_CRC_MASK),
(data[1] & MS_CRC_MASK), (data[1] & SS_CRC_MASK));
if (((data[0] & REG_CRC_MASK) != 0) ||
(system_info.u16_reg_ver != fw.sections[0].sec_ver)) {
pr_err("%s: Error updating flash reg section\n",
__func__);
return ERROR_FLASH_UPDATE;
}
/* to add MS/SS CRC CHECKS AFTER GV FLASH*/
}
return res;
}
/**
* Perform full panel initilisation based on the cx versions and crc status
* @param info pointer to fts_ts_info which contains info about the device and
* its hw setup
* @param force_update , flags that will force the flash procedure for each
* sections and executed regardless the additional info, otherwise the FW in
* the file will be burnt only if it is newer than the one running in the IC
* @return OK if success or an error code which specify the type of error
*/
int full_panel_init(struct fts_ts_info *info, struct force_update_flag *force_update)
{
int res = OK;
int event_to_search = EVT_ID_NOEVENT;
u8 read_data[8] = { 0x00 };
if (!force_update->panel_init) {
#ifndef MS_GV_METHOD
if ((force_update->section_update[1]) ||
(system_info.u8_cfg_afe_ver != system_info.u8_ms_scr_afe_ver) ||
(system_info.u8_cfg_afe_ver !=
system_info.u8_ms_scr_lp_afe_ver)) {
force_update->panel_init = 1;
}
#endif
#ifndef SS_GV_METHOD
if ((force_update->section_update[2])
|| (system_info.u8_cfg_afe_ver !=
system_info.u8_ss_tch_afe_ver) ||
(system_info.u8_cfg_afe_ver !=
system_info.u8_ss_det_afe_ver)) {
force_update->panel_init = 1;
}
#endif
}
if (force_update->panel_init) {
//Save MPFlag
u8 data = MP_FLAG_BOOT;
pr_info("%s: Saving MP Flag Boot..\n", __func__);
fts_write_fw_reg(MP_FLAG_ADDR, &data, 1);
pr_info("%s: Starting Init..\n", __func__);
res = fts_fw_request(PI_ADDR, 1, 1, TIMEOUT_FPI);
if (res < OK) {
pr_err("%s: Error performing autotune.. %08X\n",
__func__, res | ERROR_INIT);
return res | ERROR_INIT;
}
res = poll_for_event(&event_to_search, 1, read_data,
TIMEOUT_GENERAL);
if (res < OK)
pr_err("%s: ERROR %08X\n", __func__, res);
res = fts_system_reset(info, 1);
if (res < OK) {
pr_err("%s: ERROR %08X\n", __func__, res | ERROR_INIT);
return res | ERROR_INIT;
}
res = read_sys_info();
if (res < OK) {
pr_err("%s: Error reading sys info %08X\n",
__func__, res);
res |= ERROR_INIT;
}
res = fts_read_sys_errors();
#ifndef MS_GV_METHOD
if ((system_info.u8_cfg_afe_ver !=
system_info.u8_ms_scr_afe_ver) ||
(system_info.u8_cfg_afe_ver !=
system_info.u8_ms_scr_lp_afe_ver)) {
res |= ERROR_INIT;
pr_err("%s: config afe version doesn't match with MS CX"
" fields after autotune.. Touch may not work. %08X\n",
__func__, res);
}
#endif
#ifndef SS_GV_METHOD
if ((system_info.u8_cfg_afe_ver !=
system_info.u8_ss_tch_afe_ver) ||
(system_info.u8_cfg_afe_ver !=
system_info.u8_ss_det_afe_ver)) {
res |= ERROR_INIT;
pr_err("%s: config afe version doesn't match with SS CX "
"fields after autotune.. Touch may not work. %08X\n",
__func__, res);
}
#endif
pr_info("%s: Init completed..\n", __func__);
} else
pr_debug("%s: No need to start Init..\n", __func__);
return res;
}
/**
* Perform all the steps necessary to burn the FW into the IC
* @param info pointer to fts_ts_info which contains info about the device and
* its hw setup
* @param force_update , flags that will force the flash procedure for each
* sections and executed regardless the additional info, otherwise the FW in
* the file will be burnt only if it is newer than the one running in the IC
* @return OK if success or an error code which specify the type of error
*/
int flash_update(struct fts_ts_info *info, struct force_update_flag *force_update)
{
int res;
int i = 0;
struct firmware_file fw;
fw.fw_code_data = NULL;
fw.num_sections = 0;
fw.flash_code_pages = 0;
fw.num_code_pages = 0;
fw.fw_code_size = 0;
fw.fw_ver = 0;
fw.panel_info_pages = 0;
for (i = 0; i < FLASH_MAX_SECTIONS; i++) {
fw.sections[i].sec_data = NULL;
fw.sections[i].sec_id =
fw.sections[i].sec_ver = fw.sections[i].sec_size = 0;
}
res = read_fw_file(PATH_FILE_FW, &fw);
if (res < OK) {
pr_err("%s: ERROR reading file %08X\n", __func__, res);
goto goto_end;
}
res = fts_system_reset(info, 1);
if (res < OK) {
pr_err("%s: Cannot read Controller Ready..No FW or Connection "
"issue.. ERROR %08X\n",
__func__, res);
force_update->code_update = 1;
}
res = flash_burn(info, fw, force_update);
if (res < OK) {
pr_err("%s: ERROR flash update %08X\n", __func__, res);
goto goto_end;
}
res = full_panel_init(info, force_update);
if (res < OK) {
pr_err("%s: ERROR auto tune %08X\n", __func__, res);
res = OK;
force_update->panel_init = 0;
pr_info("%s: Continue with boot up, production test is skipped "
"and touch may not work\n",
__func__);
goto goto_end;
}
goto_end:
if (fw.fw_code_data != NULL) {
kfree(fw.fw_code_data);
fw.fw_code_data = NULL;
}
for (i = 0; i < fw.num_sections; i++) {
if (fw.sections[i].sec_data != NULL) {
kfree(fw.sections[i].sec_data);
fw.sections[i].sec_data = NULL;
}
}
return res;
}