blob: 565507d0252319a3609187cb4332e60bc0d65f26 [file] [log] [blame]
/*
*
* FocalTech TouchScreen driver.
*
* Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
/*****************************************************************************
*
* File Name: focaltech_esdcheck.c
*
* Author: luoguojin
*
* Created: 2016-08-03
*
* Abstract: ESD check function
*
* Version: v1.0
*
* Revision History:
* v1.0:
* First release. By luougojin 2016-08-03
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"
#if FTS_ESDCHECK_EN
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define ESDCHECK_WAIT_TIME 1000
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
struct fts_esdcheck_st {
u8 active:1;
u8 suspend:1;
u8 proc_debug:1; /* apk or adb is accessing I2C */
u8 intr:1; /* 1- Interrupt trigger */
u8 unused:4;
u8 flow_work_hold_cnt;
u8 flow_work_cnt_last;
u32 hardware_reset_cnt;
u32 i2c_nack_cnt;
u32 i2c_dataerror_cnt;
};
/*****************************************************************************
* Static variables
*****************************************************************************/
static struct delayed_work fts_esdcheck_work;
static struct workqueue_struct *fts_esdcheck_workqueue;
static struct fts_esdcheck_st fts_esdcheck_data;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
/*****************************************************************************
* functions body
*****************************************************************************/
/*****************************************************************************
* Name: lcd_esdcheck
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
int lcd_need_reset;
static int tp_need_recovery; /* LCD reset cause Tp reset */
int idc_esdcheck_lcderror(void)
{
u8 val;
int ret;
FTS_DEBUG("[ESD]Check LCD ESD");
if ((tp_need_recovery == 1) && (lcd_need_reset == 0)) {
tp_need_recovery = 0;
/* LCD reset, need recover TP state */
fts_tp_state_recovery(fts_i2c_client);
}
ret = fts_i2c_read_reg(fts_i2c_client, FTS_REG_ESD_SATURATE, &val);
if (ret < 0) {
FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret);
return -EIO;
}
if (val == 0xAA) {
/*
* 1. Set flag lcd_need_reset = 1;
* 2. LCD driver need reset(recovery) LCD and
* set lcd_need_reset to 0
* 3. recover TP state
*/
FTS_INFO("LCD ESD, Execute LCD reset!");
lcd_need_reset = 1;
tp_need_recovery = 1;
}
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_tp_reset
* Brief: esd check algorithm
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_esdcheck_tp_reset(void)
{
FTS_FUNC_ENTER();
fts_esdcheck_data.flow_work_hold_cnt = 0;
fts_esdcheck_data.hardware_reset_cnt++;
fts_reset_proc(200);
fts_tp_state_recovery(fts_i2c_client);
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: get_chip_id
* Brief: Read Chip Id 3 times
* Input:
* Output:
* Return: 1 - Read Chip Id 3 times failed
* 0 - Read Chip Id pass
*****************************************************************************/
static bool get_chip_id(void)
{
int err = 0;
int i = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
for (i = 0; i < 3; i++) {
reg_addr = FTS_REG_CHIP_ID;
err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
if (err < 0) {
FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!",
err);
fts_esdcheck_data.i2c_nack_cnt++;
} else {
/* Upgrade sometimes can't detect */
if ((reg_value == chip_types.chip_idh)
|| (reg_value == 0xEF))
break;
else
fts_esdcheck_data.i2c_dataerror_cnt++;
}
}
/* if can't get correct data in 3 times, then need hardware reset */
if (i >= 3) {
FTS_ERROR
("[ESD]: Read Chip id 3 times failed,"
"need execute TP reset!!");
return 1;
}
return 0;
}
/*****************************************************************************
* Name: get_flow_cnt
* Brief: Read flow cnt(0x91)
* Input:
* Output:
* Return: 1 - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
* 0 - Reg 0x91(flow cnt) normal
*****************************************************************************/
static bool get_flow_cnt(void)
{
int err = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
reg_addr = FTS_REG_FLOW_WORK_CNT;
err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
if (err < 0) {
FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", err);
fts_esdcheck_data.i2c_nack_cnt++;
} else {
if (reg_value == fts_esdcheck_data.flow_work_cnt_last)
fts_esdcheck_data.flow_work_hold_cnt++;
else
fts_esdcheck_data.flow_work_hold_cnt = 0;
fts_esdcheck_data.flow_work_cnt_last = reg_value;
}
/*
* if read flow work cnt 5 times and the value are
* all the same, then need hardware_reset
*/
if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
FTS_DEBUG("[ESD]: Flow Work Cnt(reg0x91) keep a value"
"for 5 times, need execute TP reset!!");
return 1;
}
return 0;
}
/*****************************************************************************
* Name: esdcheck_algorithm
* Brief: esd check algorithm
* Input:
* Output:
* Return:
*****************************************************************************/
static int esdcheck_algorithm(void)
{
int err = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
bool hardware_reset = 0;
/* 1. esdcheck is interrupt, then return */
if (fts_esdcheck_data.intr == 1) {
FTS_INFO("[ESD]: In interrupt state,"
"not check esd, return immediately!!");
return 0;
}
/* 2. check power state, if suspend, no need check esd */
if (fts_esdcheck_data.suspend == 1) {
FTS_INFO("[ESD]: In suspend, not check esd,"
"return immediately!!");
/* because in suspend state, adb can be used,
* when upgrade FW, will active ESD check(active = 1)
* But in suspend, then will don't queue_delayed_work,
* when resume, don't check ESD again
*/
fts_esdcheck_data.active = 0;
return 0;
}
/*
* 3. check fts_esdcheck_data.proc_debug state,
* if 1-proc busy, no need check esd
*/
if (fts_esdcheck_data.proc_debug == 1) {
FTS_INFO("[ESD]: In apk or adb command mode,"
"not check esd, return immediately!!");
return 0;
}
/* 4. In factory mode, can't check esd */
reg_addr = FTS_REG_WORKMODE;
err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
if (err < 0) {
fts_esdcheck_data.i2c_nack_cnt++;
} else if ((reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) {
FTS_INFO("[ESD]: In factory mode,"
"not check esd, return immediately!!");
return 0;
}
/* 5. Get Chip ID */
hardware_reset = get_chip_id();
/*
* 6. get Flow work cnt: 0x91 If no change for 5 times,
* then ESD and reset
*/
if (!hardware_reset)
hardware_reset = get_flow_cnt();
/* 7. If need hardware reset, then handle it here */
if (hardware_reset == 1)
fts_esdcheck_tp_reset();
FTS_INFO("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d\n",
fts_esdcheck_data.i2c_nack_cnt,
fts_esdcheck_data.i2c_dataerror_cnt,
fts_esdcheck_data.hardware_reset_cnt);
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_func
* Brief: fts_esdcheck_func
* Input:
* Output:
* Return:
*****************************************************************************/
static void esdcheck_func(struct work_struct *work)
{
FTS_FUNC_ENTER();
idc_esdcheck_lcderror();
esdcheck_algorithm();
if (fts_esdcheck_data.suspend == 0) {
queue_delayed_work(fts_esdcheck_workqueue, &fts_esdcheck_work,
msecs_to_jiffies(ESDCHECK_WAIT_TIME));
}
FTS_FUNC_EXIT();
}
/*****************************************************************************
* Name: fts_esdcheck_set_intr
* Brief: interrupt flag (main used in interrupt tp report)
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_set_intr(bool intr)
{
/* interrupt don't add debug message */
fts_esdcheck_data.intr = intr;
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_get_status(void)
* Brief: get current status
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_get_status(void)
{
/* interrupt don't add debug message */
return fts_esdcheck_data.active;
}
/*****************************************************************************
* Name: fts_esdcheck_proc_busy
* Brief: When APK or ADB command access TP via driver,
* then need set proc_debug,
* then will not check ESD.
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_proc_busy(bool proc_debug)
{
fts_esdcheck_data.proc_debug = proc_debug;
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_switch
* Brief: FTS esd check function switch.
* Input: enable: 1 - Enable esd check
* 0 - Disable esd check
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_switch(bool enable)
{
FTS_FUNC_ENTER();
if (enable == 1) {
if (fts_esdcheck_data.active == 0) {
FTS_INFO("[ESD]: ESD check start!!");
fts_esdcheck_data.active = 1;
queue_delayed_work(fts_esdcheck_workqueue,
&fts_esdcheck_work,
msecs_to_jiffies
(ESDCHECK_WAIT_TIME));
}
} else {
if (fts_esdcheck_data.active == 1) {
FTS_INFO("[ESD]: ESD check stop!!");
fts_esdcheck_data.active = 0;
cancel_delayed_work_sync(&fts_esdcheck_work);
}
}
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_suspend
* Brief: Run when tp enter into suspend
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_suspend(void)
{
FTS_FUNC_ENTER();
fts_esdcheck_switch(DISABLE);
fts_esdcheck_data.suspend = 1;
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_resume
* Brief: Run when tp resume
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_resume(void)
{
FTS_FUNC_ENTER();
fts_esdcheck_switch(ENABLE);
fts_esdcheck_data.suspend = 0;
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_init
* Brief: Init and create a queue work to check esd
* Input:
* Output:
* Return: < 0: Fail to create esd check queue
*****************************************************************************/
int fts_esdcheck_init(void)
{
FTS_FUNC_ENTER();
INIT_DELAYED_WORK(&fts_esdcheck_work, esdcheck_func);
fts_esdcheck_workqueue = create_workqueue("fts_esdcheck_wq");
if (fts_esdcheck_workqueue == NULL)
FTS_INFO("[ESD]: Failed to create esd work queue!!");
memset((u8 *) &fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
fts_esdcheck_switch(ENABLE);
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_exit
* Brief: When FTS TP driver is removed,
* then call this function to destroy work queue
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_exit(void)
{
FTS_FUNC_ENTER();
destroy_workqueue(fts_esdcheck_workqueue);
FTS_FUNC_EXIT();
return 0;
}
#endif /* FTS_ESDCHECK_EN */