kernel_samsung_a53x/drivers/input/sec_input/stm/stm_dump.c
2024-06-15 16:02:09 -03:00

529 lines
15 KiB
C
Executable file

/* drivers/input/sec_input/stm/stm_dump.c
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*
* Core file for Samsung TSC driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "stm_dev.h"
#include "stm_reg.h"
static ssize_t get_cmoffset_dump_v2(struct stm_ts_data *ts, char *buf, u8 position)
{
u8 *rbuff;
int ret, i, j, cm_num, value, size = 8 + ts->tx_count * ts->rx_count;
u32 signature;
u8 address[4] = { 0 };
char buff[80] = { 0 };
rbuff = kzalloc(size, GFP_KERNEL);
if (!rbuff) {
input_err(true, &ts->client->dev, "%s: alloc failed\n", __func__);
return -ENOMEM;
}
ts->stm_ts_systemreset(ts, 50);
/* read cm2 & cm3 */
for (cm_num = 2; cm_num < 4; ++cm_num) {
input_info(true, &ts->client->dev, "%s: CM%d read start! buf size:%lu\n", __func__, cm_num, strlen(buf));
// Set Test mode : prepare save test data & fail history
address[0] = 0xE4;
address[1] = 0x02;
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0) {
snprintf(buf, ts->proc_cmoffset_size, "NG, timeout, %d", ret);
goto out;
}
ts->stm_ts_command(ts, STM_TS_CMD_CLEAR_ALL_EVENT, true);
stm_ts_release_all_finger(ts);
// set factory level for read data
address[0] = 0x74;
address[1] = position;
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0) {
snprintf(buf, ts->proc_cmoffset_size, "NG, timeout, %d", ret);
goto out;
}
// set data typr for read data
address[0] = 0x7D;
if (cm_num == OFFSET_FAC_DATA_CM2) {
signature = STM_TS_CM2_SIGNATURE;
address[1] = 0x01;
} else if (cm_num == OFFSET_FAC_DATA_CM3) {
signature = STM_TS_CM3_SIGNATURE;
address[1] = 0x02;
}
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0) {
snprintf(buf, ts->proc_cmoffset_size, "NG, timeout, %d", ret);
goto out;
}
memset(rbuff, 0x00, size);
address[0] = 0x75;
ret = ts->stm_ts_read(ts, &address[0], 1, rbuff, size);
if (ret < 0) {
input_err(true, &ts->client->dev, "%s: read failed ret = %d\n", __func__, ret);
snprintf(buf, ts->proc_cmoffset_size, "NG, failed to read data, %d", ret);
continue;
} else {
input_info(true, &ts->client->dev, "%s: read size = %d, ret = %d\n", __func__, size, ret);
}
if (position == OFFSET_FW_SDC) {
snprintf(buff, sizeof(buff), "%s", "SDC ");
} else if (position == OFFSET_FW_SUB) {
snprintf(buff, sizeof(buff), "%s", "SUB ");
} else if (position == OFFSET_FW_MAIN) {
snprintf(buff, sizeof(buff), "%s", "MAIN ");
}
strlcat(buf, buff, ts->proc_cmoffset_size);
// data parsing
/* check Header */
value = rbuff[3] << 24 | rbuff[2] << 16 | rbuff[1] << 8 | rbuff[0];
if (value == 0) {
snprintf(buff, sizeof(buff), "CM%d Data empty!\n", cm_num);
strlcat(buf, buff, ts->proc_cmoffset_size);
input_err(true, &ts->client->dev, "%s: CM%d Data empty\n", __func__, cm_num);
continue;
} else if (value != signature) {
snprintf(buff, sizeof(buff), "CM%d : signature mismatched %08X != %08X\n", cm_num, signature, value);
strlcat(buf, buff, ts->proc_cmoffset_size);
input_err(true, &ts->client->dev, "%s: CM%d pos[%d] signature is mismatched %08X != %08X\n",
__func__, cm_num, position, signature, value);
continue;
} else {
snprintf(buff, sizeof(buff), "CM%d try cnt:%d\n", cm_num, rbuff[5]);
strlcat(buf, buff, ts->proc_cmoffset_size);
input_info(true, &ts->client->dev, "%s: CM%d try cnt:%d value %d signature:%8X\n",
__func__, cm_num, rbuff[5], value, signature);
}
for (i = 0; i < ts->tx_count; i++) {
for (j = 0; j < ts->rx_count; j++) {
snprintf(buff, sizeof(buff), " %3d", (rbuff[8 + i * ts->rx_count + j]) << 3);
strlcat(buf, buff, ts->proc_cmoffset_size);
}
snprintf(buff, sizeof(buff), "\n");
strlcat(buf, buff, ts->proc_cmoffset_size);
}
input_info(true, &ts->client->dev, "%s: CM%d read end!\n", __func__, cm_num);
}
out:
input_err(true, &ts->client->dev, "%s: pos:%d, buf size:%zu\n", __func__, position, strlen(buf));
// clear factory level
address[0] = 0x74;
address[1] = 0;
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0)
goto err_out;
// clear data type
address[0] = 0x7D;
address[1] = 0x00;
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0)
goto err_out;
// Set Normal mode : save test data & fail history
address[0] = 0xE4;
address[1] = 0x00;
ret = stm_ts_wait_for_echo_event(ts, address, 2, 0);
if (ret < 0)
goto err_out;
err_out:
/* reinit */
ts->stm_ts_systemreset(ts, 0);
stm_ts_set_scanmode(ts, ts->scan_mode);
kfree(rbuff);
return strlen(buf);
}
static ssize_t get_cmoffset_dump_v1(struct stm_ts_data *ts, char *buf, u8 position)
{
u8 *rbuff;
int ret, i, j, size = ts->tx_count * ts->rx_count;
u32 signature;
u8 address[4] = { 0 };
rbuff = kzalloc(size, GFP_KERNEL);
if (!rbuff) {
input_err(true, &ts->client->dev, "%s: alloc failed\n", __func__);
return -ENOMEM;
}
ts->stm_ts_command(ts, STM_TS_CMD_CLEAR_ALL_EVENT, true);
stm_ts_release_all_finger(ts);
/* Request SEC factory debug data from flash */
address[0] = 0xA4;
address[1] = 0x06;
address[2] = 0x92;
address[3] = position;
ret = stm_ts_wait_for_echo_event(ts, address, 4, 0);
if (ret < 0) {
snprintf(buf, ts->proc_cmoffset_size, "NG, failed to request data, %d", ret);
goto out;
}
/* read header info */
address[0] = 0xA6;
address[1] = 0x00;
address[2] = 0x00;
ret = ts->stm_ts_read(ts, &address[0], 3, rbuff, 8);
if (ret < 0) {
input_err(true, &ts->client->dev,
"%s: read header failed. ret: %d\n", __func__, ret);
snprintf(buf, ts->proc_cmoffset_size, "NG, failed to read header, %d", ret);
goto out;
}
signature = rbuff[3] << 24 | rbuff[2] << 16 | rbuff[1] << 8 | rbuff[0];
input_info(true, &ts->client->dev,
"%s: position:%d, signature:%08X (%X), validation:%X, try count:%X\n",
__func__, position, signature, SEC_OFFSET_SIGNATURE, rbuff[4], rbuff[5]);
if (signature != SEC_OFFSET_SIGNATURE) {
input_err(true, &ts->client->dev, "%s: cmoffset[%d], signature is mismatched\n",
__func__, position);
snprintf(buf, ts->proc_cmoffset_size, "signature mismatched %08X\n", signature);
goto out;
}
/* read history data */
address[0] = 0xA6;
address[1] = 0x00;
address[2] = (u8)ts->rx_count;
ret = ts->stm_ts_read(ts, &address[0], 3, rbuff, size);
if (ret < 0) {
input_err(true, &ts->client->dev,
"%s: read data failed. ret: %d\n", __func__, ret);
snprintf(buf, ts->proc_cmoffset_size, "NG, failed to read data %d", ret);
goto out;
}
memset(buf, 0x00, ts->proc_cmoffset_size);
for (i = 0; i < ts->tx_count; i++) {
char buff[4] = { 0 };
for (j = 0; j < ts->rx_count; j++) {
snprintf(buff, sizeof(buff), " %d", rbuff[i * ts->tx_count + j]);
strlcat(buf, buff, ts->proc_cmoffset_size);
}
snprintf(buff, sizeof(buff), "\n");
strlcat(buf, buff, ts->proc_cmoffset_size);
}
out:
input_err(true, &ts->client->dev, "%s: pos:%d, buf size:%zu\n", __func__, position, strlen(buf));
kfree(rbuff);
return strlen(buf);
}
static ssize_t stm_ts_tsp_cmoffset_all_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
struct stm_ts_data *ts = dev_get_drvdata(ptsp);
static ssize_t retlen = 0;
ssize_t retlen_sdc = 0, retlen_sub = 0, retlen_main = 0;
ssize_t count;
loff_t pos = *offset;
#if IS_ENABLED(CONFIG_SEC_FACTORY)
int ret;
#endif
if (!ts) {
pr_err("%s %s: dev is null\n", SECLOG, __func__);
return 0;
}
if (pos == 0) {
#if IS_ENABLED(CONFIG_SEC_FACTORY)
ret = get_cmoffset_dump(ts, ts->cmoffset_sdc_proc, OFFSET_FW_SDC);
if (ret < 0)
input_err(true, &ts->client->dev,
"%s: SDC fail use boot time value\n", __func__);
ret = get_cmoffset_dump(ts, ts->cmoffset_sub_proc, OFFSET_FW_SUB);
if (ret < 0)
input_err(true, &ts->client->dev,
"%s: SUB fail use boot time value\n", __func__);
ret = get_cmoffset_dump(ts, ts->cmoffset_main_proc, OFFSET_FW_MAIN);
if (ret < 0)
input_err(true, &ts->client->dev,
"%s: MAIN fail use boot time value\n", __func__);
#endif
retlen_sdc = strlen(ts->cmoffset_sdc_proc);
retlen_sub = strlen(ts->cmoffset_sub_proc);
retlen_main = strlen(ts->cmoffset_main_proc);
ts->cmoffset_all_proc = kzalloc(ts->proc_cmoffset_all_size, GFP_KERNEL);
if (!ts->cmoffset_all_proc) {
input_err(true, &ts->client->dev, "%s: kzalloc fail (cmoffset_all_proc)\n", __func__);
return 0;
}
strlcat(ts->cmoffset_all_proc, ts->cmoffset_sdc_proc, ts->proc_cmoffset_all_size);
strlcat(ts->cmoffset_all_proc, ts->cmoffset_sub_proc, ts->proc_cmoffset_all_size);
strlcat(ts->cmoffset_all_proc, ts->cmoffset_main_proc, ts->proc_cmoffset_all_size);
retlen = strlen(ts->cmoffset_all_proc);
input_info(true, &ts->client->dev, "%s: retlen[%ld], retlen_sdc[%ld], retlen_sub[%ld], retlen_main[%ld]\n",
__func__, retlen, retlen_sdc, retlen_sub, retlen_main);
}
if (pos >= retlen)
return 0;
count = min(len, (size_t)(retlen - pos));
input_info(true, &ts->client->dev, "%s: total:%ld count:%ld\n", __func__, retlen, count);
if (copy_to_user(buf, ts->cmoffset_all_proc + pos, count)) {
input_err(true, &ts->client->dev, "%s: copy_to_user error!\n", __func__);
return -EFAULT;
}
*offset += count;
if (count < len) {
input_info(true, &ts->client->dev, "%s: print all & free cmoffset_all_proc\n", __func__);
if (ts->cmoffset_all_proc)
kfree(ts->cmoffset_all_proc);
retlen = 0;
}
return count;
}
#if IS_ENABLED(CONFIG_TOUCHSCREEN_DUMP_MODE)
void stm_ts_check_rawdata(struct work_struct *work)
{
struct stm_ts_data *ts = container_of(work, struct stm_ts_data, check_rawdata.work);
if (ts->tsp_dump_lock == 1) {
input_err(true, &ts->client->dev, "%s: ignored ## already checking..\n", __func__);
return;
}
if (ts->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) {
input_err(true, &ts->client->dev, "%s: ignored ## IC is power off\n", __func__);
return;
}
stm_ts_run_rawdata_all(ts);
}
void stm_ts_dump_tsp_log(struct device *dev)
{
struct stm_ts_data *ts = dev_get_drvdata(dev);
pr_info("%s: %s %s: start\n", STM_TS_I2C_NAME, SECLOG, __func__);
if (!ts) {
pr_err("%s: %s %s: ignored ## tsp probe fail!!\n", STM_TS_I2C_NAME, SECLOG, __func__);
return;
}
schedule_delayed_work(&ts->check_rawdata, msecs_to_jiffies(100));
}
void stm_ts_sponge_dump_flush(struct stm_ts_data *ts, int dump_area)
{
int i, ret;
unsigned char *sec_spg_dat;
input_info(true, &ts->client->dev, "%s: ++\n", __func__);
sec_spg_dat = vmalloc(SEC_TS_MAX_SPONGE_DUMP_BUFFER);
if (!sec_spg_dat) {
input_err(true, &ts->client->dev, "%s : Failed!!\n", __func__);
return;
}
memset(sec_spg_dat, 0, SEC_TS_MAX_SPONGE_DUMP_BUFFER);
input_info(true, &ts->client->dev, "%s: dump area=%d\n", __func__, dump_area);
/* check dump area */
if (dump_area == 0) {
sec_spg_dat[0] = SEC_TS_CMD_SPONGE_LP_DUMP_EVENT;
sec_spg_dat[1] = 0;
} else {
sec_spg_dat[0] = (u8)ts->sponge_dump_border & 0xff;
sec_spg_dat[1] = (u8)(ts->sponge_dump_border >> 8);
}
/* dump all events at once */
if (ts->sponge_dump_event * ts->sponge_dump_format > SEC_TS_MAX_SPONGE_DUMP_BUFFER) {
input_err(true, &ts->client->dev, "%s: wrong sponge dump read size (%d)\n",
__func__, ts->sponge_dump_event * ts->sponge_dump_format);
vfree(sec_spg_dat);
return;
}
ret = ts->stm_ts_read_sponge(ts, sec_spg_dat, ts->sponge_dump_event * ts->sponge_dump_format);
if (ret < 0) {
input_err(true, &ts->client->dev, "%s: Failed to read sponge\n", __func__);
vfree(sec_spg_dat);
return;
}
for (i = 0 ; i < ts->sponge_dump_event ; i++) {
int e_offset = i * ts->sponge_dump_format;
char buff[30] = {0, };
u16 edata[5];
edata[0] = (sec_spg_dat[1 + e_offset] & 0xFF) << 8 | (sec_spg_dat[0 + e_offset] & 0xFF);
edata[1] = (sec_spg_dat[3 + e_offset] & 0xFF) << 8 | (sec_spg_dat[2 + e_offset] & 0xFF);
edata[2] = (sec_spg_dat[5 + e_offset] & 0xFF) << 8 | (sec_spg_dat[4 + e_offset] & 0xFF);
edata[3] = (sec_spg_dat[7 + e_offset] & 0xFF) << 8 | (sec_spg_dat[6 + e_offset] & 0xFF);
edata[4] = (sec_spg_dat[9 + e_offset] & 0xFF) << 8 | (sec_spg_dat[8 + e_offset] & 0xFF);
if (edata[0] || edata[1] || edata[2] || edata[3] || edata[4]) {
snprintf(buff, sizeof(buff), "%03d: %04x%04x%04x%04x%04x\n",
i + (ts->sponge_dump_event * dump_area),
edata[0], edata[1], edata[2], edata[3], edata[4]);
#if IS_ENABLED(CONFIG_SEC_DEBUG_TSP_LOG)
sec_tsp_sponge_log(buff);
#endif
}
}
vfree(sec_spg_dat);
input_info(true, &ts->client->dev, "%s: --\n", __func__);
return;
}
EXPORT_SYMBOL(stm_ts_sponge_dump_flush);
#endif
static int stm_ts_init_check(struct stm_ts_data *ts)
{
if (ts->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) {
input_err(true, &ts->client->dev, "%s: Touch is stopped\n", __func__);
return -EPERM;
}
if (ts->plat_data->power_state == SEC_INPUT_STATE_LPM) {
input_err(true, &ts->client->dev, "%s: Touch is LP mode\n", __func__);
return -EPERM;
}
if (ts->reset_is_on_going) {
input_err(true, &ts->client->dev, "%s: Reset is ongoing!\n", __func__);
return -EPERM;
}
if (ts->sec.cmd_is_running) {
input_err(true, &ts->client->dev, "%s: cmd is running\n", __func__);
return -EBUSY;
}
return 0;
}
ssize_t get_cmoffset_dump(struct stm_ts_data *ts, char *buf, u8 position)
{
int ret = 0;
ret = stm_ts_init_check(ts);
if (ret < 0) {
return ret;
}
if (ts->plat_data->dump_ic_ver == STM_TS_GET_CMOFFSET_DUMP_V1) {
return get_cmoffset_dump_v1(ts, buf, position);
} else if (ts->plat_data->dump_ic_ver == STM_TS_GET_CMOFFSET_DUMP_V2) {
return get_cmoffset_dump_v2(ts, buf, position);
}
return strlen(buf);
}
static ssize_t stm_ts_tsp_cmoffset_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
pr_info("[sec_input] %s called offset:%d\n", __func__, (int)*offset);
return stm_ts_tsp_cmoffset_all_read(file, buf, len, offset);
}
static sec_input_proc_ops(THIS_MODULE, tsp_cmoffset_all_file_ops, stm_ts_tsp_cmoffset_read, NULL);
void stm_ts_init_proc(struct stm_ts_data *ts)
{
struct proc_dir_entry *entry_cmoffset_all;
ts->proc_cmoffset_size = (ts->tx_count * ts->rx_count * 4 + 100) * 4;
ts->proc_cmoffset_all_size = ts->proc_cmoffset_size * 2;
ts->cmoffset_sdc_proc = kzalloc(ts->proc_cmoffset_size, GFP_KERNEL);
if (!ts->cmoffset_sdc_proc)
return;
ts->cmoffset_sub_proc = kzalloc(ts->proc_cmoffset_size, GFP_KERNEL);
if (!ts->cmoffset_sub_proc)
goto err_alloc_sub;
ts->cmoffset_main_proc = kzalloc(ts->proc_cmoffset_size, GFP_KERNEL);
if (!ts->cmoffset_main_proc)
goto err_alloc_main;
if (ts->plat_data->support_dual_foldable == SUB_TOUCH)
entry_cmoffset_all = proc_create("tsp_cmoffset_all_sub", S_IFREG | 0444, NULL, &tsp_cmoffset_all_file_ops);
else
entry_cmoffset_all = proc_create("tsp_cmoffset_all", S_IFREG | 0444, NULL, &tsp_cmoffset_all_file_ops);
if (!entry_cmoffset_all) {
input_err(true, &ts->client->dev, "%s: failed to create /proc/tsp_cmoffset_all\n", __func__);
goto err_cmoffset_proc_create;
}
input_info(true, &ts->client->dev, "%s: done\n", __func__);
return;
err_cmoffset_proc_create:
kfree(ts->cmoffset_main_proc);
err_alloc_main:
kfree(ts->cmoffset_sub_proc);
err_alloc_sub:
kfree(ts->cmoffset_sdc_proc);
ts->cmoffset_sdc_proc = NULL;
ts->cmoffset_sub_proc = NULL;
ts->cmoffset_main_proc = NULL;
input_err(true, &ts->client->dev, "%s: failed\n", __func__);
}
MODULE_LICENSE("GPL");