| /* 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 <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/slab.h> |
| #include <linux/i2c.h> |
| #include <linux/input.h> |
| #include <linux/input/mt.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/platform_device.h> |
| #include <linux/firmware.h> |
| #include <linux/gpio.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/irq.h> |
| #include <linux/of_gpio.h> |
| #include <linux/time.h> |
| #include <linux/vmalloc.h> |
| |
| #include <linux/uaccess.h> |
| /*#include <asm/gpio.h>*/ |
| |
| #include "sec_ts.h" |
| |
| u8 lv1cmd; |
| u8 *read_lv1_buff; |
| static int lv1_readsize; |
| static int lv1_readremain; |
| static int lv1_readoffset; |
| |
| 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 DEVICE_ATTR(sec_ts_reg, (S_IWUSR | S_IWGRP), NULL, sec_ts_reg_store); |
| static DEVICE_ATTR(sec_ts_regreadsize, (S_IWUSR | S_IWGRP), NULL, sec_ts_regreadsize_store); |
| static DEVICE_ATTR(sec_ts_enter_recovery, (S_IWUSR | S_IWGRP), NULL, sec_ts_enter_recovery_store); |
| static DEVICE_ATTR(sec_ts_regread, S_IRUGO, sec_ts_regread_show, NULL); |
| static DEVICE_ATTR(sec_ts_gesture_status, S_IRUGO, sec_ts_gesture_status_show, NULL); |
| |
| 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, |
| NULL, |
| }; |
| |
| static struct attribute_group cmd_attr_group = { |
| .attrs = cmd_attributes, |
| }; |
| |
| /* for debugging--------------------------------------------------------------------------------------*/ |
| 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_i2c_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; |
| int length; |
| int remain; |
| int offset; |
| |
| 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; |
| |
| remain = lv1_readsize; |
| offset = 0; |
| do { |
| if (remain >= ts->i2c_burstmax) |
| length = ts->i2c_burstmax; |
| else |
| length = remain; |
| |
| if (offset == 0) |
| ret = ts->sec_ts_i2c_read_heap(ts, lv1cmd, |
| &read_lv1_buff[offset], length); |
| else |
| ret = ts->sec_ts_i2c_read_bulk_heap(ts, |
| &read_lv1_buff[offset], length); |
| |
| if (ret < 0) { |
| input_err(true, &ts->client->dev, "%s: i2c read %x command, remain =%d\n", __func__, lv1cmd, remain); |
| goto i2c_err; |
| } |
| |
| remain -= length; |
| offset += length; |
| } while (remain > 0); |
| |
| 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_i2c_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; |
| } |