/* drivers/input/touchscreen/sec_ts_fw.c * * Copyright (C) 2015 Samsung Electronics Co., Ltd. * http://www.samsungsemi.com/ * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*#include */ #include "sec_ts.h" u8 lv1cmd; u8 *read_lv1_buff; static int lv1_readsize; static int lv1_readremain; static int lv1_readoffset; u8 lv1cmd_manual; static int lv1_readsize_manual; static int lv1_readremain_manual; static int lv1_readoffset_manual; #define SEC_TS_CMD_BUF_SZ 64 static u8 cmd_buf[SEC_TS_CMD_BUF_SZ]; static int cmd_buf_num; static ssize_t sec_ts_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t sec_ts_regreadsize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static inline ssize_t sec_ts_store_error(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t sec_ts_enter_recovery_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t sec_ts_regread_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t sec_ts_gesture_status_show(struct device *dev, struct device_attribute *attr, char *buf); static inline ssize_t sec_ts_show_error(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t sec_ts_reg_manual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t sec_ts_regreadsize_manual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t sec_ts_regread_manual_show(struct device *dev, struct device_attribute *attr, char *buf); static DEVICE_ATTR_WO(sec_ts_reg); static DEVICE_ATTR_WO(sec_ts_regreadsize); static DEVICE_ATTR_WO(sec_ts_enter_recovery); static DEVICE_ATTR_RO(sec_ts_regread); static DEVICE_ATTR_RO(sec_ts_gesture_status); static DEVICE_ATTR_WO(sec_ts_reg_manual); static DEVICE_ATTR_WO(sec_ts_regreadsize_manual); static DEVICE_ATTR_RO(sec_ts_regread_manual); static struct attribute *cmd_attributes[] = { &dev_attr_sec_ts_reg.attr, &dev_attr_sec_ts_regreadsize.attr, &dev_attr_sec_ts_enter_recovery.attr, &dev_attr_sec_ts_regread.attr, &dev_attr_sec_ts_gesture_status.attr, &dev_attr_sec_ts_reg_manual.attr, &dev_attr_sec_ts_regreadsize_manual.attr, &dev_attr_sec_ts_regread_manual.attr, NULL, }; static struct attribute_group cmd_attr_group = { .attrs = cmd_attributes, }; /* for debugging-------------------------------------------------------------*/ static void sec_ts_parsing_cmds(struct device *dev, const char *buf, size_t size, bool write) { u8 result, n = 0; char *p, *temp_buf, *token; size_t token_len = 0; struct sec_ts_data *ts = dev_get_drvdata(dev); /* clear cmd_buf */ memset(cmd_buf, 0, sizeof(cmd_buf)); cmd_buf_num = 0; /* pre-check input */ if (write) { if (size < 2) { input_info(true, &ts->client->dev, "%s: invalid input size %d\n", __func__); return; } } else { if (size < 3) { input_info(true, &ts->client->dev, "%s: invalid input size %d\n", __func__); return; } else if (buf[0] != 'R' && buf[0] != 'r') { input_info(true, &ts->client->dev, "%s: invalid input %c, Have to start with R(r)\n", __func__, buf[0]); return; } } /* alloc temp_buf for parsing * read case will skip 1st character */ temp_buf = kstrdup(buf, GFP_KERNEL); if (!temp_buf) { pr_err("%s: memory allocation failed!", __func__); return; } p = temp_buf; /* newline case at last char */ if (p[size - 1] == '\n') p[size - 1] = '\0'; /* skip 1st character for read case */ if (!write) p++; /* parsing */ while (p && (n < SEC_TS_CMD_BUF_SZ)) { while (isspace(*p)) p++; token = strsep(&p, " "); if (!token || *token == '\0') break; token_len = strlen(token); if (token_len != 2) { pr_err("%s: bad len %zu\n", __func__, token_len); n = 0; break; } if (kstrtou8(token, 16, &result)) { /* Conversion failed due to bad input. * Discard the entire buffer. */ pr_err("%s: bad input\n", __func__); n = 0; break; } /* found a valid cmd/args */ cmd_buf[n] = result; n++; } kfree(temp_buf); cmd_buf_num = n; } /* sysfs file node to write reg * * echo _REG_ _VAL_ ... > sec_ts_reg_manual * * e.g. write reg 0xD7 with 0x02 0x04 * echo D7 02 04 > sec_ts_reg_manual */ static ssize_t sec_ts_reg_manual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sec_ts_data *ts = dev_get_drvdata(dev); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_info(true, &ts->client->dev, "%s: Power off state\n", __func__); return -EIO; } sec_ts_parsing_cmds(dev, buf, size, true); if (cmd_buf_num) { ts->sec_ts_write_burst(ts, cmd_buf, cmd_buf_num); input_info(true, &ts->client->dev, "%s: size %d, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", __func__, cmd_buf_num, cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3], cmd_buf[4]); } return size; } /* sysfs file node to read reg * * step 1: set reg and size that want to read * echo R_REG_ _SIZE_ > sec_ts_regreadsize_manual * * e.g. read reg 0x52 for 3 bytes * echo R52 03 > sec_ts_regreadsize_manual * * step 2: read reg * cat sec_ts_regread_manual */ static ssize_t sec_ts_regreadsize_manual_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sec_ts_data *ts = dev_get_drvdata(dev); sec_ts_parsing_cmds(dev, buf, size, false); if (cmd_buf_num == 2) { lv1cmd_manual = cmd_buf[0]; lv1_readsize_manual = cmd_buf[1]; lv1_readoffset_manual = 0; lv1_readremain_manual = 0; input_info(true, &ts->client->dev, "%s: read reg %X sz %d\n", __func__, lv1cmd_manual, lv1_readsize_manual); } else input_info(true, &ts->client->dev, "%s: invalid input to reg read! cmd_num %d, cmd %x %x\n", __func__, cmd_buf_num, cmd_buf[0], cmd_buf[1]); return size; } /* sysfs file node to read reg * check sec_ts_regreadsize_manual_store() above for details. */ static ssize_t sec_ts_regread_manual_show(struct device *dev, struct device_attribute *attr, char *buf) { u8 *read_lv1_buff_manual; struct sec_ts_data *ts = dev_get_drvdata(dev); unsigned int str_len = 0; int ret = 0, i; if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, "%s: Power off state\n", __func__); return -EIO; } if (lv1_readsize_manual < 1) { input_err(true, &ts->client->dev, "%s: Nothing to read\n", __func__); return -EIO; } disable_irq(ts->client->irq); read_lv1_buff_manual = kzalloc(lv1_readsize_manual, GFP_KERNEL); if (!read_lv1_buff_manual) goto malloc_err; ret = ts->sec_ts_read_heap(ts, lv1cmd_manual, read_lv1_buff_manual, lv1_readsize_manual); if (ret < 0) input_err(true, &ts->client->dev, "%s: read reg %x failed!\n", __func__, lv1cmd_manual); else { for (i = 0 ; i < lv1_readsize_manual ; i++) str_len += scnprintf(buf + str_len, PAGE_SIZE - str_len, "%02X ", (u8)read_lv1_buff_manual[i]); str_len += scnprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); input_info(true, &ts->client->dev, "%s: reg %X sz %d -> %s\n", __func__, lv1cmd_manual, lv1_readsize_manual, buf); } kfree(read_lv1_buff_manual); malloc_err: lv1_readremain_manual = 0; enable_irq(ts->client->irq); return str_len; } static ssize_t sec_ts_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sec_ts_data *ts = dev_get_drvdata(dev); if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_info(true, &ts->client->dev, "%s: Power off state\n", __func__); return -EIO; } if (size > 0) ts->sec_ts_write_burst(ts, (u8 *)buf, size); input_info(true, &ts->client->dev, "%s: 0x%x, 0x%x, size %d\n", __func__, buf[0], buf[1], (int)size); return size; } static ssize_t sec_ts_regread_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_ts_data *ts = dev_get_drvdata(dev); int ret; if (ts->power_status == SEC_TS_STATE_POWER_OFF) { input_err(true, &ts->client->dev, "%s: Power off state\n", __func__); return -EIO; } disable_irq(ts->client->irq); mutex_lock(&ts->device_mutex); read_lv1_buff = kzalloc(lv1_readsize, GFP_KERNEL); if (!read_lv1_buff) goto malloc_err; ret = ts->sec_ts_read_heap(ts, lv1cmd, read_lv1_buff, lv1_readsize); if (ret < 0) { input_err(true, &ts->client->dev, "%s: read %x command fail\n", __func__, lv1cmd); goto i2c_err; } input_info(true, &ts->client->dev, "%s: lv1_readsize = %d\n", __func__, lv1_readsize); memcpy(buf, read_lv1_buff + lv1_readoffset, lv1_readsize); i2c_err: kfree(read_lv1_buff); malloc_err: mutex_unlock(&ts->device_mutex); lv1_readremain = 0; enable_irq(ts->client->irq); return lv1_readsize; } static ssize_t sec_ts_gesture_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_ts_data *ts = dev_get_drvdata(dev); mutex_lock(&ts->device_mutex); memcpy(buf, ts->gesture_status, sizeof(ts->gesture_status)); input_info(true, &ts->client->dev, "%s: GESTURE STATUS %x %x %x %x %x %x\n", __func__, ts->gesture_status[0], ts->gesture_status[1], ts->gesture_status[2], ts->gesture_status[3], ts->gesture_status[4], ts->gesture_status[5]); mutex_unlock(&ts->device_mutex); return sizeof(ts->gesture_status); } static ssize_t sec_ts_regreadsize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sec_ts_data *ts = dev_get_drvdata(dev); mutex_lock(&ts->device_mutex); lv1cmd = buf[0]; lv1_readsize = ((unsigned int)buf[4] << 24) | ((unsigned int)buf[3] << 16) | ((unsigned int) buf[2] << 8) | ((unsigned int)buf[1] << 0); lv1_readoffset = 0; lv1_readremain = 0; mutex_unlock(&ts->device_mutex); return size; } static ssize_t sec_ts_enter_recovery_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct sec_ts_data *ts = dev_get_drvdata(dev); struct sec_ts_plat_data *pdata = ts->plat_data; int ret; unsigned long on; ret = kstrtoul(buf, 10, &on); if (ret != 0) { input_err(true, &ts->client->dev, "%s: failed to read:%d\n", __func__, ret); return -EINVAL; } if (on == 1) { disable_irq(ts->client->irq); gpio_free(pdata->irq_gpio); input_info(true, &ts->client->dev, "%s: gpio free\n", __func__); if (gpio_is_valid(pdata->irq_gpio)) { ret = gpio_request_one(pdata->irq_gpio, GPIOF_OUT_INIT_LOW, "sec,tsp_int"); input_info(true, &ts->client->dev, "%s: gpio request one\n", __func__); if (ret < 0) input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]: %d\n", __func__, pdata->irq_gpio, ret); } else { input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); return -EINVAL; } pdata->power(ts, false); sec_ts_delay(100); pdata->power(ts, true); } else { gpio_free(pdata->irq_gpio); if (gpio_is_valid(pdata->irq_gpio)) { ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); if (ret) { input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); return -EINVAL; } } else { input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); return -EINVAL; } pdata->power(ts, false); sec_ts_delay(500); pdata->power(ts, true); sec_ts_delay(500); /* AFE Calibration */ ret = ts->sec_ts_write(ts, SEC_TS_CMD_CALIBRATION_AMBIENT, NULL, 0); if (ret < 0) input_err(true, &ts->client->dev, "%s: fail to write AFE_CAL\n", __func__); sec_ts_delay(1000); enable_irq(ts->client->irq); } sec_ts_read_information(ts); return size; } static inline ssize_t sec_ts_show_error(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_ts_data *ts = dev_get_drvdata(dev); input_err(true, &ts->client->dev, "%s: read only function, %s\n", __func__, attr->attr.name); return -EPERM; } static inline ssize_t sec_ts_store_error(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_ts_data *ts = dev_get_drvdata(dev); input_err(true, &ts->client->dev, "%s: write only function, %s\n", __func__, attr->attr.name); return -EPERM; } int sec_ts_raw_device_init(struct sec_ts_data *ts) { int ret; #ifdef CONFIG_SEC_SYSFS ts->dev = sec_device_create(ts, "sec_ts"); #else ts->dev = device_create(sec_class, NULL, 0, ts, "sec_ts"); #endif ret = IS_ERR(ts->dev); if (ret) { input_err(true, &ts->client->dev, "%s: fail - device_create\n", __func__); return ret; } ret = sysfs_create_group(&ts->dev->kobj, &cmd_attr_group); if (ret < 0) { input_err(true, &ts->client->dev, "%s: fail - sysfs_create_group\n", __func__); goto err_sysfs; } return ret; err_sysfs: input_err(true, &ts->client->dev, "%s: fail\n", __func__); return ret; } void sec_ts_raw_device_exit(struct sec_ts_data *ts) { sysfs_remove_group(&ts->dev->kobj, &cmd_attr_group); #ifdef CONFIG_SEC_SYSFS sec_device_destroy(ts->dev->devt); #else device_destroy(sec_class, 0); #endif }