input: cy8c_cs: Add Cypress CY8C20x34 touch key support

HTC kernel version: villeu-jb-crc-3.4.10-ae8b65e

Change-Id: Iec720c415be25346c84969d7d4cd11ae02266724
diff --git a/drivers/input/touchscreen/cy8c_cs.c b/drivers/input/touchscreen/cy8c_cs.c
new file mode 100644
index 0000000..006c089
--- /dev/null
+++ b/drivers/input/touchscreen/cy8c_cs.c
@@ -0,0 +1,813 @@
+/* drivers/input/touchscreen/cy8c_cs.c
+ *
+ * Copyright (C) 2011 HTC Corporation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/input/cy8c_cs.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+
+#define CY8C_I2C_RETRY_TIMES 	(10)
+#define CY8C_KEYLOCKTIME    	(1500)
+#define CY8C_KEYLOCKRESET	(6)
+
+struct cy8c_cs_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct workqueue_struct *cy8c_wq;
+	struct work_struct work;
+	struct early_suspend early_suspend;
+	int use_irq;
+	struct hrtimer timer;
+	uint16_t version;
+	struct infor id;
+	uint16_t intr;
+	uint8_t vk_id;
+	uint8_t debug_level;
+	int *keycode;
+	int (*power)(int on);
+	int (*reset)(void);
+	int func_support;
+	struct workqueue_struct *wq_raw;
+	struct delayed_work work_raw;
+};
+
+static struct cy8c_cs_data *private_cs;
+
+static irqreturn_t cy8c_cs_irq_handler(int, void *);
+static int disable_key;
+static int reset_cnt; 
+
+extern int board_build_flag(void);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_cs_early_suspend(struct early_suspend *h);
+static void cy8c_cs_late_resume(struct early_suspend *h);
+#endif
+
+int i2c_cy8c_read(struct i2c_client *client, uint8_t addr, uint8_t *data, uint8_t length)
+{
+	int retry;
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &addr,
+		},
+		{
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = data,
+		}
+	};
+
+	for (retry = 0; retry < CY8C_I2C_RETRY_TIMES; retry++) {
+		if (i2c_transfer(client->adapter, msg, 2) == 2)
+			break;
+		mdelay(10);
+	}
+	if (retry == CY8C_I2C_RETRY_TIMES) {
+		printk(KERN_INFO "[cap]i2c_read_block retry over %d\n",
+			CY8C_I2C_RETRY_TIMES);
+		return -EIO;
+	}
+	return 0;
+
+}
+
+int i2c_cy8c_write(struct i2c_client *client, uint8_t addr, uint8_t *data, uint8_t length)
+{
+	int retry, loop_i;
+	uint8_t buf[length + 1];
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = client->addr,
+			.flags = 0,
+			.len = length + 1,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = addr;
+	for (loop_i = 0; loop_i < length; loop_i++)
+		buf[loop_i + 1] = data[loop_i];
+
+	for (retry = 0; retry < CY8C_I2C_RETRY_TIMES; retry++) {
+		if (i2c_transfer(client->adapter, msg, 1) == 1)
+			break;
+		mdelay(10);
+	}
+
+	if (retry == CY8C_I2C_RETRY_TIMES) {
+		printk(KERN_ERR "[cap]i2c_write_block retry over %d\n",
+			CY8C_I2C_RETRY_TIMES);
+		return -EIO;
+	}
+	return 0;
+
+}
+
+int i2c_cy8c_write_byte_data(struct i2c_client *client, uint8_t addr, uint8_t value)
+{
+	return i2c_cy8c_write(client, addr, &value, 1);
+}
+
+static ssize_t diff(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret = 0, i;
+	char data[8] = {0};
+	struct cy8c_cs_data *cs;
+
+	pr_info("[cap] %s", __func__);
+
+	cs = private_cs;
+	ret = i2c_cy8c_write_byte_data(cs->client, CS_SELECT, CS_CMD_BASELINE);
+	if (ret < 0) {
+		pr_err("[cap] i2c Write baseline Err\n");
+		return ret;
+	}
+	msleep(100);
+	ret = i2c_cy8c_read(cs->client, CS_BL_HB, data, ARRAY_SIZE(data));
+	if (ret < 0) {
+		pr_err("[cap] i2c Read baseline Err\n");
+		return ret;
+	}
+
+	for (i = 0; i < 8 ; i += 2)
+		ret += sprintf(buf+ret, "BTN(%d)=%d, ", (i/2),
+			       (data[i] << 8 | data[i+1]));
+	ret += sprintf(buf+ret, "\n");
+
+	return ret;
+}
+static DEVICE_ATTR(diff, S_IRUGO, diff, NULL);
+
+static ssize_t reset(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct cy8c_cs_data *cs;
+	cs = private_cs;
+
+	pr_info("[cap] reset\n");
+	cs->reset();
+	ret = sprintf(buf, "Reset chip");
+	return ret;
+}
+static DEVICE_ATTR(reset, S_IRUGO, reset, NULL);
+
+static ssize_t stop_report(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	int err;
+	unsigned long i = 0;
+	err = strict_strtoul(buf, 10, &i);
+
+	if (disable_key < 2 || disable_key >= 0) {
+		disable_key = i;
+		pr_info("[cap] KEY Report %s!!\n", disable_key ?
+				"DISABLE" : "ENABLE");
+	} else
+		pr_info("[cap] Parameter Error\n");
+
+	return count;
+}
+
+static ssize_t show_flag(struct device *dev, struct device_attribute *attr,
+						 char *buf)
+{
+	return sprintf(buf, "[cap] disable_key = %d\n", disable_key);
+}
+static DEVICE_ATTR(diskey, (S_IWUSR|S_IRUGO), show_flag, stop_report);
+
+static int cy8c_printcs_raw(struct cy8c_cs_data *cs, char *buf)
+{
+	int ret = 0, pos = 0, i, j, cmd[4] = {CS_CMD_BTN1, CS_CMD_BTN2, CS_CMD_BTN3, CS_CMD_BTN4};
+	char data[6] = {0}, capstate[3][10] = {"BL", "Raw", "Dlt"};
+
+	for (i = 0; i < cs->id.config; i++) {
+		ret = i2c_cy8c_write_byte_data(cs->client, CS_SELECT, cmd[i]);
+		if (ret < 0) {
+			pr_err("[cap] i2c Write inform (%d_%#x) Err\n", i+1, cmd[i]);
+			return ret;
+		}
+		msleep(50);
+		ret = i2c_cy8c_read(cs->client, CS_BL_HB, data, ARRAY_SIZE(data));
+		if (ret < 0) {
+			pr_err("[cap] i2c Read inform (%d_%#x)) Err\n", i+1, cmd[i]);
+			return ret;
+		}
+		pos += sprintf(buf+pos, "BTN(%d)", i);
+		for (j = 0; j < 6 ; j += 2)
+			pos += sprintf(buf+pos, "%s=%d, ", capstate[j/2],
+				       (data[j] << 8 | data[j+1]));
+		pos += sprintf(buf+pos, "\n");
+		memset(data, 0, sizeof(ARRAY_SIZE(data)));
+	}
+	return pos;
+}
+
+static ssize_t inform(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret = 0, pos = 0;
+	char data[2] = {0};
+	struct cy8c_cs_data *cs;
+
+	cs = private_cs;
+
+	if (cs->id.version < 0x10 && cs->id.version > 0x08)
+		cs->id.version = cs->id.version << 4;
+
+	if (cs->id.version >= 0x86) {
+		memset(data, 0, sizeof(ARRAY_SIZE(data)));
+		ret = i2c_cy8c_read(cs->client, CS_INT_STATUS, data, 2);
+		if (ret < 0) {
+			pr_err("[cap] i2c Read inform INT status Err\n");
+			return ret;
+		}
+		pos += sprintf(buf+pos, "Btn code = %x, INT status= %x\n", data[0], data[1]);
+	}
+	pos += cy8c_printcs_raw(cs, buf+pos);
+
+	return pos;
+}
+static DEVICE_ATTR(inform, S_IRUGO, inform, NULL);
+
+static ssize_t cs_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char data[3] = {0};
+	int ret = 0;
+	struct cy8c_cs_data *cs;
+	cs = private_cs;
+
+	ret = i2c_cy8c_read(cs->client, CS_FW_VERSION, data, 2);
+	if (ret < 0) {
+		pr_err("[cap] i2c Read version Err\n");
+		return ret;
+	}
+	if (cs->id.chipid == CS_CHIPID)
+		sprintf(buf, "%s_V%x\n", CYPRESS_SS_NAME, data[0]);
+	else
+		sprintf(buf, "%s_V%x\n", CYPRESS_CS_NAME, data[0]);
+	ret += strlen(buf)+1;
+
+	return ret;
+}
+static DEVICE_ATTR(vendor, S_IRUGO, cs_vendor_show, NULL);
+
+static ssize_t cy8c_cs_gpio_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct cy8c_cs_data *cs_data;
+	struct cy8c_i2c_cs_platform_data *pdata;
+
+	cs_data = private_cs;
+	pdata = cs_data->client->dev.platform_data;
+
+	ret = gpio_get_value(pdata->gpio_irq);
+	printk(KERN_DEBUG "[cap] GPIO_CS_INT_N=%d\n", pdata->gpio_irq);
+	sprintf(buf, "GPIO_CS_INT_N=%d\n", ret);
+	ret = strlen(buf) + 1;
+	return ret;
+}
+static DEVICE_ATTR(gpio, S_IRUGO, cy8c_cs_gpio_show, NULL);
+
+static ssize_t cy8c_cs_read_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct cy8c_cs_data *cs_data;
+	struct cy8c_i2c_cs_platform_data *pdata;
+
+	cs_data = private_cs;
+	pdata = cs_data->client->dev.platform_data;
+
+	ret = gpio_get_value(pdata->gpio_irq);
+	printk(KERN_DEBUG "GPIO_CS_INT_N=%d\n", pdata->gpio_irq);
+	ret = strlen(buf) + 1;
+	return ret;
+}
+static DEVICE_ATTR(read, S_IRUGO, cy8c_cs_read_show, NULL);
+
+static ssize_t debug_level_set(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int i;
+	struct cy8c_cs_data *cs_data;
+	cs_data = private_cs;
+	if (sscanf(buf, "%d", &i) == 1 && i < 2) {
+		cs_data->debug_level = i;
+		pr_info("[cap] debug_level = %d\b", cs_data->debug_level);
+	} else
+		pr_info("[cap] Parameter Error\n");
+	return count;
+}
+
+static ssize_t debug_level_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct cy8c_cs_data *cs_data;
+	cs_data = private_cs;
+	return sprintf(buf, "[cap] debug_level = %d\n", cs_data->debug_level);
+}
+DEVICE_ATTR(debug_level, (S_IWUSR|S_IRUGO), debug_level_show, debug_level_set);
+
+static struct kobject *android_touchkey_kobj;
+
+static int cy8c_touchkey_sysfs_init(void)
+{
+	int ret;
+	android_touchkey_kobj = kobject_create_and_add("android_key", NULL);
+	if (android_touchkey_kobj == NULL) {
+		printk(KERN_ERR "%s: subsystem_register failed\n", __func__);
+		ret = -ENOMEM;
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_gpio.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file gpio failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_read.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file read failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_vendor.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file vendor failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_inform.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file inform failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_diff.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file inform failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_debug_level.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file debug_level failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_reset.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file debug_level failed\n", __func__);
+		return ret;
+	}
+	ret = sysfs_create_file(android_touchkey_kobj, &dev_attr_diskey.attr);
+	if (ret) {
+		printk(KERN_ERR "%s: sysfs_create_file debug_level failed\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static void cy8c_touchkey_sysfs_deinit(void)
+{
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_gpio.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_read.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_vendor.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_inform.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_diff.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_debug_level.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_reset.attr);
+	sysfs_remove_file(android_touchkey_kobj, &dev_attr_diskey.attr);
+	kobject_del(android_touchkey_kobj);
+}
+
+static void cy8c_rawdata_print(struct work_struct *work)
+{
+	char buf[150] = {0};
+	int pos = 0;
+	struct cy8c_cs_data *cs = container_of(work, struct cy8c_cs_data,
+					       work_raw.work);
+	pos += cy8c_printcs_raw(cs, buf+pos);
+	pos = strlen(buf)+1;
+	pr_info("[cap]%s\n", buf);
+
+	if (cs->vk_id) {
+		reset_cnt++;
+		if (reset_cnt % CY8C_KEYLOCKRESET == 0) {
+			pr_info("[cap] keylock reset\n");
+			cs->reset();
+			reset_cnt = 0;
+			cs->vk_id = 0;
+		}
+		queue_delayed_work(cs->wq_raw, &cs->work_raw,
+				   msecs_to_jiffies(CY8C_KEYLOCKTIME-500));
+	}
+}
+
+static int cy8c_init_sensor(struct cy8c_cs_data *cs, struct cy8c_i2c_cs_platform_data *pdata)
+{
+	uint8_t ver[2] = {0}, chip[2] = {0};
+	int ret = 0;
+	pr_info("[cap] %s\n", __func__);
+
+	ret = i2c_cy8c_read(cs->client, CS_FW_CHIPID, chip, 1);
+	if (ret < 0) {
+		printk(KERN_ERR "[cap_err] Chip Read Err\n");
+		goto err_fw_get_fail;
+	}
+
+	ret = i2c_cy8c_read(cs->client, CS_FW_VERSION, ver, 1);
+	if (!ret)
+		cs->id.version = ver[0];
+	else {
+		printk(KERN_ERR "[cap_err] Ver Read Err\n");
+		goto err_fw_get_fail;
+	}
+
+	if (chip[0] == CS_CHIPID) {
+		cs->id.chipid = chip[0];
+		pr_info("[cap] CY8C_Smart_V%x\n", cs->id.version);
+	} else
+		pr_info("[cap] CY8C_Cap_V%x\n", cs->id.version);
+
+	ver[0] = 0;
+	ret = i2c_cy8c_read(cs->client, CS_FW_KEYCFG, ver, 1);
+	if (ret < 0) {
+		printk(KERN_ERR "[cap_err] Config Read Err\n");
+		goto err_fw_get_fail;
+	} else {
+		if ((ver[0] != 0) && (ver[0] == CS_KEY_3 || ver[0] == CS_KEY_4))
+			cs->id.config = ver[0];
+		else
+			cs->id.config = 0;
+		pr_info("[cap] config = %d\n", cs->id.config);
+	}
+	return 0;
+
+err_fw_get_fail:
+	return ret;
+}
+
+static void report_key_func(struct cy8c_cs_data *cs, uint8_t vk)
+{
+	int ret = 0;
+	if ((cs->debug_level & 0x01) || board_build_flag() > 0)
+		pr_info("[cap] vk = %x\n", vk);
+
+	if (vk) {
+		switch (vk) {
+		case 0x01:
+			input_report_key(cs->input_dev, cs->keycode[0], 1);
+			cs->vk_id = vk;
+			break;
+		case 0x02:
+			input_report_key(cs->input_dev, cs->keycode[1], 1);
+			cs->vk_id = vk;
+			break;
+		case 0x04:
+			input_report_key(cs->input_dev, cs->keycode[2], 1);
+			cs->vk_id = vk;
+			break;
+		case 0x08:
+			input_report_key(cs->input_dev, cs->keycode[3], 1);
+			cs->vk_id = vk;
+			break;
+		}
+#if defined(CONFIG_TOUCH_KEY_FILTER)
+		blocking_notifier_call_chain(&touchkey_notifier_list, 1, NULL);
+#endif
+	} else {
+		switch (cs->vk_id) {
+		case 0x01:
+			input_report_key(cs->input_dev, cs->keycode[0], 0);
+			break;
+		case 0x02:
+			input_report_key(cs->input_dev, cs->keycode[1], 0);
+			break;
+		case 0x04:
+			input_report_key(cs->input_dev, cs->keycode[2], 0);
+			break;
+		case 0x08:
+			input_report_key(cs->input_dev, cs->keycode[3], 0);
+			break;
+		}
+		cs->vk_id = 0;
+	}
+	input_sync(cs->input_dev);
+
+	if (cs->func_support & CS_FUNC_PRINTRAW) {
+		if (cs->vk_id) {
+			queue_delayed_work(cs->wq_raw, &cs->work_raw,
+					   msecs_to_jiffies(CY8C_KEYLOCKTIME));
+		} else {
+			ret = cancel_delayed_work_sync(&cs->work_raw);
+			if (!ret)
+				cancel_delayed_work(&cs->work_raw);
+		}
+	}
+}
+
+static void cy8c_cs_work_func(struct work_struct *work)
+{
+	struct cy8c_cs_data *cs;
+	uint8_t buf[3] = {0};
+	static	uint8_t pre_buf[3] = {0};
+
+	cs = container_of(work, struct cy8c_cs_data, work);
+
+	if (i2c_cy8c_read(cs->client, CS_STATUS, buf, 2) < 0) {
+		memset(buf, 0, sizeof(buf));
+		memset(pre_buf, 0, sizeof(pre_buf));
+		pr_err("[cap_err] %s i2c read fail", __func__);
+		goto enableirq;
+	}
+
+	if (!disable_key)
+		report_key_func(cs, buf[0]);
+
+	memcpy(pre_buf, buf, 2);
+enableirq:
+	if (!cs->use_irq)
+		hrtimer_start(&cs->timer, ktime_set(0, 20000000), HRTIMER_MODE_REL);
+	else
+		enable_irq(cs->client->irq);
+}
+
+#if 1
+static enum hrtimer_restart cy8c_cs_timer_func(struct hrtimer *timer)
+{
+	struct cy8c_cs_data *cs;
+
+	cs = container_of(timer, struct cy8c_cs_data, timer);
+	queue_work(cs->cy8c_wq, &cs->work);
+	return HRTIMER_NORESTART;
+}
+#endif
+#if 1
+static irqreturn_t cy8c_cs_irq_handler(int irq, void *dev_id)
+{
+	struct cy8c_cs_data *cs = dev_id;
+
+	disable_irq_nosync(cs->client->irq);
+	queue_work(cs->cy8c_wq, &cs->work);
+	return IRQ_HANDLED;
+}
+#endif
+
+static int cy8c_cs_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct cy8c_cs_data *cs;
+	struct cy8c_i2c_cs_platform_data *pdata;
+	int ret = 0;
+
+	printk(KERN_DEBUG "[cap] %s: enter\n", __func__);
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		printk(KERN_ERR "[cap_err] need I2C_FUNC_I2C\n");
+		ret = -ENODEV;
+		goto err_check_functionality_failed;
+	}
+
+	cs = kzalloc(sizeof(struct cy8c_cs_data), GFP_KERNEL);
+	if (cs == NULL) {
+		printk(KERN_ERR "[cap_err] allocate cy8c_cs_data failed\n");
+		ret = -ENOMEM;
+		goto err_alloc_data_failed;
+	}
+
+	cs->client = client;
+	i2c_set_clientdata(client, cs);
+	pdata = client->dev.platform_data;
+
+	if (pdata) {
+		pdata->reset();
+		msleep(50);
+		cs->intr = pdata->gpio_irq;
+	}
+
+	if (cy8c_init_sensor(cs, pdata) < 0) {
+		pr_err("[cap_err] init failure, not probe up driver\n");
+		goto err_init_sensor_failed;
+	}
+	if (pdata) {
+		if (pdata->id.config != cs->id.config) {
+			pr_info("[cap] pdata ++\n");
+			pdata++;
+		}
+	}
+
+	cs->cy8c_wq = create_singlethread_workqueue("cypress_touchkey");
+	if (!cs->cy8c_wq) {
+		printk(KERN_ERR "[cap_err] create_singlethread_workqueue cy8c_wq fail\n");
+		goto err_create_wq_failed;
+	}
+	INIT_WORK(&cs->work, cy8c_cs_work_func);
+
+	cs->input_dev = input_allocate_device();
+	if (cs->input_dev == NULL) {
+		ret = -ENOMEM;
+		printk(KERN_ERR "[cap_err] Failed to allocate input device\n");
+		goto err_input_dev_alloc_failed;
+	}
+	cs->input_dev->name       = "cy8c-touchkey";
+	cs->input_dev->id.product = cs->id.chipid;
+	cs->input_dev->id.version = cs->id.version;
+	cs->func_support          = pdata->func_support;
+	cs->keycode               = pdata->keycode;
+	cs->reset                 = pdata->reset;
+
+	set_bit(EV_SYN, cs->input_dev->evbit);
+	set_bit(EV_KEY, cs->input_dev->evbit);
+
+	set_bit(KEY_BACK, cs->input_dev->keybit);
+	set_bit(KEY_HOME, cs->input_dev->keybit);
+	set_bit(KEY_APP_SWITCH, cs->input_dev->keybit);
+	set_bit(KEY_MENU, cs->input_dev->keybit);
+
+	set_bit(KEY_SEARCH, cs->input_dev->keybit);
+	set_bit(KEY_WEIBO, cs->input_dev->keybit);
+
+	ret = input_register_device(cs->input_dev);
+	if (ret) {
+		printk(KERN_ERR "[cap_err] unable to register %s input device\n",
+			cs->input_dev->name);
+
+		goto err_input_register_device_failed;
+	}
+
+	private_cs = cs;
+
+	if (cs->func_support & CS_FUNC_PRINTRAW) {
+		pr_info("[cap]support_keylock(%x)\n", cs->func_support);
+		cs->wq_raw = create_singlethread_workqueue("CY8C_print_rawdata");
+		if (!cs->wq_raw) {
+			pr_err("[cap]allocate cy8c_cs_print_rawdata failed\n");
+			ret = -ENOMEM;
+			goto err_input_register_device_failed;
+		}
+		INIT_DELAYED_WORK(&cs->work_raw, cy8c_rawdata_print);
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	cs->early_suspend.level   = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+	cs->early_suspend.suspend = cy8c_cs_early_suspend;
+	cs->early_suspend.resume  = cy8c_cs_late_resume;
+	register_early_suspend(&cs->early_suspend);
+#endif
+	cy8c_touchkey_sysfs_init();
+
+	cs->use_irq = 1;
+	if (client->irq && cs->use_irq) {
+		ret = request_irq(client->irq, cy8c_cs_irq_handler,
+				  IRQF_TRIGGER_FALLING,
+				  cs->id.chipid == CS_CHIPID ? CYPRESS_SS_NAME : CYPRESS_CS_NAME,
+				  cs);
+		if (ret < 0) {
+			dev_err(&client->dev, "[cap_err]request_irq failed\n");
+			printk(KERN_ERR "[cap_err] request_irq failed for gpio %d,"
+			       " irq %d\n", cs->intr, client->irq);
+		}
+	}
+
+	if (!cs->use_irq) {
+		hrtimer_init(&cs->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		cs->timer.function = cy8c_cs_timer_func;
+		hrtimer_start(&cs->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
+	}
+
+	return 0;
+
+err_input_register_device_failed:
+	input_free_device(cs->input_dev);
+
+err_input_dev_alloc_failed:
+	destroy_workqueue(cs->cy8c_wq);
+err_init_sensor_failed:
+err_create_wq_failed:
+	kfree(cs);
+
+err_alloc_data_failed:
+err_check_functionality_failed:
+	return ret;
+}
+
+static int cy8c_cs_remove(struct i2c_client *client)
+{
+	struct cy8c_cs_data *cs = i2c_get_clientdata(client);
+
+	cy8c_touchkey_sysfs_deinit();
+
+	unregister_early_suspend(&cs->early_suspend);
+	free_irq(client->irq, cs);
+	input_unregister_device(cs->input_dev);
+
+	kfree(cs);
+
+	return 0;
+}
+
+static int cy8c_cs_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	int ret;
+	struct cy8c_cs_data *cs = i2c_get_clientdata(client);
+
+	pr_info("[cap] %s\n", __func__);
+
+	if (cs->func_support & CS_FUNC_PRINTRAW) {
+		ret = cancel_delayed_work_sync(&cs->work_raw);
+		if (!ret)
+			cancel_delayed_work(&cs->work_raw);
+	}
+	if (client->irq && cs->use_irq) {
+		disable_irq(client->irq);
+		ret = cancel_work_sync(&cs->work);
+		if (ret)
+			enable_irq(client->irq);
+	}
+	i2c_cy8c_write_byte_data(client, CS_MODE, CS_CMD_DSLEEP);
+	return 0;
+}
+
+static int cy8c_cs_resume(struct i2c_client *client)
+{
+	struct cy8c_cs_data *cs = i2c_get_clientdata(client);
+
+	pr_info("[cap] %s\n", __func__);
+	cs->reset();
+
+	msleep(50);
+
+	if (client->irq && cs->use_irq)
+		enable_irq(client->irq);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void cy8c_cs_early_suspend(struct early_suspend *h)
+{
+	struct cy8c_cs_data *ts;
+	ts = container_of(h, struct cy8c_cs_data, early_suspend);
+	cy8c_cs_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void cy8c_cs_late_resume(struct early_suspend *h)
+{
+	struct cy8c_cs_data *ts;
+	ts = container_of(h, struct cy8c_cs_data, early_suspend);
+	cy8c_cs_resume(ts->client);
+}
+#endif
+
+static const struct i2c_device_id cy8c_cs_id[] = {
+	{ CYPRESS_CS_NAME, 0 },
+};
+
+static struct i2c_driver cy8c_cs_driver = {
+	.probe		= cy8c_cs_probe,
+	.remove		= cy8c_cs_remove,
+	.id_table	= cy8c_cs_id,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+	.suspend	= cy8c_cs_suspend,
+	.resume		= cy8c_cs_resume,
+#endif
+	.driver		= {
+		.name = CYPRESS_CS_NAME,
+	},
+};
+
+static int __init cy8c_cs_init(void)
+{
+	printk(KERN_INFO "[cap] %s: enter\n", __func__);
+	return i2c_add_driver(&cy8c_cs_driver);
+}
+
+static void __exit cy8c_cs_exit(void)
+{
+	i2c_del_driver(&cy8c_cs_driver);
+}
+
+module_init(cy8c_cs_init);
+module_exit(cy8c_cs_exit);
+
+MODULE_DESCRIPTION("cy8c_cs driver");
+MODULE_LICENSE("GPL");