492 lines
14 KiB
C
492 lines
14 KiB
C
/*
|
|
*
|
|
* FocalTech TouchScreen driver.
|
|
*
|
|
* Copyright (c) 2010-2017, 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
|
|
* v1.1: By luougojin 2017-02-15
|
|
* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Included header files
|
|
*****************************************************************************/
|
|
#include "focaltech_core.h"
|
|
|
|
#if FTS_ESDCHECK_EN
|
|
/*****************************************************************************
|
|
* Private constant and macro definitions using #define
|
|
*****************************************************************************/
|
|
#define ESDCHECK_WAIT_TIME 1000 //ms
|
|
#define LCD_ESD_PATCH 0
|
|
|
|
/*****************************************************************************
|
|
* Private enumerations, structures and unions using typedef
|
|
*****************************************************************************/
|
|
struct fts_esdcheck_st
|
|
{
|
|
u8 active : 1; /* 1- esd check active, need check esd 0- no esd check */
|
|
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; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */
|
|
u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */
|
|
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 = NULL;
|
|
static struct fts_esdcheck_st fts_esdcheck_data;
|
|
|
|
/*****************************************************************************
|
|
* Global variable or extern global variabls/functions
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* Static function prototypes
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* functions body
|
|
*****************************************************************************/
|
|
#if LCD_ESD_PATCH
|
|
/*****************************************************************************
|
|
* 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;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* 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, ®_addr, 1, ®_value, 1);
|
|
|
|
if ( err < 0 )
|
|
{
|
|
FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!", err);
|
|
fts_esdcheck_data.i2c_nack_cnt++;
|
|
}
|
|
else
|
|
{
|
|
if ( (reg_value == chip_types.chip_idh) || (reg_value == 0xEF) ) /* Upgrade sometimes can't detect */
|
|
{
|
|
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, ®_addr, 1, ®_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, ®_addr, 1, ®_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. IDC esd check lcd default:close */
|
|
#if LCD_ESD_PATCH
|
|
idc_esdcheck_lcderror();
|
|
#endif
|
|
|
|
/* 6. Get Chip ID */
|
|
hardware_reset = get_chip_id();
|
|
|
|
/* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */
|
|
if (!hardware_reset)
|
|
{
|
|
hardware_reset = get_flow_cnt();
|
|
}
|
|
|
|
/* 8. 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();
|
|
|
|
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 destory 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 */
|
|
|