/* * * 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 */