blob: 2ae433715bff184fad99b1cffd5c50611bfc488a [file] [log] [blame]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
** marco.cali@st.com **
**************************************************************************
* *
* FTS error/info kernel log reporting *
* *
**************************************************************************
**************************************************************************
*
*/
/*!
* \file ftsError.c
* \brief Contains all the function which handle with Error conditions
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "../fts.h"
#include "ftsCore.h"
#include "ftsError.h"
#include "ftsIO.h"
#include "ftsTool.h"
#include "ftsCompensation.h"
static ErrorList errors;/* /< private variable which implement the Error List */
/**
* Check if an error code is related to an I2C failure
* @param error error code to check
* @return 1 if the first level error code is I2C related otherwise 0
*/
int isI2cError(int error)
{
if (((error & 0x000000FF) >= (ERROR_BUS_R & 0x000000FF)) &&
((error & 0x000000FF) <= (ERROR_BUS_O & 0x000000FF)))
return 1;
else
return 0;
}
/**
* Dump in the kernel log some debug info in case of FW hang
* @param outBuf (optional)pointer to bytes array where to copy the debug info,
* if NULL the data will just printed on the kernel log
* @param size dimension in bytes of outBuf,
* if > ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE, only the first
* ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE bytes will be copied
* @return OK if success or an error code which specify the type of error
*/
int dumpErrorInfo(u8 *outBuf, int size)
{
int ret, i;
u8 data[ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE] = { 0 };
u32 sign = 0;
pr_err("%s: Starting dump of error info...\n", __func__);
ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, ADDR_ERROR_DUMP,
data, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE,
DUMMY_FRAMEBUFFER);
if (ret < OK) {
pr_err("%s: reading data ERROR %08X\n", __func__,
ret);
return ret;
} else {
int buff_len, index = 0;
char *buff;
buff_len = (2 + 1) * ERROR_DUMP_COL_SIZE + 1;
buff = kzalloc(buff_len, GFP_KERNEL);
if (buff == NULL) {
pr_err("%s: fail to allocate buffer\n", __func__);
return -ENOMEM;
}
if (outBuf != NULL) {
sign = size > ERROR_DUMP_ROW_SIZE *
ERROR_DUMP_COL_SIZE ? ERROR_DUMP_ROW_SIZE *
ERROR_DUMP_COL_SIZE : size;
memcpy(outBuf, data, sign);
pr_err("%s: error info copied in the buffer!\n",
__func__);
}
pr_err("%s: Error Info =\n", __func__);
u8ToU32(data, &sign);
if (sign != ERROR_DUMP_SIGNATURE)
pr_err("%s: Wrong Error Signature! Data may be invalid!\n",
__func__);
else
pr_err("%s: Error Signature OK! Data are valid!\n",
__func__);
for (i = 0; i < ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE;
i++) {
index += scnprintf(buff + index, buff_len - index,
"%02X ", data[i]);
if (i % ERROR_DUMP_COL_SIZE ==
(ERROR_DUMP_COL_SIZE - 1)) {
pr_err("%s: %d) %s\n", __func__,
i / ERROR_DUMP_COL_SIZE,
buff);
index = 0;
}
}
kfree(buff);
pr_err("%s: dump of error info FINISHED!\n", __func__);
return OK;
}
}
/**
* Implement recovery strategies to be used when an error event is found
* while polling the FIFO
* @param event error event found during the polling
* @param size size of event
* @return OK if the error event doesn't require any action or the recovery
* strategy doesn't have any impact in the possible procedure that trigger the
* error,
* otherwise return an error code which specify the kind of error.
* If ERROR_HANDLER_STOP_PROC the calling function must stop!
*/
int errorHandler(u8 *event, int size)
{
int res = OK;
struct fts_ts_info *info = NULL;
if (getDev() != NULL)
info = dev_get_drvdata(getDev());
if (info != NULL && event != NULL && size > 1 &&
event[0] == EVT_ID_ERROR) {
pr_debug("errorHandler: Starting handling...\n");
addErrorIntoList(event, size);
switch (event[1]) { /* TODO: write an error log for
* undefined command subtype 0xBA */
case EVT_TYPE_ERROR_ESD: /* esd */
res = fts_chip_powercycle(info);
if (res < OK)
pr_err("errorHandler: Error performing powercycle ERROR %08X\n",
res);
res = fts_system_reset();
if (res < OK)
pr_err("errorHandler: Cannot reset the device ERROR %08X\n",
res);
res = (ERROR_HANDLER_STOP_PROC | res);
break;
case EVT_TYPE_ERROR_WATCHDOG: /* watchdog */
dumpErrorInfo(NULL, 0);
res = fts_system_reset();
if (res < OK)
pr_err("errorHandler: Cannot reset the device ERROR %08X\n",
res);
res = (ERROR_HANDLER_STOP_PROC | res);
break;
case EVT_TYPE_ERROR_ITO_FORCETOGND:
pr_err("errorHandler: Force Short to GND!\n");
break;
case EVT_TYPE_ERROR_ITO_SENSETOGND:
pr_err("errorHandler: Sense short to GND!\n");
break;
case EVT_TYPE_ERROR_ITO_FORCETOVDD:
pr_err("errorHandler: Force short to VDD!\n");
break;
case EVT_TYPE_ERROR_ITO_SENSETOVDD:
pr_err("errorHandler: Sense short to VDD!\n");
break;
case EVT_TYPE_ERROR_ITO_FORCE_P2P:
pr_err("errorHandler: Force Pin to Pin Short!\n");
break;
case EVT_TYPE_ERROR_ITO_SENSE_P2P:
pr_err("errorHandler: Sense Pin to Pin Short!\n");
break;
case EVT_TYPE_ERROR_ITO_FORCEOPEN:
pr_err("errorHandler: Force Open !\n");
break;
case EVT_TYPE_ERROR_ITO_SENSEOPEN:
pr_err("errorHandler: Sense Open !\n");
break;
case EVT_TYPE_ERROR_ITO_KEYOPEN:
pr_err("errorHandler: Key Open !\n");
break;
case EVT_TYPE_ERROR_FLASH_FAILED:
pr_err("errorHandler: Previous flash failed!\n");
info->reflash_fw = 1;
break;
default:
pr_debug("errorHandler: No Action taken!\n");
break;
}
pr_debug("errorHandler: handling Finished! res = %08X\n",
res);
return res;
} else {
pr_err("errorHandler: event Null or not correct size! ERROR %08X\n",
ERROR_OP_NOT_ALLOW);
return ERROR_OP_NOT_ALLOW;
}
}
/**
* Add an error event into the Error List
* @param event error event to add
* @param size size of event
* @return OK
*/
int addErrorIntoList(u8 *event, int size)
{
int i = 0;
pr_debug("Adding error in to ErrorList...\n");
memcpy(&errors.list[errors.last_index * FIFO_EVENT_SIZE], event, size);
i = FIFO_EVENT_SIZE - size;
if (i > 0) {
pr_info("Filling last %d bytes of the event with zero...\n", i);
memset(&errors.list[errors.last_index * FIFO_EVENT_SIZE + size],
0, i);
}
pr_debug("Adding error in to ErrorList... FINISHED!\n");
errors.count += 1;
if (errors.count > FIFO_DEPTH)
pr_err("ErrorList is going in overflow... the first %d event(s) were override!\n",
errors.count - FIFO_DEPTH);
errors.last_index = (errors.last_index + 1) % FIFO_DEPTH;
return OK;
}
/**
* Reset the Error List setting the count and last_index to 0.
* @return OK
*/
int resetErrorList(void)
{
errors.count = 0;
errors.last_index = 0;
memset(errors.list, 0, FIFO_DEPTH * FIFO_EVENT_SIZE);
/* if count is not considered is better reset also the list in order to
* avoid to read data previously copied into the list */
return OK;
}
/**
* Get the number of error events copied into the Error List
* @return the number of error events into the Error List
*/
int getErrorListCount(void)
{
if (errors.count > FIFO_DEPTH)
return FIFO_DEPTH;
else
return errors.count;
}
/* in case of success return the index of the event found */
/**
* Scroll the Error List looking for the event specified
* @param event_to_search event_to_search pointer to an array of int where
* each element correspond to a byte of the event to find. If the element
* of the array has value -1, the byte of the event, in the same position
* of the element is ignored.
* @param event_bytes size of event_to_search
* @return a value >=0 if the event is found which represent the index of
* the Error List where the event is located otherwise an error code
*/
int pollErrorList(int *event_to_search, int event_bytes)
{
int i = 0, j = 0, find = 0;
int count = getErrorListCount();
pr_debug("Starting to poll ErrorList...\n");
while (find != 1 && i < count) {
find = 1;
for (j = 0; j < event_bytes; j++) {
if ((event_to_search[i] != -1) &&
((int)errors.list[i * FIFO_EVENT_SIZE + j] !=
event_to_search[i])) {
find = 0;
break;
}
}
i++;
}
if (find == 1) {
pr_debug("Error Found into ErrorList!\n");
return i - 1; /* there is i++ at the end of the while */
} else {
pr_err("Error Not Found into ErrorList! ERROR %08X\n",
ERROR_TIMEOUT);
return ERROR_TIMEOUT;
}
}
/**
* Poll the Error List looking for any error types passed in the arguments.
* Return at the first match!
* @param list pointer to a list of error types to look for
* @param size size of list
* @return error type found if success or ERROR_TIMEOUT
*/
int pollForErrorType(u8 *list, int size)
{
int i = 0, j = 0, find = 0;
int count = getErrorListCount();
pr_info("%s: Starting to poll ErrorList... count = %d\n",
__func__, count);
while (find != 1 && i < count) {
for (j = 0; j < size; j++) {
if (list[j] == errors.list[i * FIFO_EVENT_SIZE + 1]) {
find = 1;
break;
}
}
i++;
}
if (find == 1) {
pr_info("%s: Error Type %02X into ErrorList!\n",
__func__, list[j]);
return list[j];
} else {
pr_err("%s: Error Type Not Found into ErrorList! ERROR %08X\n",
__func__, ERROR_TIMEOUT);
return ERROR_TIMEOUT;
}
}