blob: 3472532c8fa97772fff1b8a92c2a5d2aaf536699 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* FTS Capacitive touch screen controller (FingerTipS)
*
* Copyright (C) 2016-2019, STMicroelectronics Limited.
* Authors: AMG(Analog Mems Group) <marco.cali@st.com>
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
** marco.cali@st.com **
**************************************************************************
* *
* FTS API for Flashing the IC *
* *
**************************************************************************
**************************************************************************
*
*/
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <stdarg.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/time.h>
#include <linux/pm.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/power_supply.h>
#include <linux/firmware.h>
#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include "ftsCrossCompile.h"
#include "ftsCompensation.h"
#include "ftsError.h"
#include "ftsFlash.h"
#include "ftsFrame.h"
#include "ftsIO.h"
#include "ftsSoftware.h"
#include "ftsTest.h"
#include "ftsTime.h"
#include "ftsTool.h"
#include "../fts.h"//needed for including the define FW_H_FILE
#ifdef FW_H_FILE
#include <../fts_fw.h>
#define LOAD_FW_FROM 1
#else
#define LOAD_FW_FROM 0
#endif
#define FTS_LATEST_VERSION 0x1101
static char tag[8] = "[ FTS ]\0";
int getFirmwareVersion(u16 *fw_vers, u16 *config_id)
{
u8 fwvers[DCHIP_FW_VER_BYTE];
u8 confid[CONFIG_ID_BYTE];
int res;
res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR,
fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG);
if (res < OK) {
logError(1,
"%s %s:unable to read fw_version ERROR %02X\n",
tag, __func__, ERROR_FW_VER_READ);
return (res | ERROR_FW_VER_READ);
}
u8ToU16(fwvers, fw_vers); //fw version use big endian
if (*fw_vers != 0) {
// if fw_version is 00 00 means that there is
//no firmware running in the chip therefore will be
//impossible find the config_id
res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE);
if (res < OK) {
logError(1, "%s %s:unable to read config_id ",
tag, __func__);
logError(1, "ERROR %02X\n", ERROR_FW_VER_READ);
return (res | ERROR_FW_VER_READ);
}
u8ToU16(confid, config_id); //config id use little endian
} else {
*config_id = 0x0000;
}
logError(0, "%s FW VERS = %04X\n", tag, *fw_vers);
logError(0, "%s CONFIG ID = %04X\n", tag, *config_id);
return OK;
}
int getFWdata(const char *pathToFile, u8 **data, int *size, int from)
{
const struct firmware *fw = NULL;
struct device *dev = NULL;
int res;
logError(0, "%s %s starting...\n", tag, __func__);
switch (from) {
#ifdef FW_H_FILE
case 1:
logError(1, "%s Read FW from .h file!\n", tag);
*size = FW_SIZE_NAME;
*data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL);
if (*data == NULL) {
logError(1, "%s %s:Impossible to allocate memory! ",
tag, __func__);
logError(1, "ERROR %08X\n", ERROR_ALLOC);
return ERROR_ALLOC;
}
memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size));
break;
#endif
default:
logError(0, "%s Read FW from BIN file!\n", tag);
if (ftsInfo.u16_fwVer == FTS_LATEST_VERSION)
return ERROR_FW_NO_UPDATE;
dev = getDev();
if (dev != NULL) {
res = firmware_request_nowarn(&fw, pathToFile, dev);
if (res == 0) {
*size = fw->size;
*data = (u8 *)kmalloc_array((*size), sizeof(u8),
GFP_KERNEL);
if (*data == NULL) {
logError(1, "%s %s:Impossible to ",
tag, __func__);
logError(1, "%allocate! %08X\n",
ERROR_ALLOC);
release_firmware(fw);
return ERROR_ALLOC;
}
memcpy(*data, (u8 *)fw->data, (*size));
release_firmware(fw);
} else {
logError(0, "%s %s:No File found! ERROR %08X\n",
tag, __func__, ERROR_FILE_NOT_FOUND);
return ERROR_FILE_NOT_FOUND;
}
} else {
logError(1, "%s %s:No device found! ERROR %08X\n",
tag, __func__, ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
/* break; */
}
logError(0, "%s %s:Finshed!\n", tag, __func__);
return OK;
}
int readFwFile(const char *path, struct Firmware *fw, int keep_cx)
{
int res;
int orig_size;
u8 *orig_data = NULL;
res = getFWdata(path, &orig_data, &orig_size, LOAD_FW_FROM);
if (res < OK) {
logError(0, "%s %s:impossible retrieve FW... ERROR %08X\n",
tag, __func__, ERROR_MEMH_READ);
return (res | ERROR_MEMH_READ);
}
res = parseBinFile(orig_data, orig_size, fw, keep_cx);
if (res < OK) {
logError(1, "%s %s:impossible parse ERROR %08X\n",
tag, __func__, ERROR_MEMH_READ);
return (res | ERROR_MEMH_READ);
}
return OK;
}
int flashProcedure(const char *path, int force, int keep_cx)
{
struct Firmware fw;
int res;
fw.data = NULL;
logError(0, "%s Reading Fw file...\n", tag);
res = readFwFile(path, &fw, keep_cx);
if (res < OK) {
logError(0, "%s %s: ERROR %02X\n",
tag, __func__, (res | ERROR_FLASH_PROCEDURE));
kfree(fw.data);
return (res | ERROR_FLASH_PROCEDURE);
}
logError(0, "%s Fw file read COMPLETED!\n", tag);
logError(0, "%s Starting flashing procedure...\n", tag);
res = flash_burn(&fw, force, keep_cx);
if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) {
logError(1, "%s %s: ERROR %02X\n",
tag, __func__, ERROR_FLASH_PROCEDURE);
kfree(fw.data);
return (res | ERROR_FLASH_PROCEDURE);
}
logError(0, "%s flashing procedure Finished!\n", tag);
kfree(fw.data);
return res;
}
#ifdef FTM3_CHIP
int flash_status(void)
{
u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00};
u8 readData = 0;
logError(0, "%s %s:Reading ...\n", tag, __func__);
if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) {
logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R);
return ERROR_I2C_R;
}
readData &= 0x01;
logError(0, "%s %s = %d\n", tag, __func__, readData);
return (int) readData;
}
int flash_status_ready(void)
{
int status = flash_status();
if (status == ERROR_I2C_R) {
logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R);
return ERROR_I2C_R;
}
if (status != FLASH_READY) {
//logError(1,
//"%s %s:flash busy or unknown STATUS = % 02X\n",
//tag, status);
return ERROR_FLASH_UNKNOWN;
}
return FLASH_READY;
}
int wait_for_flash_ready(void)
{
int status;
int (*code)(void);
code = flash_status_ready;
logError(0, "%s Waiting for flash ready...\n", tag);
status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY,
FLASH_RETRY_COUNT);
if (status != FLASH_READY) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
return (status | ERROR_FLASH_NOT_READY);
}
logError(0, "%s Flash ready!\n", tag);
return OK;
}
int flash_unlock(void)
{
int status;
//write the command to perform the unlock
u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0,
FLASH_UNLOCK_CODE1};
logError(0, "%s Try to unlock flash...\n", tag);
status = wait_for_flash_ready();
if (status != OK) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
//Flash not ready within the chosen time, better exit!
return (status | ERROR_FLASH_NOT_READY);
}
logError(0, "%s Command unlock ...\n", tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
status = wait_for_flash_ready();
if (status != OK) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
//Flash not ready within the chosen time,
//better exit!
return (status | ERROR_FLASH_NOT_READY);
}
logError(0, "%s Unlock flash DONE!\n", tag);
return OK;
}
int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx)
{
int dimension;
if (keep_cx) {
dimension = FW_SIZE - FW_CX_SIZE;
logError(1, "%s %s: Selected 124k Configuration!\n",
tag, __func__);
} else {
dimension = FW_SIZE;
logError(1, "%s %s: Selected 128k Configuration!\n",
tag, __func__);
}
if (fw_size - FW_HEADER_SIZE != FW_SIZE || fw_data == NULL) {
logError(1, "%s %s:Read only %d instead of %d... ERROR %02X\n",
tag, __func__,
fw_size - FW_HEADER_SIZE,
FW_SIZE, ERROR_FILE_PARSE);
kfree(fw_data);
return ERROR_FILE_PARSE;
}
fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), GFP_KERNEL);
if (fwData->data == NULL) {
logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC);
kfree(fw_data);
return ERROR_ALLOC;
}
memcpy(fwData->data, ((u8 *)(fw_data) + FW_HEADER_SIZE),
dimension);
fwData->data_size = dimension;
fwData->fw_ver = (u16)(((fwData->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8)
+ (fwData->data[FW_VER_MEMH_BYTE0] & 0x00FF));
fwData->config_id = (u16)(((fwData->data[(FW_CODE_SIZE)
+ FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8)
+ (fwData->data[(FW_CODE_SIZE) +
FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF));
logError(0, "%s %s: FW VERS File = %04X\n",
tag, __func__, fwData->fw_ver);
logError(0, "%s %s: CONFIG ID File = %04X\n",
tag, __func__, fwData->config_id);
logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size);
kfree(fw_data);
return OK;
}
int fillMemory(u32 address, u8 *data, int size)
{
int remaining = size;
int toWrite = 0;
int delta;
u8 *buff = (u8 *)kmalloc_array((MEMORY_CHUNK + 3), sizeof(u8),
GFP_KERNEL);
if (buff == NULL) {
logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
while (remaining > 0) {
if (remaining >= MEMORY_CHUNK) {
if ((address + MEMORY_CHUNK) < FLASH_ADDR_SWITCH_CMD) {
buff[0] = FLASH_CMD_WRITE_LOWER_64;
toWrite = MEMORY_CHUNK;
remaining -= MEMORY_CHUNK;
} else {
if (address < FLASH_ADDR_SWITCH_CMD) {
delta = FLASH_ADDR_SWITCH_CMD - address;
buff[0] = FLASH_CMD_WRITE_LOWER_64;
toWrite = delta;
remaining -= delta;
} else {
buff[0] = FLASH_CMD_WRITE_UPPER_64;
toWrite = MEMORY_CHUNK;
remaining -= MEMORY_CHUNK;
}
}
} else {
if ((address + remaining) < FLASH_ADDR_SWITCH_CMD) {
buff[0] = FLASH_CMD_WRITE_LOWER_64;
toWrite = remaining;
remaining = 0;
} else {
if (address < FLASH_ADDR_SWITCH_CMD) {
delta = FLASH_ADDR_SWITCH_CMD - address;
buff[0] = FLASH_CMD_WRITE_LOWER_64;
toWrite = delta;
remaining -= delta;
} else {
buff[0] = FLASH_CMD_WRITE_UPPER_64;
toWrite = remaining;
remaining = 0;
}
}
}
buff[1] = (u8) ((address & 0x0000FF00) >> 8);
buff[2] = (u8) (address & 0x000000FF);
memcpy(buff + 3, data, toWrite);
//logError(0,
//"%s Command = %02X , address = %02X %02X, bytes = %d\n",
//tag, buff[0], buff[1], buff[2], toWrite);
if (fts_writeCmd(buff, 3 + toWrite) < 0) {
logError(1, "%s %s: ERROR %02X\n",
tag, __func__, ERROR_I2C_W);
kfree(buff);
return ERROR_I2C_W;
}
address += toWrite;
data += toWrite;
}
kfree(buff);
return OK;
}
int flash_burn(Firmware *fw, int force_burn, int keep_cx)
{
u8 cmd;
int res;
if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver)
&& (ftsInfo.u16_cfgId >= fw->config_id)) {
logError(0, "Firmware in the chip newer");
logError(0, " or equal to the one to burn! ");
logError(0, "%s %s:NO UPDATE ERROR %02X\n",
tag, __func__, ERROR_FW_NO_UPDATE);
return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED);
}
//programming procedure start
logError(0, "%s Programming Procedure for flashing started:\n", tag);
logError(0, "%s 1) SYSTEM RESET:\n", tag);
res = fts_system_reset();
if (res < 0) {
logError(1, "%s system reset FAILED!\n", tag);
//if there is no firmware i will not
//get the controller ready event and
//there will be a timeout but i can
//keep going, but if there is
//an I2C error i have to exit
if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT))
return (res | ERROR_FLASH_BURN_FAILED);
} else
logError(0, "%s system reset COMPLETED!\n\n", tag);
logError(0, "%s 2) FLASH UNLOCK:\n", tag);
res = flash_unlock();
if (res < 0) {
logError(1, "%s flash unlock FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s flash unlock COMPLETED!\n\n", tag);
//Write the lower part of the Program RAM
logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag);
res = fillMemory(FLASH_ADDR_CODE, fw->data, fw->data_size);
if (res < 0) {
logError(1, "%s Error During filling the memory!%02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s Data copy COMPLETED!\n\n", tag);
logError(0, "%s 4) ERASE FLASH:\n", tag);
res = wait_for_flash_ready();
if (res < 0) {
logError(1, "%s Flash not ready! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s Command erase ...\n", tag);
cmd = FLASH_CMD_ERASE;
if (fts_writeCmd(&cmd, 1) < 0) {
logError(1, "%s Error during erasing flash! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED);
}
res = wait_for_flash_ready();
if (res < 0) {
logError(1, "%s Flash not ready 2! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s Flash erase COMPLETED!\n\n", tag);
logError(0, "%s 5) BURN FLASH:\n", tag);
logError(0, "%s Command burn ...\n", tag);
cmd = FLASH_CMD_BURN;
if (fts_writeCmd(&cmd, 1) < 0) {
logError(1, "%s Error during burning data! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED);
}
res = wait_for_flash_ready();
if (res < 0) {
logError(1, "%s Flash not ready! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s Flash burn COMPLETED!\n\n", tag);
logError(0, "%s 6) SYSTEM RESET:\n", tag);
res = fts_system_reset();
if (res < 0) {
logError(1, "%s system reset FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s system reset COMPLETED!\n\n", tag);
logError(0, "%s 7) FINAL CHECK:\n", tag);
res = readChipInfo(0);
if (res < 0) {
logError(1, "%s %s:Unable to retrieve Chip INFO!%02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
if ((ftsInfo.u16_fwVer != fw->fw_ver)
&& (ftsInfo.u16_cfgId != fw->config_id)) {
logError(1, "Firmware in the chip different");
logError(1, " from the one that was burn!");
logError(1, "%s fw: %x != %x , conf: %x != %x\n",
tag, ftsInfo.u16_fwVer,
fw->fw_ver,
ftsInfo.u16_cfgId,
fw->config_id);
return ERROR_FLASH_BURN_FAILED;
}
logError(0, "%s Final check OK! fw: %02X, conf: %02X\n",
tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId);
return OK;
}
#else
int wait_for_flash_ready(u8 type)
{
u8 cmd[2] = {FLASH_CMD_READ_REGISTER, type};
u8 readData = 0;
int i, res = -1;
logError(0, "%s Waiting for flash ready ...\n", tag);
for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) {
if (fts_readCmd(cmd, sizeof(cmd), &readData, 1) < 0) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_I2C_R);
} else {
res = readData & 0x80;
//logError(0, "%s flash status = %d\n", tag, res);
}
msleep(FLASH_WAIT_BEFORE_RETRY);
}
if (i == FLASH_RETRY_COUNT && res != 0) {
logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n",
tag, ERROR_TIMEOUT);
return ERROR_TIMEOUT;
}
logError(0, "%s Flash READY!\n", tag);
return OK;
}
int fts_warm_boot(void)
{
//write the command to perform the warm boot
u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE};
u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]);
logError(0, "%s Command warm boot ...\n", tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W);
return ERROR_I2C_W;
}
logError(0, "%s Warm boot DONE!\n", tag);
return OK;
}
int parseBinFile(u8 *data, int fw_size,
struct Firmware *fwData, int keep_cx)
{
int dimension, index = 0;
u32 temp;
int res, i;
//the file should contain at least the header plus the content_crc
if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN || data == NULL) {
logError(1, "%s %s:Read only %d instead of %d...ERROR %02X\n",
tag, __func__, fw_size,
FW_HEADER_SIZE + FW_BYTES_ALIGN,
ERROR_FILE_PARSE);
res = ERROR_FILE_PARSE;
goto END;
} else {
//start parsing of bytes
u8ToU32(&data[index], &temp);
if (temp != FW_HEADER_SIGNATURE) {
logError(1, "%s %s:Wrong Signature %08X...ERROR %02X\n",
tag, __func__, temp, ERROR_FILE_PARSE);
res = ERROR_FILE_PARSE;
goto END;
}
logError(0, "%s %s: Fw Signature OK!\n", tag, __func__);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
if (temp != FW_FTB_VER) {
logError(1, "%s %s:Wrong ftb_version %08X.ERROR %02X\n",
tag, __func__, temp, ERROR_FILE_PARSE);
res = ERROR_FILE_PARSE;
goto END;
}
logError(0, "%s %s:ftb_version OK!\n", __func__, tag);
index += FW_BYTES_ALIGN;
if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) {
logError(1, "%s %s:Wrong target %02X != %02X ",
tag, __func__, data[index]);
logError(1, "%%02X != %02X:%08X\n",
DCHIP_ID_0, data[index+1],
DCHIP_ID_1, ERROR_FILE_PARSE);
res = ERROR_FILE_PARSE;
goto END;
}
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
logError(0, "%s %s: Fw ID = %08X\n", tag, __func__, temp);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->fw_ver = temp;
logError(0, "%s %s:FILE Fw Version = %04X\n",
tag, __func__, fwData->fw_ver);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->config_id = temp;
logError(0, "%s %s:FILE Config ID = %04X\n",
tag, __func__, fwData->config_id);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
logError(0, "%s %s:Config Version = %08X\n",
tag, __func__, temp);
//skip reserved data
index += FW_BYTES_ALIGN * 2;
index += FW_BYTES_ALIGN;
logError(0, "%s %s:File External Release = ",
tag, __func__);
for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) {
fwData->externalRelease[i] = data[index++];
logError(0, "%02X", fwData->externalRelease[i]);
}
logError(0, "\n");
//index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->sec0_size = temp;
logError(0, "%s %s:sec0_size = %08X (%d bytes)\n",
tag, __func__, fwData->sec0_size, fwData->sec0_size);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->sec1_size = temp;
logError(0, "%s %s:sec1_size = %08X (%d bytes)\n",
tag, __func__, fwData->sec1_size, fwData->sec1_size);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->sec2_size = temp;
logError(0, "%s %s:sec2_size = %08X (%d bytes)\n",
tag, __func__, fwData->sec2_size, fwData->sec2_size);
index += FW_BYTES_ALIGN;
u8ToU32(&data[index], &temp);
fwData->sec3_size = temp;
logError(0, "%s %s:sec3_size = %08X (%d bytes)\n",
tag, __func__, fwData->sec3_size, fwData->sec3_size);
//skip header crc
index += FW_BYTES_ALIGN;
if (!keep_cx) {
dimension = fwData->sec0_size + fwData->sec1_size
+ fwData->sec2_size + fwData->sec3_size;
temp = fw_size;
} else {
//sec2 may contain cx data (future implementation)
//sec3 atm not used
dimension = fwData->sec0_size + fwData->sec1_size;
temp = fw_size - fwData->sec2_size - fwData->sec3_size;
fwData->sec2_size = 0;
fwData->sec3_size = 0;
}
if (dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN != temp) {
logError(1, "%s %s:Read only %d instead of %d...",
tag, __func__, fw_size,
dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN);
logError(1, "ERROR %02X\n", ERROR_FILE_PARSE);
res = ERROR_FILE_PARSE;
goto END;
}
fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8),
GFP_KERNEL);
if (fwData->data == NULL) {
logError(1, "%s %s: ERROR %02X\n",
tag, __func__, ERROR_ALLOC);
res = ERROR_ALLOC;
goto END;
}
index += FW_BYTES_ALIGN;
memcpy(fwData->data, &data[index], dimension);
fwData->data_size = dimension;
logError(0, "%s READ FW DONE %d bytes!\n",
tag, fwData->data_size);
res = OK;
goto END;
}
END:
kfree(data);
return res;
}
int flash_unlock(void)
{
//write the command to perform the unlock
u8 cmd[3] = {FLASH_CMD_UNLOCK,
FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1};
logError(0, "%s Command unlock ...\n", tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
//mdelay(FLASH_WAIT_TIME);
logError(0, "%s Unlock flash DONE!\n", tag);
return OK;
}
int flash_erase_unlock(void)
{
//write the command to perform
//the unlock for erasing the flash
u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0,
FLASH_ERASE_UNLOCK_CODE1};
logError(0, "%s Try to erase unlock flash...\n", tag);
logError(0, "%s Command erase unlock ...\n", tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
logError(0, "%s Erase Unlock flash DONE!\n", tag);
return OK;
}
int flash_full_erase(void)
{
int status;
//write the command to erase the flash
u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0,
FLASH_ERASE_CODE1};
logError(0, "%s Command full erase sent...\n",
tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
status = wait_for_flash_ready(FLASH_ERASE_CODE0);
if (status != OK) {
logError(1, "%s %s:ERROR % 02X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
//Flash not ready within the chosen time,
//better exit!
return (status | ERROR_FLASH_NOT_READY);
}
logError(0, "%s Full Erase flash DONE!\n", tag);
return OK;
}
int flash_erase_page_by_page(int keep_cx)
{
u8 status, i = 0;
//write the command to erase the flash
u8 cmd[4] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, 0x00, 0x00};
for (i = 0; i < FLASH_NUM_PAGE; i++) {
if (i >= FLASH_CX_PAGE_START && i <= FLASH_CX_PAGE_END
&& keep_cx == 1) {
logError(0, "%s Skipping erase page %d!\n", tag, i);
continue;
}
cmd[2] = (0x3F & i) | FLASH_ERASE_START;
logError(0, "Command erase page %d sent", i);
logError(0, "%s:%02X %02X %02X %02X\n",
tag, i, cmd[0], cmd[1], cmd[2], cmd[3]);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1,
"%s %s:ERROR % 08X\n",
tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
status = wait_for_flash_ready(FLASH_ERASE_CODE0);
if (status != OK) {
logError(1, "%s %s:ERROR % 08X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
//Flash not ready within the chosen time,
//better exit!
return (status | ERROR_FLASH_NOT_READY);
}
}
logError(0, "%s Erase flash page by page DONE!\n", tag);
return OK;
}
int start_flash_dma(void)
{
int status;
//write the command to erase the flash
u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0,
FLASH_DMA_CODE1};
logError(0, "%s Command flash DMA ...\n", tag);
if (fts_writeCmd(cmd, sizeof(cmd)) < 0) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_I2C_W);
return ERROR_I2C_W;
}
status = wait_for_flash_ready(FLASH_DMA_CODE0);
if (status != OK) {
logError(1, "%s %s: ERROR % 02X\n",
tag, __func__, ERROR_FLASH_NOT_READY);
//Flash not ready within the chosen time, better exit!
return (status | ERROR_FLASH_NOT_READY);
}
logError(0, "%s flash DMA DONE!\n", tag);
return OK;
}
int fillFlash(u32 address, u8 *data, int size)
{
int remaining = size;
int toWrite = 0;
int byteBlock = 0;
int wheel = 0;
u32 addr = 0;
int res;
int delta;
u8 *buff = NULL;
u8 buff2[9] = {0};
buff = (u8 *)kmalloc_array((DMA_CHUNK + 3), sizeof(u8), GFP_KERNEL);
if (buff == NULL) {
logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC);
return ERROR_ALLOC;
}
while (remaining > 0) {
byteBlock = 0;
addr = 0;
while (byteBlock < FLASH_CHUNK && remaining > 0) {
buff[0] = FLASH_CMD_WRITE_64K;
if (remaining >= DMA_CHUNK) {
if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) {
//logError(1, "%s fillFlash:1\n", tag);
toWrite = DMA_CHUNK;
remaining -= DMA_CHUNK;
byteBlock += DMA_CHUNK;
} else {
//logError(1, "%s fillFlash:2\n", tag);
delta = FLASH_CHUNK - byteBlock;
toWrite = delta;
remaining -= delta;
byteBlock += delta;
}
} else {
if ((byteBlock + remaining) <= FLASH_CHUNK) {
//logError(1, "%s fillFlash:3\n", tag);
toWrite = remaining;
byteBlock += remaining;
remaining = 0;
} else {
//logError(1, "%s fillFlash:4\n", tag);
delta = FLASH_CHUNK - byteBlock;
toWrite = delta;
remaining -= delta;
byteBlock += delta;
}
}
buff[1] = (u8) ((addr & 0x0000FF00) >> 8);
buff[2] = (u8) (addr & 0x000000FF);
memcpy(&buff[3], data, toWrite);
//logError(0,
//"%s Command = %02X, address = %02X %02X,
//bytes = %d, data = %02X %02X, %02X %02X\n",
//tag, buff[0], buff[1], buff[2], toWrite,
//buff[3], buff[4], buff[3 + toWrite-2],
//buff[3 + toWrite-1]);
if (fts_writeCmd(buff, 3 + toWrite) < 0) {
logError(1, "%s %s: ERROR %02X\n",
tag, __func__, ERROR_I2C_W);
kfree(buff);
return ERROR_I2C_W;
}
addr += toWrite;
data += toWrite;
}
//configuring the DMA
byteBlock = byteBlock / 4 - 1;
buff2[0] = FLASH_CMD_WRITE_REGISTER;
buff2[1] = FLASH_DMA_CONFIG;
buff2[2] = 0x00;
buff2[3] = 0x00;
addr = address + ((wheel * FLASH_CHUNK)/4);
buff2[4] = (u8) ((addr & 0x000000FF));
buff2[5] = (u8) ((addr & 0x0000FF00) >> 8);
buff2[6] = (u8) (byteBlock & 0x000000FF);
buff2[7] = (u8) ((byteBlock & 0x0000FF00) >> 8);
buff2[8] = 0x00;
logError(0, "%s:Command:%02X, address:%02X %02X, ",
tag, buff2[0], buff2[5], buff2[4]);
logError(0, "words:%02X %02X\n", buff2[7], buff2[6]);
if (fts_writeCmd(buff2, 9) < OK) {
logError(1, "%s Error during filling Flash!:%02X\n",
tag, ERROR_I2C_W);
kfree(buff);
return ERROR_I2C_W;
}
//mdelay(FLASH_WAIT_TIME);
res = start_flash_dma();
if (res < OK) {
logError(1, "%s Error during flashing DMA!:%02X\n",
tag, res);
kfree(buff);
return res;
}
wheel++;
}
kfree(buff);
return OK;
}
int flash_burn(struct Firmware *fw, int force_burn, int keep_cx)
{
int res;
if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver)
&& (ftsInfo.u16_cfgId >= fw->config_id)) {
for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) {
if (fw->externalRelease[res] >
ftsInfo.u8_extReleaseInfo[res])
goto start;
}
logError(0, "Firmware in the chip newer or ");
logError(0, "equal to the one to burn!");
logError(0, "%s %s:NO UPDATE ERROR %02X\n",
tag, __func__, ERROR_FW_NO_UPDATE);
return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED);
}
//programming procedure start
start:
logError(0, "%s Programming Procedure for flashing started:\n\n", tag);
logError(0, "%s 1) SYSTEM RESET:\n", tag);
logError(0, "%s 2) WARM BOOT:\n", tag);
res = fts_warm_boot();
if (res < OK) {
logError(1, "%s warm boot FAILED!\n", tag);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s warm boot COMPLETED!\n\n", tag);
//mdelay(FLASH_WAIT_TIME);
logError(0, "%s 3) FLASH UNLOCK:\n", tag);
res = flash_unlock();
if (res < OK) {
logError(1, "%s flash unlock FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s flash unlock COMPLETED!\n\n", tag);
//mdelay(200);
logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag);
res = flash_erase_unlock();
if (res < 0) {
logError(1, "%s flash unlock FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s flash unlock COMPLETED!\n\n", tag);
//mdelay(FLASH_WAIT_TIME);
logError(0, "%s 5) FLASH ERASE:\n", tag);
if (keep_cx == 1)
res = flash_erase_page_by_page(keep_cx);
else
res = flash_full_erase();
if (res < 0) {
logError(1, "%s flash erase FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s flash erase COMPLETED!\n\n", tag);
//mdelay(FLASH_WAIT_TIME);
logError(0, "%s 6) LOAD PROGRAM:\n", tag);
res = fillFlash(FLASH_ADDR_CODE, (u8 *)(&fw->data[0]),
fw->sec0_size);
if (res < OK) {
logError(1, "%s load program ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s load program DONE!\n", tag);
logError(0, "%s 7) LOAD CONFIG:\n", tag);
res = fillFlash(FLASH_ADDR_CONFIG,
&(fw->data[fw->sec0_size]), fw->sec1_size);
if (res < OK) {
logError(1, "%s load config ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s load config DONE!\n", tag);
logError(0, "%s Flash burn COMPLETED!\n\n", tag);
logError(0, "%s 8) SYSTEM RESET:\n", tag);
res = fts_system_reset();
if (res < 0) {
logError(1, "%s system reset FAILED! ERROR %02X\n",
tag, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
logError(0, "%s system reset COMPLETED!\n\n", tag);
logError(0, "%s 9) FINAL CHECK:\n", tag);
res = readChipInfo(0);
if (res < 0) {
logError(1, "%s %s:Unable to retrieve Chip INFO!:%02X\n",
tag, __func__, ERROR_FLASH_BURN_FAILED);
return (res | ERROR_FLASH_BURN_FAILED);
}
if ((ftsInfo.u16_fwVer != fw->fw_ver)
&& (ftsInfo.u16_cfgId != fw->config_id)) {
pr_err("Firmware is different from the old!\n");
logError(1, "%s fw: %x != %x, conf: %x != %x\n",
tag, ftsInfo.u16_fwVer, fw->fw_ver,
ftsInfo.u16_cfgId, fw->config_id);
return ERROR_FLASH_BURN_FAILED;
}
logError(0, "%s Final check OK! fw: %02X , conf: %02X\n",
tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId);
return OK;
}
#endif