| /* |
| * Goodix Touchscreen Driver |
| * Copyright (C) 2020 - 2021 Goodix, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be a reference |
| * to you, when you are integrating the GOODiX's CTP IC into your system, |
| * 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. |
| * |
| */ |
| |
| #include "goodix_ts_core.h" |
| #include <asm/uaccess.h> |
| #include <linux/fs.h> |
| #include <linux/rtc.h> |
| #include <linux/timer.h> |
| #include <linux/version.h> |
| |
| /* test config */ |
| #define TOTAL_FRAME_NUM 1 /* rawdata test frames */ |
| #define NOISEDATA_TEST_TIMES 1 /* noise test frames */ |
| |
| #define GOODIX_RESULT_SAVE_PATH "/vendor/etc/Test_Data.csv" |
| #define GOODIX_TEST_FILE_NAME "goodix" |
| #define MAX_DATA_BUFFER 28000 |
| #define MAX_SHORT_NUM 15 |
| #define MAX_LINE_LEN (1024 * 3 * 7) |
| #define MAX_DRV_NUM 52 |
| #define MAX_SEN_NUM 75 |
| |
| #define STATISTICS_DATA_LEN 32 |
| #define MAX_STR_LEN 32 |
| #define MAX_TEST_ITEMS 10 /* 0P-1P-2P-3P-5P total test items */ |
| #define GTP_CAP_TEST 1 |
| #define GTP_DELTA_TEST 2 |
| #define GTP_NOISE_TEST 3 |
| #define GTP_OPEN_TEST 4 |
| #define GTP_SHORT_TEST 5 |
| #define GTP_SELFCAP_TEST 6 |
| #define GTP_SELFNOISE_TEST 7 |
| |
| #define GTP_TEST_PASS 1 |
| #define GTP_PANEL_REASON 2 |
| #define SYS_SOFTWARE_REASON 3 |
| |
| #define CHN_VDD 0xFF |
| #define CHN_GND 0x7F |
| #define DRV_CHANNEL_FLAG 0x80 |
| |
| #define CSV_TP_SPECIAL_RAW_MIN "special_raw_min" |
| #define CSV_TP_SPECIAL_RAW_MAX "special_raw_max" |
| #define CSV_TP_SPECIAL_RAW_DELTA "special_raw_delta" |
| #define CSV_TP_SHORT_THRESHOLD "shortciurt_threshold" |
| #define CSV_TP_SPECIAL_SELFRAW_MAX "special_selfraw_max" |
| #define CSV_TP_SPECIAL_SELFRAW_MIN "special_selfraw_min" |
| #define CSV_TP_NOISE_LIMIT "noise_data_limit" |
| #define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit" |
| #define CSV_TP_TEST_CONFIG "test_config" |
| |
| #define MAX_TEST_TIME_MS 15000 |
| #define DEFAULT_TEST_TIME_MS 7000 |
| |
| /* berlin A */ |
| #define MAX_DRV_NUM_BRA 21 |
| #define MAX_SEN_NUM_BRA 42 |
| #define SHORT_TEST_TIME_REG_BRA 0x11FF2 |
| #define DFT_ADC_DUMP_NUM_BRA 1396 |
| #define DFT_SHORT_THRESHOLD_BRA 16 |
| #define DFT_DIFFCODE_SHORT_THRESHOLD_BRA 16 |
| #define SHORT_TEST_STATUS_REG_BRA 0x10400 |
| #define SHORT_TEST_RESULT_REG_BRA 0x10410 |
| #define DRV_DRV_SELFCODE_REG_BRA 0x1045E |
| #define SEN_SEN_SELFCODE_REG_BRA 0x1084E |
| #define DRV_SEN_SELFCODE_REG_BRA 0x11712 |
| #define DIFF_CODE_DATA_REG_BRA 0x11F72 |
| |
| /* berlin B */ |
| #define MAX_DRV_NUM_BRB 52 |
| #define MAX_SEN_NUM_BRB 75 |
| #define SHORT_TEST_TIME_REG_BRB 0x26AE0 |
| #define DFT_ADC_DUMP_NUM_BRB 762 |
| #define DFT_SHORT_THRESHOLD_BRB 100 |
| #define DFT_DIFFCODE_SHORT_THRESHOLD_BRB 32 |
| #define SHORT_TEST_STATUS_REG_BRB 0x20400 |
| #define SHORT_TEST_RESULT_REG_BRB 0x20410 |
| #define DRV_DRV_SELFCODE_REG_BRB 0x2049A |
| #define SEN_SEN_SELFCODE_REG_BRB 0x21AF2 |
| #define DRV_SEN_SELFCODE_REG_BRB 0x248A6 |
| #define DIFF_CODE_DATA_REG_BRB 0x269E0 |
| |
| /* berlinD */ |
| #define MAX_DRV_NUM_BRD 20 |
| #define MAX_SEN_NUM_BRD 40 |
| #define SHORT_TEST_TIME_REG_BRD 0x14D7A |
| #define DFT_ADC_DUMP_NUM_BRD 762 |
| #define DFT_SHORT_THRESHOLD_BRD 100 |
| #define DFT_DIFFCODE_SHORT_THRESHOLD_BRD 32 |
| #define SHORT_TEST_STATUS_REG_BRD 0x13400 |
| #define SHORT_TEST_RESULT_REG_BRD 0x13408 |
| #define DRV_DRV_SELFCODE_REG_BRD 0x1344E |
| #define SEN_SEN_SELFCODE_REG_BRD 0x137E6 |
| #define DRV_SEN_SELFCODE_REG_BRD 0x14556 |
| #define DIFF_CODE_DATA_REG_BRD 0x14D00 |
| |
| /* nottingham */ |
| #define MAX_DRV_NUM_NOT 17 |
| #define MAX_SEN_NUM_NOT 35 |
| #define SHORT_TEST_TIME_REG_NOT 0x1479E |
| #define SHORT_TEST_STATUS_REG_NOT 0x13400 |
| #define SHORT_TEST_RESULT_REG_NOT 0x13408 |
| #define DRV_DRV_SELFCODE_REG_NOT 0x13446 |
| #define SEN_SEN_SELFCODE_REG_NOT 0x136EE |
| #define DRV_SEN_SELFCODE_REG_NOT 0x14152 |
| #define DIFF_CODE_DATA_REG_NOT 0x14734 |
| |
| #define ABS(val) ((val < 0) ? -(val) : val) |
| #define MAX(a, b) ((a > b) ? a : b) |
| |
| /* short threshold, drv-drv, drv-sen, sen-sen, drv-gnd, sen-gnd, avdd */ |
| static u8 short_circuit_threshold[] = { 10, 200, 200, 200, 200, 200, 30 }; |
| |
| /* berlin A drv-sen map */ |
| static u8 brl_a_drv_map[] = { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, |
| 54, 55, 56, 57, 58, 59, 60, 61, 62 }; |
| |
| static u8 brl_a_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
| 33, 34, 35, 36, 37, 38, 39, 40, 41 }; |
| |
| /* berlin B drv-sen map */ |
| static u8 brl_b_drv_map[] = { 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, |
| 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, |
| 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, |
| 118, 119, 120, 121, 122, 123, 124, 125, 126 }; |
| |
| static u8 brl_b_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
| 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, |
| 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, |
| 69, 70, 71, 72, 73, 74 }; |
| |
| /* berlin D drv-sen map */ |
| static u8 brl_d_drv_map[] = { |
| 40, |
| 41, |
| 42, |
| 43, |
| 44, |
| 45, |
| 46, |
| 47, |
| 48, |
| 49, |
| 50, |
| 51, |
| 52, |
| 53, |
| 54, |
| 55, |
| 56, |
| 57, |
| 58, |
| 59, |
| }; |
| |
| static u8 brl_d_sen_map[] = { |
| 0, |
| 1, |
| 2, |
| 3, |
| 4, |
| 5, |
| 6, |
| 7, |
| 8, |
| 9, |
| 10, |
| 11, |
| 12, |
| 13, |
| 14, |
| 15, |
| 16, |
| 17, |
| 18, |
| 19, |
| 20, |
| 21, |
| 22, |
| 23, |
| 24, |
| 25, |
| 26, |
| 27, |
| 28, |
| 29, |
| 30, |
| 31, |
| 32, |
| 33, |
| 34, |
| 35, |
| 36, |
| 37, |
| 38, |
| 39, |
| }; |
| |
| /* nottingham drv-sen map */ |
| static u8 not_drv_map[] = { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, |
| 48, 49, 50, 51 }; |
| |
| static u8 not_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
| 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, |
| 33, 34 }; |
| |
| typedef struct __attribute__((packed)) { |
| u8 result; |
| u8 drv_drv_num; |
| u8 sen_sen_num; |
| u8 drv_sen_num; |
| u8 drv_gnd_avdd_num; |
| u8 sen_gnd_avdd_num; |
| u16 checksum; |
| } test_result_t; |
| |
| struct params_info_t { |
| u32 max_drv_num; |
| u32 max_sen_num; |
| u8 *drv_map; |
| u8 *sen_map; |
| u32 short_test_time_reg; |
| u32 short_test_status_reg; |
| u32 short_test_result_reg; |
| u32 drv_drv_selfcode_reg; |
| u32 sen_sen_selfcode_reg; |
| u32 drv_sen_selfcode_reg; |
| u32 diffcode_data_reg; |
| u16 short_test_dump_num; |
| u16 dft_short_threshold; |
| u16 short_diffcode_threshold; |
| }; |
| |
| struct params_info_t params_bra = { |
| MAX_DRV_NUM_BRA, |
| MAX_SEN_NUM_BRA, |
| brl_a_drv_map, |
| brl_a_sen_map, |
| SHORT_TEST_TIME_REG_BRA, |
| SHORT_TEST_STATUS_REG_BRA, |
| SHORT_TEST_RESULT_REG_BRA, |
| DRV_DRV_SELFCODE_REG_BRA, |
| SEN_SEN_SELFCODE_REG_BRA, |
| DRV_SEN_SELFCODE_REG_BRA, |
| DIFF_CODE_DATA_REG_BRA, |
| DFT_ADC_DUMP_NUM_BRA, |
| DFT_SHORT_THRESHOLD_BRA, |
| DFT_DIFFCODE_SHORT_THRESHOLD_BRA, |
| }; |
| |
| struct params_info_t params_brb = { |
| MAX_DRV_NUM_BRB, |
| MAX_SEN_NUM_BRB, |
| brl_b_drv_map, |
| brl_b_sen_map, |
| SHORT_TEST_TIME_REG_BRB, |
| SHORT_TEST_STATUS_REG_BRB, |
| SHORT_TEST_RESULT_REG_BRB, |
| DRV_DRV_SELFCODE_REG_BRB, |
| SEN_SEN_SELFCODE_REG_BRB, |
| DRV_SEN_SELFCODE_REG_BRB, |
| DIFF_CODE_DATA_REG_BRB, |
| DFT_ADC_DUMP_NUM_BRB, |
| DFT_SHORT_THRESHOLD_BRB, |
| DFT_DIFFCODE_SHORT_THRESHOLD_BRB, |
| }; |
| |
| struct params_info_t params_brd = { |
| MAX_DRV_NUM_BRD, |
| MAX_SEN_NUM_BRD, |
| brl_d_drv_map, |
| brl_d_sen_map, |
| SHORT_TEST_TIME_REG_BRD, |
| SHORT_TEST_STATUS_REG_BRD, |
| SHORT_TEST_RESULT_REG_BRD, |
| DRV_DRV_SELFCODE_REG_BRD, |
| SEN_SEN_SELFCODE_REG_BRD, |
| DRV_SEN_SELFCODE_REG_BRD, |
| DIFF_CODE_DATA_REG_BRD, |
| DFT_ADC_DUMP_NUM_BRD, |
| DFT_SHORT_THRESHOLD_BRD, |
| DFT_DIFFCODE_SHORT_THRESHOLD_BRD, |
| }; |
| |
| struct params_info_t params_not = { |
| MAX_DRV_NUM_NOT, |
| MAX_SEN_NUM_NOT, |
| not_drv_map, |
| not_sen_map, |
| SHORT_TEST_TIME_REG_NOT, |
| SHORT_TEST_STATUS_REG_NOT, |
| SHORT_TEST_RESULT_REG_NOT, |
| DRV_DRV_SELFCODE_REG_NOT, |
| SEN_SEN_SELFCODE_REG_NOT, |
| DRV_SEN_SELFCODE_REG_NOT, |
| DIFF_CODE_DATA_REG_NOT, |
| 0, |
| 0, |
| 0, |
| }; |
| |
| struct ts_test_params { |
| bool test_items[MAX_TEST_ITEMS]; |
| |
| u32 rawdata_addr; |
| u32 noisedata_addr; |
| u32 self_rawdata_addr; |
| u32 self_noisedata_addr; |
| |
| u32 drv_num; |
| u32 sen_num; |
| |
| struct params_info_t *params_info; |
| |
| s32 cfg_buf[GOODIX_CFG_MAX_SIZE]; |
| s32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM]; |
| s32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM]; |
| s32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM]; |
| s32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM]; |
| s32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM]; |
| s32 noise_threshold; |
| s32 self_noise_threshold; |
| |
| u32 short_threshold; |
| u32 r_drv_drv_threshold; |
| u32 r_drv_sen_threshold; |
| u32 r_sen_sen_threshold; |
| u32 r_drv_gnd_threshold; |
| u32 r_sen_gnd_threshold; |
| u32 avdd_value; |
| }; |
| |
| struct ts_test_rawdata { |
| s16 data[MAX_DRV_NUM * MAX_SEN_NUM]; |
| u32 size; |
| }; |
| |
| struct ts_test_self_rawdata { |
| s16 data[MAX_DRV_NUM + MAX_SEN_NUM]; |
| u32 size; |
| }; |
| |
| struct ts_short_res { |
| u8 short_num; |
| s16 short_msg[4 * MAX_SHORT_NUM]; |
| }; |
| |
| struct ts_open_res { |
| u8 beyond_max_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; |
| u8 beyond_min_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; |
| u8 beyond_accord_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; |
| }; |
| |
| struct goodix_ts_test { |
| struct goodix_ts_core *ts; |
| struct ts_test_params test_params; |
| struct ts_test_rawdata rawdata[TOTAL_FRAME_NUM]; |
| struct ts_test_rawdata accord_arr[TOTAL_FRAME_NUM]; |
| struct ts_test_rawdata noisedata[NOISEDATA_TEST_TIMES]; |
| struct goodix_ic_config test_config; |
| struct ts_test_self_rawdata self_rawdata; |
| struct ts_test_self_rawdata self_noisedata; |
| struct ts_short_res short_res; |
| struct ts_open_res open_res; |
| |
| /*[0][0][0][0][0].. 0 without test; 1 pass, 2 panel failed; 3 software |
| * failed */ |
| char test_result[MAX_TEST_ITEMS]; |
| char test_info[TS_RAWDATA_RESULT_MAX]; |
| }; |
| |
| static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2) |
| { |
| if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) |
| return (v1 - v2) * 63 / v2; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) |
| return (v1 - v2) * 74 / v2 + 20; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) |
| return (v1 / v2 - 1) * 70 + 59; |
| else |
| return (v1 / v2 - 1) * 55 + 45; |
| } |
| |
| static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2) |
| { |
| if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) |
| return 64 * (2 * v2 - 25) * 40 / v1 - 40; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) |
| return 64 * (2 * v2 - 25) * 99 / v1 - 60; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) |
| return 64 * (2 * v2 - 25) * 93 / v1 - 20; |
| else |
| return 64 * (2 * v2 - 25) * 76 / v1 - 15; |
| } |
| |
| static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) |
| { |
| if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) |
| return 64148 / v - 40; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) |
| return 150500 / v - 60; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) |
| return 145000 / v - 15; |
| else |
| return 120000 / v - 16; |
| } |
| |
| static int ts_test_reset(struct goodix_ts_test *ts_test, u32 delay_ms) |
| { |
| return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); |
| } |
| |
| static int ts_test_read( |
| struct goodix_ts_test *ts_test, u32 addr, u8 *data, u32 len) |
| { |
| return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); |
| } |
| |
| static int ts_test_write( |
| struct goodix_ts_test *ts_test, u32 addr, u8 *data, u32 len) |
| { |
| return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); |
| } |
| |
| static int ts_test_send_cmd( |
| struct goodix_ts_test *ts_test, struct goodix_ts_cmd *cmd) |
| { |
| return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); |
| } |
| |
| static int ts_test_irq_enable(struct goodix_ts_test *ts_test, bool flag) |
| { |
| return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag); |
| } |
| |
| /* |
| static int ts_test_send_config(struct goodix_ts_test *ts_test, int type) |
| { |
| struct goodix_ic_config *cfg; |
| |
| if (type >= GOODIX_MAX_CONFIG_GROUP) { |
| ts_err("unsupported config type %d", type); |
| return -EINVAL; |
| } |
| cfg = ts_test->ts->ic_configs[type]; |
| if (!cfg || cfg->len <= 0) { |
| ts_err("no valid normal config found"); |
| return -EINVAL; |
| } |
| |
| return ts_test->ts->hw_ops->send_config( |
| ts_test->ts, cfg->data, cfg->len); |
| } |
| */ |
| |
| static int ts_test_read_version( |
| struct goodix_ts_test *ts_test, struct goodix_fw_version *version) |
| { |
| return ts_test->ts->hw_ops->read_version(ts_test->ts, version); |
| } |
| |
| static void goodix_init_params(struct goodix_ts_test *ts_test) |
| { |
| struct goodix_ts_core *ts = ts_test->ts; |
| struct ts_test_params *test_params = &ts_test->test_params; |
| |
| test_params->rawdata_addr = ts->ic_info.misc.mutual_rawdata_addr; |
| test_params->noisedata_addr = ts->ic_info.misc.mutual_diffdata_addr; |
| test_params->self_rawdata_addr = ts->ic_info.misc.self_rawdata_addr; |
| test_params->self_noisedata_addr = ts->ic_info.misc.self_diffdata_addr; |
| |
| test_params->short_threshold = short_circuit_threshold[0]; |
| test_params->r_drv_drv_threshold = short_circuit_threshold[1]; |
| test_params->r_drv_sen_threshold = short_circuit_threshold[2]; |
| test_params->r_sen_sen_threshold = short_circuit_threshold[3]; |
| test_params->r_drv_gnd_threshold = short_circuit_threshold[4]; |
| test_params->r_sen_gnd_threshold = short_circuit_threshold[5]; |
| test_params->avdd_value = short_circuit_threshold[6]; |
| |
| test_params->drv_num = ts->ic_info.parm.drv_num; |
| test_params->sen_num = ts->ic_info.parm.sen_num; |
| |
| if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) |
| test_params->params_info = ¶ms_bra; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) |
| test_params->params_info = ¶ms_brb; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) |
| test_params->params_info = ¶ms_brd; |
| else if (ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) |
| test_params->params_info = ¶ms_not; |
| } |
| |
| static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) |
| { |
| ts_info("TP test prepare IN"); |
| |
| goodix_init_params(ts_test); |
| |
| /* disable irq */ |
| ts_test_irq_enable(ts_test, false); |
| /* close esd */ |
| goodix_ts_esd_off(ts_test->ts); |
| |
| return 0; |
| } |
| |
| static void goodix_tptest_finish(struct goodix_ts_test *ts_test) |
| { |
| ts_info("TP test finish IN"); |
| /* reset chip */ |
| ts_test_reset(ts_test, 100); |
| |
| /* open esd */ |
| goodix_ts_esd_on(ts_test->ts); |
| /* enable irq */ |
| ts_test_irq_enable(ts_test, true); |
| } |
| |
| #define PRODUCT_TEST_ADDR 0x15D4C |
| #define DRV_CHAN_BYTES 7 |
| #define SEN_CHAN_BYTES 10 |
| static void goodix_opencircuit_test(struct goodix_ts_test *ts_test) |
| { |
| struct goodix_ts_cmd temp_cmd; |
| u8 buf[24]; |
| int retry = 20; |
| int i; |
| int ret; |
| |
| ts_info("---------------------- open_test begin ----------------------"); |
| ts_test->test_result[GTP_OPEN_TEST] = SYS_SOFTWARE_REASON; |
| |
| temp_cmd.cmd = 0x63; |
| temp_cmd.len = 4; |
| ret = ts_test_send_cmd(ts_test, &temp_cmd); |
| if (ret < 0) { |
| ts_err("send open test cmd failed"); |
| return; |
| } |
| |
| msleep(200); |
| |
| while (retry--) { |
| ret = ts_test_read( |
| ts_test, PRODUCT_TEST_ADDR, buf, sizeof(buf)); |
| if (ret < 0) { |
| ts_err("read open test result failed"); |
| return; |
| } |
| if (buf[0] == 0xCC && buf[1] == 0xCC) |
| break; |
| msleep(50); |
| } |
| if (retry < 0) { |
| ts_err("open test not ready, status = %x%x", buf[0], buf[1]); |
| return; |
| } |
| ret = checksum_cmp(buf, sizeof(buf), CHECKSUM_MODE_U8_LE); |
| if (ret) { |
| ts_err("open test result checksum error"); |
| return; |
| } |
| |
| if (buf[2] == 0) { |
| ts_info("open test pass"); |
| ts_test->test_result[GTP_OPEN_TEST] = GTP_TEST_PASS; |
| } else { |
| ts_err("open test failed"); |
| ts_test->test_result[GTP_OPEN_TEST] = GTP_PANEL_REASON; |
| |
| /* DRV[0~55] total 7 bytes */ |
| for (i = 0; i < DRV_CHAN_BYTES; i++) { |
| if (buf[i + 3]) |
| ts_info("DRV[%d~%d] open circuit, ret=0x%X", |
| i * 8, i * 8 + 7, buf[i + 3]); |
| } |
| /* SEN[0~79] total 10 bytes */ |
| for (i = 0; i < SEN_CHAN_BYTES; i++) { |
| if (buf[i + 10]) |
| ts_info("SEN[%d~%d] open circuit, ret=0x%X", |
| i * 8, i * 8 + 7, buf[i + 10]); |
| } |
| } |
| } |
| |
| #define SHORT_TEST_RUN_REG 0x10400 |
| #define SHORT_TEST_RUN_FLAG 0xAA |
| #define INSPECT_FW_SWITCH_CMD 0x85 |
| #define TEST_FW_PID "OST" |
| static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) |
| { |
| struct goodix_ts_cmd tmp_cmd; |
| struct goodix_fw_version fw_ver; |
| int ret; |
| int retry; |
| int resend = 3; |
| u8 status; |
| |
| ts_info("short test prepare IN"); |
| ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; |
| tmp_cmd.len = 4; |
| tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; |
| |
| resend_cmd: |
| ret = ts_test_send_cmd(ts_test, &tmp_cmd); |
| if (ret < 0) { |
| ts_err("send test mode failed"); |
| return ret; |
| } |
| |
| retry = 3; |
| while (retry--) { |
| msleep(40); |
| if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { |
| ret = ts_test_read_version(ts_test, &fw_ver); |
| if (ret < 0) { |
| ts_err("read test version failed"); |
| return ret; |
| } |
| ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, |
| strlen(TEST_FW_PID)); |
| if (ret == 0) |
| return 0; |
| else |
| ts_info("patch ID dismatch %s != %s", |
| fw_ver.patch_pid, TEST_FW_PID); |
| } else { |
| ret = ts_test_read( |
| ts_test, SHORT_TEST_RUN_REG, &status, 1); |
| if (!ret && status == SHORT_TEST_RUN_FLAG) |
| return 0; |
| ts_info("short_mode_status=0x%02x ret=%d", status, ret); |
| } |
| } |
| |
| if (resend--) { |
| ts_test_reset(ts_test, 100); |
| goto resend_cmd; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) |
| { |
| int i = 0; |
| u32 res = 255; |
| |
| if (chn_num & DRV_CHANNEL_FLAG) |
| chn_num = (chn_num & ~DRV_CHANNEL_FLAG) + |
| test_params->params_info->max_sen_num; |
| |
| for (i = 0; i < test_params->params_info->max_sen_num; i++) { |
| if (test_params->params_info->sen_map[i] == chn_num) { |
| res = i; |
| break; |
| } |
| } |
| /* res != 255 mean found the corresponding channel num */ |
| if (res != 255) |
| return res; |
| /* if cannot find in SenMap try find in DrvMap */ |
| for (i = 0; i < test_params->params_info->max_drv_num; i++) { |
| if (test_params->params_info->drv_map[i] == chn_num) { |
| res = i; |
| break; |
| } |
| } |
| if (i >= test_params->params_info->max_drv_num) |
| ts_err("Faild found corrresponding channel num:%d", chn_num); |
| else |
| res |= DRV_CHANNEL_FLAG; |
| |
| return res; |
| } |
| |
| static void goodix_save_short_res( |
| struct ts_test_params *params, u16 chn1, u16 chn2, int r) |
| { |
| int i; |
| u8 repeat_cnt = 0; |
| u8 repeat = 0; |
| struct goodix_ts_test *ts_test = |
| container_of(params, struct goodix_ts_test, test_params); |
| struct ts_short_res *short_res = &ts_test->short_res; |
| |
| if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM) |
| return; |
| |
| for (i = 0; i < short_res->short_num; i++) { |
| repeat_cnt = 0; |
| if (short_res->short_msg[4 * i] == chn1) |
| repeat_cnt++; |
| if (short_res->short_msg[4 * i] == chn2) |
| repeat_cnt++; |
| if (short_res->short_msg[4 * i + 1] == chn1) |
| repeat_cnt++; |
| if (short_res->short_msg[4 * i + 1] == chn2) |
| repeat_cnt++; |
| if (repeat_cnt >= 2) { |
| repeat = 1; |
| break; |
| } |
| } |
| if (repeat == 0) { |
| short_res->short_msg[4 * short_res->short_num + 0] = chn1; |
| short_res->short_msg[4 * short_res->short_num + 1] = chn2; |
| short_res->short_msg[4 * short_res->short_num + 2] = |
| (r >> 8) & 0xFF; |
| short_res->short_msg[4 * short_res->short_num + 3] = r & 0xFF; |
| if (short_res->short_num < MAX_SHORT_NUM) |
| short_res->short_num++; |
| } |
| } |
| |
| static int gdix_check_tx_tx_shortcircut( |
| struct goodix_ts_test *ts_test, u8 short_ch_num) |
| { |
| int ret = 0, err = 0; |
| u32 r_threshold = 0, short_r = 0; |
| int size = 0, i = 0, j = 0; |
| u16 adc_signal = 0; |
| u8 master_pin_num, slave_pin_num; |
| u8 *data_buf; |
| u32 data_reg; |
| struct ts_test_params *test_params = &ts_test->test_params; |
| int max_drv_num = test_params->params_info->max_drv_num; |
| int max_sen_num = test_params->params_info->max_sen_num; |
| u16 self_capdata, short_die_num = 0; |
| |
| size = 4 + max_drv_num * 2 + 2; |
| data_buf = kzalloc(size, GFP_KERNEL); |
| if (!data_buf) { |
| ts_err("Failed to alloc memory"); |
| return -ENOMEM; |
| } |
| /* drv&drv shortcircut check */ |
| data_reg = test_params->params_info->drv_drv_selfcode_reg; |
| for (i = 0; i < short_ch_num; i++) { |
| ret = ts_test_read(ts_test, data_reg, data_buf, size); |
| if (ret < 0) { |
| ts_err("Failed read Drv-to-Drv short rawdata"); |
| err = -EINVAL; |
| break; |
| } |
| |
| if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { |
| ts_err("Drv-to-Drv adc data checksum error"); |
| err = -EINVAL; |
| break; |
| } |
| |
| r_threshold = test_params->r_drv_drv_threshold; |
| short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); |
| short_die_num -= max_sen_num; |
| if (short_die_num >= max_drv_num) { |
| ts_info("invalid short pad num:%d", |
| short_die_num + max_sen_num); |
| continue; |
| } |
| |
| /* TODO: j start position need recheck */ |
| self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); |
| if (self_capdata == 0xffff || self_capdata == 0) { |
| ts_info("invalid self_capdata:0x%x", self_capdata); |
| continue; |
| } |
| |
| for (j = short_die_num + 1; j < max_drv_num; j++) { |
| adc_signal = |
| le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); |
| |
| if (adc_signal < test_params->short_threshold) |
| continue; |
| |
| short_r = (u32)cal_cha_to_cha_res( |
| ts_test, self_capdata, adc_signal); |
| if (short_r < r_threshold) { |
| master_pin_num = map_die2pin(test_params, |
| short_die_num + max_sen_num); |
| slave_pin_num = map_die2pin( |
| test_params, j + max_sen_num); |
| if (master_pin_num == 0xFF || |
| slave_pin_num == 0xFF) { |
| ts_info("WARNNING invalid pin"); |
| continue; |
| } |
| goodix_save_short_res(test_params, |
| master_pin_num, slave_pin_num, short_r); |
| ts_err("short circut:R=%dK,R_Threshold=%dK", |
| short_r, r_threshold); |
| ts_err("%s%d--%s%d shortcircut", |
| (master_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (master_pin_num & ~DRV_CHANNEL_FLAG), |
| (slave_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (slave_pin_num & ~DRV_CHANNEL_FLAG)); |
| err = -EINVAL; |
| } |
| } |
| data_reg += size; |
| } |
| |
| kfree(data_buf); |
| return err; |
| } |
| |
| static int gdix_check_rx_rx_shortcircut( |
| struct goodix_ts_test *ts_test, u8 short_ch_num) |
| { |
| int ret = 0, err = 0; |
| u32 r_threshold = 0, short_r = 0; |
| int size = 0, i = 0, j = 0; |
| u16 adc_signal = 0; |
| u8 master_pin_num, slave_pin_num; |
| u8 *data_buf; |
| u32 data_reg; |
| struct ts_test_params *test_params = &ts_test->test_params; |
| int max_sen_num = test_params->params_info->max_sen_num; |
| u16 self_capdata, short_die_num = 0; |
| |
| size = 4 + max_sen_num * 2 + 2; |
| data_buf = kzalloc(size, GFP_KERNEL); |
| if (!data_buf) { |
| ts_err("Failed to alloc memory"); |
| return -ENOMEM; |
| } |
| /* drv&drv shortcircut check */ |
| data_reg = test_params->params_info->sen_sen_selfcode_reg; |
| for (i = 0; i < short_ch_num; i++) { |
| ret = ts_test_read(ts_test, data_reg, data_buf, size); |
| if (ret) { |
| ts_err("Failed read Sen-to-Sen short rawdata"); |
| err = -EINVAL; |
| break; |
| } |
| |
| if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { |
| ts_err("Sen-to-Sen adc data checksum error"); |
| err = -EINVAL; |
| break; |
| } |
| |
| r_threshold = test_params->r_sen_sen_threshold; |
| short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); |
| if (short_die_num >= max_sen_num) { |
| ts_info("invalid short pad num:%d", short_die_num); |
| continue; |
| } |
| |
| /* TODO: j start position need recheck */ |
| self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); |
| if (self_capdata == 0xffff || self_capdata == 0) { |
| ts_info("invalid self_capdata:0x%x", self_capdata); |
| continue; |
| } |
| |
| for (j = short_die_num + 1; j < max_sen_num; j++) { |
| adc_signal = |
| le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); |
| |
| if (adc_signal < test_params->short_threshold) |
| continue; |
| |
| short_r = (u32)cal_cha_to_cha_res( |
| ts_test, self_capdata, adc_signal); |
| if (short_r < r_threshold) { |
| master_pin_num = |
| map_die2pin(test_params, short_die_num); |
| slave_pin_num = map_die2pin(test_params, j); |
| if (master_pin_num == 0xFF || |
| slave_pin_num == 0xFF) { |
| ts_info("WARNNING invalid pin"); |
| continue; |
| } |
| goodix_save_short_res(test_params, |
| master_pin_num, slave_pin_num, short_r); |
| ts_err("short circut:R=%dK,R_Threshold=%dK", |
| short_r, r_threshold); |
| ts_err("%s%d--%s%d shortcircut", |
| (master_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (master_pin_num & ~DRV_CHANNEL_FLAG), |
| (slave_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (slave_pin_num & ~DRV_CHANNEL_FLAG)); |
| err = -EINVAL; |
| } |
| } |
| data_reg += size; |
| } |
| |
| kfree(data_buf); |
| return err; |
| } |
| |
| static int gdix_check_tx_rx_shortcircut( |
| struct goodix_ts_test *ts_test, u8 short_ch_num) |
| { |
| int ret = 0, err = 0; |
| u32 r_threshold = 0, short_r = 0; |
| int size = 0, i = 0, j = 0; |
| u16 adc_signal = 0; |
| u8 master_pin_num, slave_pin_num; |
| u8 *data_buf = NULL; |
| u32 data_reg; |
| struct ts_test_params *test_params = &ts_test->test_params; |
| int max_drv_num = test_params->params_info->max_drv_num; |
| int max_sen_num = test_params->params_info->max_sen_num; |
| u16 self_capdata, short_die_num = 0; |
| |
| size = 4 + max_drv_num * 2 + 2; |
| data_buf = kzalloc(size, GFP_KERNEL); |
| if (!data_buf) { |
| ts_err("Failed to alloc memory"); |
| return -ENOMEM; |
| } |
| /* drv&sen shortcircut check */ |
| data_reg = test_params->params_info->drv_sen_selfcode_reg; |
| for (i = 0; i < short_ch_num; i++) { |
| ret = ts_test_read(ts_test, data_reg, data_buf, size); |
| if (ret) { |
| ts_err("Failed read Drv-to-Sen short rawdata"); |
| err = -EINVAL; |
| break; |
| } |
| |
| if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { |
| ts_err("Drv-to-Sen adc data checksum error"); |
| err = -EINVAL; |
| break; |
| } |
| |
| r_threshold = test_params->r_drv_sen_threshold; |
| short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); |
| if (short_die_num >= max_sen_num) { |
| ts_info("invalid short pad num:%d", short_die_num); |
| continue; |
| } |
| |
| /* TODO: j start position need recheck */ |
| self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); |
| if (self_capdata == 0xffff || self_capdata == 0) { |
| ts_info("invalid self_capdata:0x%x", self_capdata); |
| continue; |
| } |
| |
| for (j = 0; j < max_drv_num; j++) { |
| adc_signal = |
| le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); |
| |
| if (adc_signal < test_params->short_threshold) |
| continue; |
| |
| short_r = (u32)cal_cha_to_cha_res( |
| ts_test, self_capdata, adc_signal); |
| if (short_r < r_threshold) { |
| master_pin_num = |
| map_die2pin(test_params, short_die_num); |
| slave_pin_num = map_die2pin( |
| test_params, j + max_sen_num); |
| if (master_pin_num == 0xFF || |
| slave_pin_num == 0xFF) { |
| ts_info("WARNNING invalid pin"); |
| continue; |
| } |
| goodix_save_short_res(test_params, |
| master_pin_num, slave_pin_num, short_r); |
| ts_err("short circut:R=%dK,R_Threshold=%dK", |
| short_r, r_threshold); |
| ts_err("%s%d--%s%d shortcircut", |
| (master_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (master_pin_num & ~DRV_CHANNEL_FLAG), |
| (slave_pin_num & DRV_CHANNEL_FLAG) |
| ? "DRV" |
| : "SEN", |
| (slave_pin_num & ~DRV_CHANNEL_FLAG)); |
| err = -EINVAL; |
| } |
| } |
| data_reg += size; |
| } |
| |
| kfree(data_buf); |
| return err; |
| } |
| |
| static int gdix_check_resistance_to_gnd( |
| struct ts_test_params *test_params, u16 adc_signal, u32 pos) |
| { |
| long r = 0; |
| u16 r_th = 0, avdd_value = 0; |
| u16 chn_id_tmp = 0; |
| u8 pin_num = 0; |
| unsigned short short_type; |
| struct goodix_ts_test *ts_test = |
| container_of(test_params, struct goodix_ts_test, test_params); |
| int max_drv_num = test_params->params_info->max_drv_num; |
| int max_sen_num = test_params->params_info->max_sen_num; |
| |
| avdd_value = test_params->avdd_value; |
| short_type = adc_signal & 0x8000; |
| adc_signal &= ~0x8000; |
| if (adc_signal == 0) |
| adc_signal = 1; |
| |
| if (short_type == 0) { |
| /* short to GND */ |
| r = cal_cha_to_gnd_res(ts_test, adc_signal); |
| } else { |
| /* short to VDD */ |
| r = cal_cha_to_avdd_res(ts_test, adc_signal, avdd_value); |
| } |
| |
| if (pos < max_drv_num) |
| r_th = test_params->r_drv_gnd_threshold; |
| else |
| r_th = test_params->r_sen_gnd_threshold; |
| |
| chn_id_tmp = pos; |
| if (chn_id_tmp < max_drv_num) |
| chn_id_tmp += max_sen_num; |
| else |
| chn_id_tmp -= max_drv_num; |
| |
| if (r < r_th) { |
| pin_num = map_die2pin(test_params, chn_id_tmp); |
| goodix_save_short_res(test_params, pin_num, |
| short_type ? CHN_VDD : CHN_GND, r); |
| ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", |
| (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", |
| (pin_num & ~DRV_CHANNEL_FLAG), |
| short_type ? "VDD" : "GND", r, r_th); |
| |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) |
| { |
| int ret = 0, err = 0; |
| int size = 0, i = 0; |
| u16 adc_signal = 0; |
| u32 data_reg; |
| u8 *data_buf = NULL; |
| int max_drv_num = ts_test->test_params.params_info->max_drv_num; |
| int max_sen_num = ts_test->test_params.params_info->max_sen_num; |
| |
| size = (max_drv_num + max_sen_num) * 2 + 2; |
| data_buf = kzalloc(size, GFP_KERNEL); |
| if (!data_buf) { |
| ts_err("Failed to alloc memory"); |
| return -ENOMEM; |
| } |
| /* read diff code, diff code will be used to calculate |
| * resistance between channel and GND */ |
| data_reg = ts_test->test_params.params_info->diffcode_data_reg; |
| ret = ts_test_read(ts_test, data_reg, data_buf, size); |
| if (ret < 0) { |
| ts_err("Failed read to-gnd rawdata"); |
| err = -EINVAL; |
| goto err_out; |
| } |
| |
| if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { |
| ts_err("diff code checksum error"); |
| err = -EINVAL; |
| goto err_out; |
| } |
| |
| for (i = 0; i < max_drv_num + max_sen_num; i++) { |
| adc_signal = le16_to_cpup((__le16 *)&data_buf[i * 2]); |
| ret = gdix_check_resistance_to_gnd( |
| &ts_test->test_params, adc_signal, i); |
| if (ret != 0) { |
| ts_err("Resistance to-gnd/vdd short"); |
| err = ret; |
| } |
| } |
| |
| err_out: |
| kfree(data_buf); |
| return err; |
| } |
| |
| static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) |
| { |
| int ret; |
| int err = 0; |
| test_result_t test_result; |
| |
| ret = ts_test_read(ts_test, |
| ts_test->test_params.params_info->short_test_result_reg, |
| (u8 *)&test_result, sizeof(test_result)); |
| if (ret < 0) { |
| ts_err("Read TEST_RESULT_REG failed"); |
| return ret; |
| } |
| |
| if (checksum_cmp((u8 *)&test_result, sizeof(test_result), |
| CHECKSUM_MODE_U8_LE)) { |
| ts_err("shrot result checksum err"); |
| return -EINVAL; |
| } |
| |
| if (!(test_result.result & 0x0F)) { |
| ts_info(">>>>> No shortcircut"); |
| return 0; |
| } |
| ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d", |
| test_result.result, test_result.drv_drv_num, |
| test_result.sen_sen_num, test_result.drv_sen_num, |
| test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num); |
| |
| if (test_result.drv_drv_num) |
| err |= gdix_check_tx_tx_shortcircut( |
| ts_test, test_result.drv_drv_num); |
| if (test_result.sen_sen_num) |
| err |= gdix_check_rx_rx_shortcircut( |
| ts_test, test_result.sen_sen_num); |
| if (test_result.drv_sen_num) |
| err |= gdix_check_tx_rx_shortcircut( |
| ts_test, test_result.drv_sen_num); |
| if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num) |
| err |= gdix_check_gndvdd_shortcircut(ts_test); |
| |
| ts_info(">>>>> short check return 0x%x", err); |
| |
| return err; |
| } |
| |
| #define SHORT_FW_CMD_REG 0x10400 |
| static int send_test_cmd( |
| struct goodix_ts_test *ts_test, struct goodix_ts_cmd *cmd) |
| { |
| int ret; |
| u32 reg = SHORT_FW_CMD_REG; |
| cmd->state = 0; |
| cmd->ack = 0; |
| goodix_append_checksum( |
| &(cmd->buf[2]), cmd->len - 2, CHECKSUM_MODE_U8_LE); |
| ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); |
| if (ret < 0) |
| return ret; |
| usleep_range(10000, 11000); |
| return ret; |
| } |
| |
| #define INSPECT_PARAM_CMD 0xAA |
| #define SHORT_TEST_FINISH_FLAG 0x88 |
| #define SHORT_TEST_THRESHOLD_REG 0x20402 |
| static void goodix_shortcircuit_test(struct goodix_ts_test *ts_test) |
| { |
| int ret = 0; |
| int retry; |
| u16 test_time; |
| u8 status; |
| int ic_type = ts_test->ts->bus->ic_type; |
| struct goodix_ts_cmd test_parm_cmd; |
| // u8 test_param[6]; |
| |
| ts_info("---------------------- short_test begin ----------------------"); |
| ret = goodix_short_test_prepare(ts_test); |
| if (ret < 0) { |
| ts_err("Failed enter short test mode"); |
| return; |
| } |
| |
| /* get short test time */ |
| ret = ts_test_read(ts_test, |
| ts_test->test_params.params_info->short_test_time_reg, |
| (u8 *)&test_time, 2); |
| if (ret < 0) { |
| ts_err("Failed to get test_time, default %dms", |
| DEFAULT_TEST_TIME_MS); |
| test_time = DEFAULT_TEST_TIME_MS; |
| } else { |
| if (ic_type == IC_TYPE_BERLIN_A) |
| test_time /= 10; |
| if (test_time > MAX_TEST_TIME_MS) { |
| ts_info("test time too long %d > %d", test_time, |
| MAX_TEST_TIME_MS); |
| test_time = MAX_TEST_TIME_MS; |
| } |
| ts_info("get test time %dms", test_time); |
| } |
| |
| /* start short test */ |
| if (ic_type == IC_TYPE_BERLIN_A) { |
| test_parm_cmd.len = 0x0A; |
| test_parm_cmd.cmd = INSPECT_PARAM_CMD; |
| test_parm_cmd.data[0] = |
| ts_test->test_params.params_info->dft_short_threshold & |
| 0xFF; |
| test_parm_cmd.data[1] = (ts_test->test_params.params_info |
| ->dft_short_threshold >> |
| 8) & |
| 0xFF; |
| test_parm_cmd.data[2] = ts_test->test_params.params_info |
| ->short_diffcode_threshold & |
| 0xFF; |
| test_parm_cmd.data[3] = |
| (ts_test->test_params.params_info |
| ->short_diffcode_threshold >> |
| 8) & |
| 0xFF; |
| test_parm_cmd.data[4] = |
| ts_test->test_params.params_info->short_test_dump_num & |
| 0xFF; |
| test_parm_cmd.data[5] = (ts_test->test_params.params_info |
| ->short_test_dump_num >> |
| 8) & |
| 0xFF; |
| ret = send_test_cmd(ts_test, &test_parm_cmd); |
| if (ret < 0) { |
| ts_err("send INSPECT_PARAM_CMD failed"); |
| return; |
| } |
| } else { |
| // test_param[0] = |
| // ts_test->test_params.params_info->dft_short_threshold & 0xFF; |
| // test_param[1] = |
| // (ts_test->test_params.params_info->dft_short_threshold >> 8) |
| // & 0xFF; test_param[2] = |
| // ts_test->test_params.params_info->short_diffcode_threshold & |
| // 0xFF; test_param[3] = |
| // (ts_test->test_params.params_info->short_diffcode_threshold |
| // >> 8) & 0xFF; test_param[4] = |
| // ts_test->test_params.params_info->short_test_dump_num & 0xFF; |
| // test_param[5] = |
| // (ts_test->test_params.params_info->short_test_dump_num >> 8) |
| // & 0xFF; ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, |
| // test_param, sizeof(test_param)); |
| status = 0; |
| ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1); |
| } |
| |
| /* wait short test finish */ |
| msleep(test_time); |
| retry = 50; |
| while (retry--) { |
| ret = ts_test_read(ts_test, |
| ts_test->test_params.params_info->short_test_status_reg, |
| &status, 1); |
| if (!ret && status == SHORT_TEST_FINISH_FLAG) |
| break; |
| msleep(50); |
| } |
| if (retry < 0) { |
| ts_err("short test failed, status:0x%02x", status); |
| return; |
| } |
| |
| /* start analysis short result */ |
| ts_info("short_test finished, start analysis"); |
| ret = goodix_shortcircut_analysis(ts_test); |
| if (ret < 0) |
| ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON; |
| else |
| ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS; |
| } |
| |
| int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) |
| { |
| int ret; |
| int cnt; |
| struct goodix_ts_test *ts_test = NULL; |
| |
| if (!cd || !info) { |
| ts_err("core_data or info is NULL"); |
| return -ENODEV; |
| } |
| |
| ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); |
| if (!ts_test) |
| return -ENOMEM; |
| |
| ts_test->ts = cd; |
| ret = goodix_tptest_prepare(ts_test); |
| if (ret < 0) { |
| ts_err("Failed to prepare TP test, exit"); |
| strncpy(info->result, "[FAIL]\n", TS_RAWDATA_RESULT_MAX - 1); |
| goto exit_finish; |
| } |
| ts_info("TP test prepare OK"); |
| |
| goodix_opencircuit_test(ts_test); |
| goodix_shortcircuit_test(ts_test); |
| |
| cnt = snprintf(info->result, TS_RAWDATA_RESULT_MAX - 1, |
| "open_test-[%s] ", |
| (ts_test->test_result[GTP_OPEN_TEST] == GTP_TEST_PASS) |
| ? "PASS" |
| : "FAIL"); |
| snprintf(&info->result[cnt], TS_RAWDATA_RESULT_MAX - 1, |
| "short_test-[%s]\n", |
| (ts_test->test_result[GTP_SHORT_TEST] == GTP_TEST_PASS) |
| ? "PASS" |
| : "FAIL"); |
| |
| goodix_tptest_finish(ts_test); |
| exit_finish: |
| kfree(ts_test); |
| return ret; |
| } |
| |
| /* show rawdata */ |
| static ssize_t get_rawdata_show( |
| struct device *dev, struct device_attribute *attr, char *buf) |
| { |
| int ret = 0; |
| struct ts_rawdata_info *info = NULL; |
| struct goodix_ts_core *cd = dev_get_drvdata(dev); |
| |
| info = kzalloc(sizeof(*info), GFP_KERNEL); |
| if (!info) |
| return -ENOMEM; |
| |
| goodix_do_inspect(cd, info); |
| |
| ret = snprintf(buf, TS_RAWDATA_RESULT_MAX - 1, "%s", info->result); |
| |
| kfree(info); |
| return ret; |
| } |
| |
| static DEVICE_ATTR(get_rawdata, 0444, get_rawdata_show, NULL); |
| |
| int inspect_module_init(struct goodix_ts_core *core_data) |
| { |
| int ret; |
| struct kobject *def_kobj = &core_data->pdev->dev.kobj; |
| |
| /* create sysfs */ |
| ret = sysfs_create_file(def_kobj, &dev_attr_get_rawdata.attr); |
| if (ret < 0) { |
| ts_err("create sysfs of get_rawdata failed"); |
| goto err_out; |
| } |
| ts_info("inspect module init success"); |
| return 0; |
| |
| err_out: |
| ts_err("inspect module init failed!"); |
| return ret; |
| } |
| |
| void inspect_module_exit(struct goodix_ts_core *core_data) |
| { |
| struct kobject *def_kobj = &core_data->pdev->dev.kobj; |
| |
| ts_info("inspect module exit"); |
| sysfs_remove_file(def_kobj, &dev_attr_get_rawdata.attr); |
| } |