/*
 * Copyright (C) 2018 Semtech Corporation. All rights reserved.
 *
 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
 *
 * 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.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wakelock.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include <linux/power_supply.h>
#include <linux/sensor/sensors_core.h>
#include "sx9360_wifi_reg.h"
#ifdef CONFIG_CCIC_NOTIFIER
#include <linux/ccic/ccic_notifier.h>
#endif
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
#include <linux/usb/manager/usb_typec_manager_notifier.h>
#endif

#define VENDOR_NAME              "SEMTECH"
#define MODEL_NAME               "SX9360_WIFI"
#define MODULE_NAME              "grip_sensor_wifi"

#define I2C_M_WR                 0 /* for i2c Write */
#define I2c_M_RD                 1 /* for i2c Read */

#define IDLE                     0
#define ACTIVE                   1

#define SX9360_MODE_SLEEP        0
#define SX9360_MODE_NORMAL       1

#define DIFF_READ_NUM            10
#define GRIP_LOG_TIME            15 /* 30 sec */
#define ZERO_DETECT_TIME         5 /* 10 sec */

/* CS Main */
#define ENABLE_CSX               0x03

#define CSX_STATUS_REG           SX9360_STAT_PROXSTAT_FLAG

#define IRQ_PROCESS_CONDITION   (SX9360_IRQSTAT_TOUCH_FLAG	\
				| SX9360_IRQSTAT_RELEASE_FLAG	\
				| SX9360_IRQSTAT_COMPDONE_FLAG)

#if defined(CONFIG_FOLDER_HALL)
#define HALLIC_PATH		"/sys/class/sec/sec_flip/flipStatus"
#else
#define HALLIC_PATH		"/sys/class/sec/sec_key/hall_detect"
#endif

struct sx9360_p {
	struct i2c_client *client;
	struct input_dev *input;
	struct device *factory_device;
	struct delayed_work init_work;
	struct delayed_work irq_work;
	struct delayed_work debug_work;
	struct wake_lock grip_wake_lock;
	struct mutex mode_mutex;
	struct mutex read_mutex;
#if defined(CONFIG_CCIC_NOTIFIER)
	struct notifier_block cpuidle_ccic_nb;
#endif

	bool skip_data;

	int irq;
	int gpio_nirq;
	int state;
	int init_done;
	int hallic_detect;

	atomic_t enable;

	int again_m;
	int dgain_m;

	u16 offset;
	s32 capMain;
	s32 useful;
	s16 avg;
	s16 diff;

	s16 diff_avg;
	int diff_cnt;
	s32 useful_avg;

	int irq_count;
	int abnormal_mode;
	s16 max_diff;
	s16 max_normal_diff;

	int debug_count;
	int debug_zero_count;
	char hall_ic[6];
};

static int sx9360_check_hallic_state(char *file_path, char hall_ic_status[])
{
	int iRet = 0;
	mm_segment_t old_fs;
	struct file *filep;
	char hall_sysfs[5];

	old_fs = get_fs();
	set_fs(KERNEL_DS);

	filep = filp_open(file_path, O_RDONLY, 0440);
	if (IS_ERR(filep)) {
		iRet = PTR_ERR(filep);
		pr_err("[SX9360_WIFI]: %s - file open fail %d\n", __func__, iRet);
		set_fs(old_fs);
		return iRet;
	}

	iRet = filep->f_op->read(filep, hall_sysfs,
		sizeof(hall_sysfs), &filep->f_pos);

	if (iRet <= 0) {
		pr_err("[SX9360_WIFI]: %s - file read fail %d\n", __func__, iRet);
		filp_close(filep, current->files);
		set_fs(old_fs);
		return -EIO;
	} else {
		strncpy(hall_ic_status, hall_sysfs, sizeof(hall_sysfs));
	}

	filp_close(filep, current->files);
	set_fs(old_fs);

	return iRet;
}

static int sx9360_get_nirq_state(struct sx9360_p *data)
{
	return gpio_get_value_cansleep(data->gpio_nirq);
}

static int sx9360_i2c_write(struct sx9360_p *data, u8 reg_addr, u8 buf)
{
	int ret;
	struct i2c_msg msg;
	unsigned char w_buf[2];

	w_buf[0] = reg_addr;
	w_buf[1] = buf;

	msg.addr = data->client->addr;
	msg.flags = I2C_M_WR;
	msg.len = 2;
	msg.buf = (char *)w_buf;

	ret = i2c_transfer(data->client->adapter, &msg, 1);
	if (ret < 0)
		pr_err("[SX9360_WIFI]: %s - i2c write error %d\n", __func__, ret);

	return 0;
}

static int sx9360_i2c_read(struct sx9360_p *data, u8 reg_addr, u8 *buf)
{
	int ret;
	struct i2c_msg msg[2];

	msg[0].addr = data->client->addr;
	msg[0].flags = I2C_M_WR;
	msg[0].len = 1;
	msg[0].buf = &reg_addr;

	msg[1].addr = data->client->addr;
	msg[1].flags = I2C_M_RD;
	msg[1].len = 1;
	msg[1].buf = buf;

	ret = i2c_transfer(data->client->adapter, msg, 2);
	if (ret < 0)
		pr_err("[SX9360_WIFI]: %s - i2c read error %d\n", __func__, ret);

	return ret;
}

static u8 sx9360_read_irqstate(struct sx9360_p *data)
{
	u8 val = 0;

	if (sx9360_i2c_read(data, SX9360_IRQSTAT_REG, &val) >= 0)
		return (val & 0x00FF);

	return 0;
}

static void sx9360_initialize_register(struct sx9360_p *data)
{
	u8 val = 0;
	int idx;

	for (idx = 0; idx < (sizeof(setup_reg) >> 1); idx++) {		
		sx9360_i2c_write(data, setup_reg[idx].reg, setup_reg[idx].val);
		pr_info("[SX9360_WIFI]: %s - Write Reg: 0x%x Value: 0x%x\n",
			__func__, setup_reg[idx].reg, setup_reg[idx].val);

		sx9360_i2c_read(data, setup_reg[idx].reg, &val);
		pr_info("[SX9360_WIFI]: %s - Read Reg: 0x%x Value: 0x%x\n\n",
			__func__, setup_reg[idx].reg, val);
	}

	data->init_done = ON;
}

static void sx9360_initialize_chip(struct sx9360_p *data)
{
	int cnt = 0;

	while((sx9360_get_nirq_state(data) == 0) && (cnt++ < 10)) {
		sx9360_read_irqstate(data);
		msleep(20);
	}

	if (cnt >= 10)
		pr_err("[SX9360_WIFI]: %s - s/w reset fail(%d)\n", __func__, cnt);

	sx9360_initialize_register(data);
}

static int sx9360_set_offset_calibration(struct sx9360_p *data)
{
	int ret = 0;

	ret = sx9360_i2c_write(data, SX9360_STAT_REG,
                                 SX9360_STAT_COMPSTAT_ALL_FLAG);

	return ret;
}

static void sx9360_send_event(struct sx9360_p *data, u8 state)
{
	if (data->skip_data == true) {
		pr_info("[SX9360_WIFI]: %s - skip grip event\n", __func__);
		return;
	}

	if (state == ACTIVE) {
		data->state = ACTIVE;
		pr_info("[SX9360_WIFI]: %s - touched\n", __func__);
	} else {
		data->state = IDLE;
		pr_info("[SX9360_WIFI]: %s - released\n", __func__);
	}

	if (state == ACTIVE)
		input_report_rel(data->input, REL_MISC, 1);
	else
		input_report_rel(data->input, REL_MISC, 2);

	input_sync(data->input);
}

static void sx9360_display_data_reg(struct sx9360_p *data)
{
	u8 val, reg;

	pr_info("[SX9360_WIFI]: ############# %d reference #############\n", 0);
	for (reg = SX9360_REGUSEMSBPHR; reg <= SX9360_REGOFFSETLSBPHR; reg++)
	{
		sx9360_i2c_read(data, reg, &val);
		pr_info("[SX9360_WIFI]: %s - Register(0x%2x) data(0x%2x)\n",
			__func__, reg, val);
	}
	pr_info("[SX9360_WIFI]: ############# %d Main #############\n", 0);
	for (reg = SX9360_REGUSEMSBPHM; reg <= SX9360_REGOFFSETLSBPHM; reg++)
	{
		sx9360_i2c_read(data, reg, &val);
		pr_info("[SX9360_WIFI]: %s - Register(0x%2x) data(0x%2x)\n",
			__func__, reg, val);
	}
}

static void sx9360_get_gain(struct sx9360_p *data)
{
	u8 msByte;
	static const int again_phm[]={7500,22500,37500,52500,60000,75000,90000,105000};  
	sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &msByte);
	msByte=(msByte>>4) & 0x07;
	data->again_m = again_phm[msByte];
	
	sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &msByte);
	msByte=(msByte>>3) & 0x07;
	if(msByte)
		data->dgain_m = 1 << (msByte-1);
	else
		data->dgain_m = 1;
}


static void sx9360_get_data(struct sx9360_p *data)
{
	u8 msByte = 0;
	u8 lsByte = 0;
	u16 offset = 0;
	s32 capMain = 0, useful = 0;
	s16 avg = 0, diff = 0;
	s16 retry = 0;
	u8 convstat = 0;

	mutex_lock(&data->read_mutex);

	sx9360_get_gain(data);

	while(1)
	{
		sx9360_i2c_read(data, SX9360_STAT_REG, &convstat);
		convstat &= 0x01;

		if(++retry > 5 || convstat == 0)
			break;

		usleep_range(10000, 11000);
	}
	pr_info("[SX9360_WIFI]: %s retry : %d, CONVSTAT : %u\n", __func__, retry, convstat);

	/* diff read */
	sx9360_i2c_read(data, SX9360_REGDIFFMSBPHM, &msByte);
	sx9360_i2c_read(data, SX9360_REGDIFFLSBPHM, &lsByte);

	diff = (s16)msByte;
	diff = (diff << 8) | ((s16)lsByte);


	/* Calculate out the Main Cap information */
	sx9360_i2c_read(data, SX9360_REGUSEMSBPHM, &msByte);
	sx9360_i2c_read(data, SX9360_REGUSELSBPHM, &lsByte);

	useful = (s32)msByte;
	useful = (useful << 8) | ((s32)lsByte);
	if (useful > 32767)
		useful -= 65536;

	sx9360_i2c_read(data, SX9360_REGOFFSETMSBPHM, &msByte);
	sx9360_i2c_read(data, SX9360_REGOFFSETLSBPHM, &lsByte);

	offset = (u16)msByte;
	offset = (offset << 8) | ((u16)lsByte);

	msByte = (u8)((offset >> 7) & 0x7F);
	lsByte = (u8)((offset)      & 0x7F);

	capMain = (((s32)msByte * 30000) + ((s32)lsByte * 500)) +
            		(s32)(((s64)useful * data->again_m) / (data->dgain_m * 32768));

	/* avg read */
	sx9360_i2c_read(data, SX9360_REGAVGMSBPHM, &msByte);
	sx9360_i2c_read(data, SX9360_REGAVGLSBPHM, &lsByte);

	avg = (s16)msByte;
	avg = (avg << 8) | ((s16)lsByte);

	data->useful = useful;
	data->offset = offset;
	data->capMain = capMain;
	data->avg = avg;
	data->diff = diff;

	mutex_unlock(&data->read_mutex);

	pr_info("[SX9360_WIFI]: %s - capMain: %ld, useful: %ld, avg: %d, diff: %d, Offset: %u\n",
		__func__, (long int)capMain, (long int)useful, avg, diff, offset);
}

static int sx9360_set_mode(struct sx9360_p *data, unsigned char mode)
{
	int ret = -EINVAL;

	pr_info("[SX9360_WIFI]: %s %u\n", __func__, mode);

	mutex_lock(&data->mode_mutex);
	if (mode == SX9360_MODE_SLEEP) {
		ret = sx9360_i2c_write(data, SX9360_GNRLCTRL0_REG, SX9360_GNRLCTRL0_VAL_PHOFF);
	} else if (mode == SX9360_MODE_NORMAL) {
		ret = sx9360_i2c_write(data, SX9360_GNRLCTRL0_REG,
			SX9360_GNRLCTRL0_VAL_PHOFF | ENABLE_CSX);
		msleep(20);

		sx9360_set_offset_calibration(data);
		msleep(450);
	}

	pr_info("[SX9360_WIFI]: %s - change the mode : %u\n", __func__, mode);

	mutex_unlock(&data->mode_mutex);
	return ret;
}

static void sx9360_check_status(struct sx9360_p *data)
{
	u8 status = 0;	

	sx9360_i2c_read(data, SX9360_STAT_REG, &status);

	pr_info("[SX9360_WIFI]: %s - (status: 0x%x)\n", __func__, status);

	if (data->skip_data == true) {
		input_report_rel(data->input, REL_MISC, 2);
		input_sync(data->input);
		return;
	}

	if (status & CSX_STATUS_REG) {
		sx9360_send_event(data, ACTIVE);
	} else {
		sx9360_send_event(data, IDLE);
	}
}

static void sx9360_set_enable(struct sx9360_p *data, int enable)
{
	int pre_enable = atomic_read(&data->enable);

	pr_info("[SX9360_WIFI]: %s %d\n", __func__, enable);

	if (enable) {
		if (pre_enable == OFF) {
			data->diff_avg = 0;
			data->diff_cnt = 0;
			data->useful_avg = 0;
			sx9360_get_data(data);
			sx9360_check_status(data);

			msleep(20);

			/* make sure no interrupts are pending since enabling irq
			 * will only work on next falling edge */
			sx9360_read_irqstate(data);

			/* enable interrupt */
			sx9360_i2c_write(data, SX9360_IRQ_ENABLE_REG, 0x0E);

			enable_irq(data->irq);
			enable_irq_wake(data->irq);
			
			atomic_set(&data->enable, ON);
		}
	} else {
		if (pre_enable == ON) {
			/* disable interrupt */
			sx9360_i2c_write(data, SX9360_IRQ_ENABLE_REG, 0x00);

			disable_irq(data->irq);
			disable_irq_wake(data->irq);

			atomic_set(&data->enable, OFF);
		}
	}
}

static void sx9360_set_debug_work(struct sx9360_p *data, u8 enable,
		unsigned int time_ms)
{
	if (enable == ON) {
		data->debug_count = 0;
		data->debug_zero_count = 0;
		schedule_delayed_work(&data->debug_work,
			msecs_to_jiffies(time_ms));
	} else {
		cancel_delayed_work_sync(&data->debug_work);
	}
}

static ssize_t sx9360_get_offset_calibration_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	u8 val = 0;
	struct sx9360_p *data = dev_get_drvdata(dev);

	sx9360_i2c_read(data, SX9360_IRQSTAT_REG, &val);

	return snprintf(buf, PAGE_SIZE, "%d\n", val);
}

static ssize_t sx9360_set_offset_calibration_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	unsigned long val;
	struct sx9360_p *data = dev_get_drvdata(dev);

	if (kstrtoul(buf, 10, &val)) {
		pr_err("[SX9360_WIFI]: %s - Invalid Argument\n", __func__);
		return -EINVAL;
	}

	if (val)
		sx9360_set_offset_calibration(data);

	return count;
}

static ssize_t sx9360_register_write_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	int regist = 0, val = 0;
	struct sx9360_p *data = dev_get_drvdata(dev);

	if (sscanf(buf, "%2x,%2x", &regist, &val) != 2) {
		pr_err("[SX9360_WIFI]: %s - The number of data are wrong\n",
			__func__);
		return -EINVAL;
	}

	sx9360_i2c_write(data, (unsigned char)regist, (unsigned char)val);
	pr_info("[SX9360_WIFI]: %s - Register(0x%2x) data(0x%2x)\n",
		__func__, regist, val);

	return count;
}

static ssize_t sx9360_register_read_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	u8 val = 0;
	int offset = 0, idx = 0;
	struct sx9360_p *data = dev_get_drvdata(dev);

	for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg)); idx++) {
		sx9360_i2c_read(data, setup_reg[idx].reg, &val);
		pr_info("[SX9360_WIFI]: %s - Read Reg: 0x%x Value: 0x%x\n\n",
			__func__, setup_reg[idx].reg, val);

		offset += snprintf(buf + offset, PAGE_SIZE - offset,
		"Reg: 0x%x Value: 0x%08x\n", setup_reg[idx].reg, val);
	}

	return offset;
}

static ssize_t sx9360_read_data_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	sx9360_display_data_reg(data);

	return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}

static ssize_t sx9360_sw_reset_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	pr_info("[SX9360_WIFI]: %s\n", __func__);
	sx9360_set_offset_calibration(data);
	msleep(450);
	sx9360_get_data(data);

	return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}

static ssize_t sx9360_vendor_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME);
}

static ssize_t sx9360_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME);
}

static ssize_t sx9360_touch_mode_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "1\n");
}

static ssize_t sx9360_raw_data_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	static s32 sum_diff, sum_useful;
	struct sx9360_p *data = dev_get_drvdata(dev);

	sx9360_get_data(data);

	if (data->diff_cnt == 0) {
		sum_diff = (s32)data->diff;
		sum_useful = data->useful;
	}
	else {
		sum_diff += (s32)data->diff;
		sum_useful += data->useful;
	}

	if (++data->diff_cnt >= DIFF_READ_NUM) {
		data->diff_avg = (s16)(sum_diff / DIFF_READ_NUM);
		data->useful_avg = sum_useful / DIFF_READ_NUM;
		data->diff_cnt = 0;
	}

	return snprintf(buf, PAGE_SIZE, "%ld,%ld,%u,%d,%d\n", (long int)data->capMain,
		(long int)data->useful, data->offset, data->diff, data->avg);
}

static ssize_t sx9360_diff_avg_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%d\n", data->diff_avg);
}

static ssize_t sx9360_useful_avg_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->useful_avg);
}

static ssize_t sx9360_avgnegfilt_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 avgnegfilt = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL3_REG, &avgnegfilt);

	avgnegfilt = (avgnegfilt & 0x38) >> 3;

	if (avgnegfilt == 7)
		return snprintf(buf, PAGE_SIZE, "1\n");
	else if (avgnegfilt > 0 && avgnegfilt < 7)
		return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << avgnegfilt);
	else if (avgnegfilt == 0)
		return snprintf(buf, PAGE_SIZE, "0\n");

	return snprintf(buf, PAGE_SIZE, "not set\n");
}

static ssize_t sx9360_avgposfilt_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 avgposfilt = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL3_REG, &avgposfilt);
	avgposfilt = avgposfilt & 0x07;

	if (avgposfilt == 7)
		return snprintf(buf, PAGE_SIZE, "1\n");
	else if (avgposfilt > 1 && avgposfilt < 7)
		return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 16 << avgposfilt);
	else if (avgposfilt == 0)
		return snprintf(buf, PAGE_SIZE, "0\n");
	else if (avgposfilt == 1)
		return snprintf(buf, PAGE_SIZE, "1-1/16\n");

	return snprintf(buf, PAGE_SIZE, "not set\n");
}

static ssize_t sx9360_gain_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 gain = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &gain);
	gain = (gain & 0x38) >> 3;

	if (gain > 0 && gain < 5)
		return snprintf(buf, PAGE_SIZE, "x%u\n", 1 << (gain - 1));

	return snprintf(buf, PAGE_SIZE, "Reserved\n");
}

static ssize_t sx9360_range_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "None\n");
}

static ssize_t sx9360_avgthresh_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 avgthresh = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL1_REG, &avgthresh);
	avgthresh = avgthresh & 0x3F;

	return snprintf(buf, PAGE_SIZE, "%ld\n", 512 * (long int)avgthresh);
}

static ssize_t sx9360_rawfilt_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 rawfilt = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL0PHM_REG, &rawfilt);
	rawfilt = rawfilt & 0x07;

	if (rawfilt > 0 && rawfilt < 8)
		return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << rawfilt);
	else if (rawfilt == 0)
		return snprintf(buf, PAGE_SIZE, "0\n");

	return snprintf(buf, PAGE_SIZE, "not set\n");
}

static ssize_t sx9360_sampling_freq_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 sampling_freq = 0;
	const char *table[16] = {
		"250", "200", "166.67", "142.86", "125", "100",	"83.33", "71.43",
		"62.50", "50", "41.67", "35.71", "27.78", "20.83", "15.62", "7.81"};

	sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &sampling_freq);
	sampling_freq = sampling_freq & 0x0F;

	return snprintf(buf, PAGE_SIZE, "%skHz\n", table[sampling_freq]);
}

static ssize_t sx9360_scan_period_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 scan_period = 0;

	sx9360_i2c_read(data, SX9360_GNRLCTRL2_REG, &scan_period);

	return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)(((long int)scan_period << 11) / 1000));
}

static ssize_t sx9360_again_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	const char *table[8] = {
		"+/-0.75", "+/-2.25", "+/-3.75", "+/-5.25",
		"+/-6", "+/-7.5", "+/-9", "+/-10.5"};
	u8 again = 0;

	sx9360_i2c_read(data, SX9360_AFEPARAM1PHM_REG, &again);
	again = (again & 0x70) >> 4;

	return snprintf(buf, PAGE_SIZE, "%spF\n", table[again]);
}

static ssize_t sx9360_phase_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "1\n");
}

static ssize_t sx9360_hysteresis_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	const char *table[4] = {"None", "+/-6%", "+/-12%", "+/-25%"};
	u8 hyst = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL4_REG, &hyst);
	hyst = (hyst & 0x30) >> 4;

	return snprintf(buf, PAGE_SIZE, "%s\n", table[hyst]);
}

static ssize_t sx9360_resolution_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 resolution = 0;

	sx9360_i2c_read(data, SX9360_AFEPARAM0PHM_REG, &resolution);
	resolution = resolution & 0x7;

	return snprintf(buf, PAGE_SIZE, "%u\n", 1 << (resolution + 3));
}

static ssize_t sx9360_adc_filt_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "None\n");
}

static ssize_t sx9360_useful_filt_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 useful_filt = 0;

	sx9360_i2c_read(data, SX9360_USEFILTER4_REG, &useful_filt);
	useful_filt = useful_filt & 0x01;

	return snprintf(buf, PAGE_SIZE, "%s\n", useful_filt ? "on" : "off");
}

static ssize_t sx9360_irq_count_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	int ret = 0;
	s16 max_diff_val = 0;

	if (data->irq_count) {
		ret = -1;
		max_diff_val = data->max_diff;
	} else {
		max_diff_val = data->max_normal_diff;
	}

	pr_info("[SX9360_WIFI]: %s - called\n", __func__);

	return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n",
		ret, data->irq_count, max_diff_val);
}

static ssize_t sx9360_irq_count_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	u8 onoff;
	int ret;

	ret = kstrtou8(buf, 10, &onoff);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - kstrtou8 failed.(%d)\n", __func__, ret);
		return count;
	}

	mutex_lock(&data->read_mutex);

	if (onoff == 0) {
		data->abnormal_mode = OFF;
	} else if (onoff == 1) {
		data->abnormal_mode = ON;
		data->irq_count = 0;
		data->max_diff = 0;
		data->max_normal_diff = 0;
	} else {
		pr_err("[SX9360_WIFI]: %s - unknown value %d\n", __func__, onoff);
	}

	mutex_unlock(&data->read_mutex);

	pr_info("[SX9360_WIFI]: %s - %d\n", __func__, onoff);

	return count;
}

static ssize_t sx9360_normal_threshold_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	u8 th_buf = 0, hyst = 0;
	u32 threshold = 0;

	sx9360_i2c_read(data, SX9360_PROXCTRL5_REG, &th_buf);
	threshold = (u32)th_buf * (u32)th_buf / 2;

	sx9360_i2c_read(data, SX9360_PROXCTRL4_REG, &hyst);
	hyst = (hyst & 0x30) >> 4;

	switch (hyst) {
	case 0x01: /* 6% */
		hyst = threshold >> 4;
		break;
	case 0x02: /* 12% */
		hyst = threshold >> 3;
		break;
	case 0x03: /* 25% */
		hyst = threshold >> 2;
		break;
	default:
		/* None */
		break;
	}

	return snprintf(buf, PAGE_SIZE, "%d,%d\n",
		(u32)threshold + (u32)hyst, (u32)threshold - (u32)hyst);
}

static ssize_t sx9360_onoff_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_data);
}

static ssize_t sx9360_onoff_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	u8 val;
	int ret;
	struct sx9360_p *data = dev_get_drvdata(dev);

	ret = kstrtou8(buf, 2, &val);
	if (ret) {
		pr_err("[SX9360_WIFI]: %s - Invalid Argument\n", __func__);
		return ret;
	}

	if (val == 0) {
		data->skip_data = true;
		if (atomic_read(&data->enable) == ON) {
			data->state = IDLE;
			input_report_rel(data->input, REL_MISC, 2);
			input_sync(data->input);
		}
	} else
		data->skip_data = false;

	pr_info("[SX9360_WIFI]: %s -%u\n", __func__, val);
	return count;
}

static DEVICE_ATTR(menual_calibrate, S_IRUGO | S_IWUSR | S_IWGRP,
		sx9360_get_offset_calibration_show,
		sx9360_set_offset_calibration_store);
static DEVICE_ATTR(register_write, S_IWUSR | S_IWGRP,
		NULL, sx9360_register_write_store);
static DEVICE_ATTR(register_read, S_IRUGO, sx9360_register_read_show, NULL);
static DEVICE_ATTR(readback, S_IRUGO, sx9360_read_data_show, NULL);
static DEVICE_ATTR(reset, S_IRUGO, sx9360_sw_reset_show, NULL);

static DEVICE_ATTR(name, S_IRUGO, sx9360_name_show, NULL);
static DEVICE_ATTR(vendor, S_IRUGO, sx9360_vendor_show, NULL);
static DEVICE_ATTR(mode, S_IRUGO, sx9360_touch_mode_show, NULL);
static DEVICE_ATTR(raw_data, S_IRUGO, sx9360_raw_data_show, NULL);
static DEVICE_ATTR(diff_avg, S_IRUGO, sx9360_diff_avg_show, NULL);
static DEVICE_ATTR(useful_avg, S_IRUGO, sx9360_useful_avg_show, NULL);
static DEVICE_ATTR(onoff, S_IRUGO | S_IWUSR | S_IWGRP,
		sx9360_onoff_show, sx9360_onoff_store);
static DEVICE_ATTR(normal_threshold, S_IRUGO,
		sx9360_normal_threshold_show, NULL);

static DEVICE_ATTR(avg_negfilt, S_IRUGO, sx9360_avgnegfilt_show, NULL);
static DEVICE_ATTR(avg_posfilt, S_IRUGO, sx9360_avgposfilt_show, NULL);
static DEVICE_ATTR(avg_thresh, S_IRUGO, sx9360_avgthresh_show, NULL);
static DEVICE_ATTR(rawfilt, S_IRUGO, sx9360_rawfilt_show, NULL);
static DEVICE_ATTR(sampling_freq, S_IRUGO, sx9360_sampling_freq_show, NULL);
static DEVICE_ATTR(scan_period, S_IRUGO, sx9360_scan_period_show, NULL);
static DEVICE_ATTR(gain, S_IRUGO, sx9360_gain_show, NULL);
static DEVICE_ATTR(range, S_IRUGO, sx9360_range_show, NULL);
static DEVICE_ATTR(analog_gain, S_IRUGO, sx9360_again_show, NULL);
static DEVICE_ATTR(phase, S_IRUGO, sx9360_phase_show, NULL);
static DEVICE_ATTR(hysteresis, S_IRUGO, sx9360_hysteresis_show, NULL);
static DEVICE_ATTR(irq_count, S_IRUGO | S_IWUSR | S_IWGRP,
		sx9360_irq_count_show, sx9360_irq_count_store);
static DEVICE_ATTR(resolution, S_IRUGO, sx9360_resolution_show, NULL);
static DEVICE_ATTR(adc_filt, S_IRUGO, sx9360_adc_filt_show, NULL);
static DEVICE_ATTR(useful_filt, S_IRUGO, sx9360_useful_filt_show, NULL);

static struct device_attribute *sensor_attrs[] = {
	&dev_attr_menual_calibrate,
	&dev_attr_register_write,
	&dev_attr_register_read,
	&dev_attr_readback,
	&dev_attr_reset,
	&dev_attr_name,
	&dev_attr_vendor,
	&dev_attr_mode,
	&dev_attr_raw_data,
	&dev_attr_diff_avg,
	&dev_attr_useful_avg,
	&dev_attr_onoff,
	&dev_attr_normal_threshold,
	&dev_attr_avg_negfilt,
	&dev_attr_avg_posfilt,
	&dev_attr_avg_thresh,
	&dev_attr_rawfilt,
	&dev_attr_sampling_freq,
	&dev_attr_scan_period,
	&dev_attr_gain,
	&dev_attr_range,
	&dev_attr_analog_gain,
	&dev_attr_phase,
	&dev_attr_hysteresis,
	&dev_attr_irq_count,
	&dev_attr_resolution,
	&dev_attr_adc_filt,
	&dev_attr_useful_filt,
	NULL,
};

/*****************************************************************************/
static ssize_t sx9360_enable_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	u8 enable;
	int ret;
	struct sx9360_p *data = dev_get_drvdata(dev);

	ret = kstrtou8(buf, 2, &enable);
	if (ret) {
		pr_err("[SX9360_WIFI]: %s - Invalid Argument\n", __func__);
		return ret;
	}

	pr_info("[SX9360_WIFI]: %s - new_value = %u\n", __func__, enable);
	if ((enable == 0) || (enable == 1))
		sx9360_set_enable(data, (int)enable);

	return size;
}

static ssize_t sx9360_enable_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable));
}

static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
		sx9360_enable_show, sx9360_enable_store);

static struct attribute *sx9360_attributes[] = {
	&dev_attr_enable.attr,
	NULL
};

static struct attribute_group sx9360_attribute_group = {
	.attrs = sx9360_attributes
};

static void sx9360_touch_process(struct sx9360_p *data)
{
	u8 status = 0;

	sx9360_get_data(data);

	sx9360_i2c_read(data, SX9360_STAT_REG, &status);

	if (data->abnormal_mode) {
		if (status & CSX_STATUS_REG) {
			if (data->max_diff < data->diff)
				data->max_diff = data->diff;
			data->irq_count++;
		}
	}

	if (data->state == IDLE) {
		if (status & CSX_STATUS_REG)
			sx9360_send_event(data, ACTIVE);
		else
			pr_info("[SX9360_WIFI]: %s - 0x%x already released.\n",
				__func__, status);
	} else { /* User released button */
		if (!(status & CSX_STATUS_REG)) {
			sx9360_send_event(data, IDLE);
		} else {
			pr_info("[SX9360_WIFI]: %s - 0x%x still touched\n",
				__func__, status);
		}
	}
}

static void sx9360_process_interrupt(struct sx9360_p *data)
{
	u8 status = 0;

	/* since we are not in an interrupt don't need to disable irq. */
	status = sx9360_read_irqstate(data);

	pr_info("[SX9360_WIFI]: %s - status %d\n", __func__, status);

	if (status & IRQ_PROCESS_CONDITION)
		sx9360_touch_process(data);
}

static void sx9360_init_work_func(struct work_struct *work)
{
	struct sx9360_p *data = container_of((struct delayed_work *)work,
		struct sx9360_p, init_work);

	int retry = 0;

	sx9360_initialize_chip(data);

	sx9360_set_mode(data, SX9360_MODE_NORMAL);
	/* make sure no interrupts are pending since enabling irq
	 * will only work on next falling edge */
	sx9360_read_irqstate(data);
	msleep(20);

	while(retry++ < 10) {
		sx9360_get_data(data);
		/* Defence code */
		if (data->capMain == 0 && data->avg == 0 && data->diff == 0
			&& data->useful == 0 && data->offset == 0) {
			pr_info("[SX9360]: Defence code for grip sensor - retry: %d\n", retry);

			sx9360_i2c_write(data, SX9360_SOFTRESET_REG, SX9360_SOFTRESET);
			msleep(300);
			sx9360_initialize_chip(data);
			sx9360_set_mode(data, SX9360_MODE_NORMAL);
			sx9360_read_irqstate(data);
			msleep(20);
		} else {
			break;
		}
	}
}

static void sx9360_irq_work_func(struct work_struct *work)
{
	struct sx9360_p *data = container_of((struct delayed_work *)work,
		struct sx9360_p, irq_work);

	if (sx9360_get_nirq_state(data) == 0)
		sx9360_process_interrupt(data);
	else
		pr_err("[SX9360_WIFI]: %s - nirq read high %d\n",
			__func__, sx9360_get_nirq_state(data));
}

static void sx9360_read_register(struct sx9360_p *data)
{
	u8 val, offset = 0;
	int array_size, idx = 0;
	char buf[52] = {0,};

	array_size = (int)(ARRAY_SIZE(setup_reg));
	while (idx < array_size) {
		sx9360_i2c_read(data, setup_reg[idx].reg, &val);
		offset += snprintf(buf + offset, sizeof(buf) - offset, "[0x%02x]:0x%02x ", setup_reg[idx].reg, val);
		idx++;
		if(!(idx & 0x03) || (idx == array_size)) {
			pr_info("[SX9360_WIFI]: %s - %s\n", __func__, buf);
			offset = 0;
		}
	}
}

static void sx9360_debug_work_func(struct work_struct *work)
{
	struct sx9360_p *data = container_of((struct delayed_work *)work,
		struct sx9360_p, debug_work);
	static int hall_flag = 1;
	int ret;
	u8 value = 0;

#if defined(CONFIG_FOLDER_HALL)
	char str[2] = "0";
#else
	char str[6] = "CLOSE";
#endif
	if (data->hallic_detect) {
		sx9360_check_hallic_state(HALLIC_PATH, data->hall_ic);
	
		data->hall_ic[sizeof(str)-1] = '\0';
	
		if (strcmp(data->hall_ic, str) == 0) {
			if (hall_flag) {
				pr_info("[SX9360_WIFI]: %s - hall IC is closed\n", __func__);
				sx9360_set_offset_calibration(data);
				hall_flag = 0;
			}
		} else {
			hall_flag = 1;
		}
	}

	if (atomic_read(&data->enable) == ON) {
		if (data->abnormal_mode) {
			sx9360_get_data(data);
			if (data->max_normal_diff < data->diff)
				data->max_normal_diff = data->diff;
		} else {
			if (data->debug_count >= GRIP_LOG_TIME) {
				sx9360_get_data(data);
				data->debug_count = 0;
			} else {
				data->debug_count++;
			}
		}
	}

	/* Zero Detect Defence code*/
	if(data->debug_zero_count >= ZERO_DETECT_TIME) {
		ret = sx9360_i2c_read(data, SX9360_GNRLCTRL0_REG, &value);
		if (ret < 0)
			pr_err("[SX9360_WIFI]: fail to read PHEN :0x%02x (%d)\n", value, ret);
		else if (value == 0) {
			pr_info("[SX9360_WIFI]: %s - detected all data zero!!!\n", __func__);
			sx9360_read_register(data);

			sx9360_i2c_write(data, SX9360_SOFTRESET_REG, SX9360_SOFTRESET);
			msleep(300);
			sx9360_initialize_chip(data);
			sx9360_set_mode(data, SX9360_MODE_NORMAL);
			sx9360_read_irqstate(data);
			msleep(20);
		}
		data->debug_zero_count = 0;
	} else {
		data->debug_zero_count++;
	}

	schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000));
}

static irqreturn_t sx9360_interrupt_thread(int irq, void *pdata)
{
	struct sx9360_p *data = pdata;

	wake_lock_timeout(&data->grip_wake_lock, 3 * HZ);
	schedule_delayed_work(&data->irq_work, msecs_to_jiffies(100));

	return IRQ_HANDLED;
}

static int sx9360_input_init(struct sx9360_p *data)
{
	int ret = 0;
	struct input_dev *dev = NULL;

	/* Create the input device */
	dev = input_allocate_device();
	if (!dev)
		return -ENOMEM;

	dev->name = MODULE_NAME;
	dev->id.bustype = BUS_I2C;

	input_set_capability(dev, EV_REL, REL_MISC);
	input_set_drvdata(dev, data);

	ret = input_register_device(dev);
	if (ret < 0) {
		input_free_device(dev);
		return ret;
	}

	ret = sensors_create_symlink(&dev->dev.kobj, dev->name);
	if (ret < 0) {
		input_unregister_device(dev);
		return ret;
	}

	ret = sysfs_create_group(&dev->dev.kobj, &sx9360_attribute_group);
	if (ret < 0) {
		sensors_remove_symlink(&data->input->dev.kobj,
			data->input->name);
		input_unregister_device(dev);
		return ret;
	}

	/* save the input pointer and finish initialization */
	data->input = dev;

	return 0;
}

static int sx9360_setup_pin(struct sx9360_p *data)
{
	int ret;

	ret = gpio_request(data->gpio_nirq, "SX9360_nIRQ");
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - gpio %d request failed (%d)\n",
			__func__, data->gpio_nirq, ret);
		return ret;
	}

	ret = gpio_direction_input(data->gpio_nirq);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - failed to set gpio %d as input (%d)\n",
			__func__, data->gpio_nirq, ret);
		gpio_free(data->gpio_nirq);
		return ret;
	}

	return 0;
}

static void sx9360_initialize_variable(struct sx9360_p *data)
{
	data->init_done = OFF;
	data->skip_data = false;
	data->state = IDLE;

	atomic_set(&data->enable, OFF);
}

static int sx9360_read_setupreg(struct device_node *dnode, char *str, u32 *val)
{
	u32 temp_val;
	int ret;

	ret = of_property_read_u32(dnode, str, &temp_val);

	if (!ret)
		*val = temp_val;
	else
		pr_err("[SX9360_WIFI]: %s - %s: property read err 0x%08x (%d)\n",
			__func__, str, temp_val, ret);

	return ret;
}

static int sx9360_parse_dt(struct sx9360_p *data, struct device *dev)
{
	struct device_node *dNode = dev->of_node;
	enum of_gpio_flags flags;
	int ret;

	u32 reggnrlctrl2;
	u32 regafeparam1phm;
	u32 regproxctrl0phm;
	u32 regproxctrl0phr;
	u32 regproxctrl5;
	u32 regproxctrl4;
	u32 regproxctrl3;
	u32 regafeparam0phm;
	u32 regafeparam1phr;
	u32 regafeparam0phr;

	if (dNode == NULL)
		return -ENODEV;

	data->gpio_nirq = of_get_named_gpio_flags(dNode,
		"sx9360_wifi,nirq-gpio", 0, &flags);
	if (data->gpio_nirq < 0) {
		pr_err("[SENSOR]: %s - get gpio_nirq error\n", __func__);
		return -ENODEV;
	}

	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,reggnrlctrl2", &reggnrlctrl2))
		setup_reg[SX9360_GNRLCTRL_REG_IDX + 2].val = reggnrlctrl2;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regafeparam1phm", &regafeparam1phm))
		setup_reg[SX9360_AFE_REG_IDX + 4].val = regafeparam1phm;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regproxctrl0phm", &regproxctrl0phm))
		setup_reg[SX9360_PROXCTRL_REG_IDX + 1].val = regproxctrl0phm;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regproxctrl0phr", &regproxctrl0phr))
		setup_reg[SX9360_PROXCTRL_REG_IDX].val = regproxctrl0phr;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regproxctrl5", &regproxctrl5))
		setup_reg[SX9360_PROXCTRL_REG_IDX + 6].val = regproxctrl5;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regproxctrl4", &regproxctrl4))
		setup_reg[SX9360_PROXCTRL_REG_IDX + 5].val = regproxctrl4;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regproxctrl3", &regproxctrl3))
		setup_reg[SX9360_PROXCTRL_REG_IDX + 4].val = regproxctrl3;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regafeparam0phm", &regafeparam0phm))
		setup_reg[SX9360_AFE_REG_IDX + 3].val = regafeparam0phm;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regafeparam1phr", &regafeparam1phr))
		setup_reg[SX9360_AFE_REG_IDX + 2].val = regafeparam1phr;
	if (!sx9360_read_setupreg(dNode, "sx9360_wifi,regafeparam0phr", &regafeparam0phr))
		setup_reg[SX9360_AFE_REG_IDX + 1].val = regafeparam0phr;

	ret = of_property_read_u32(dNode, "sx9360_wifi,hallic_detect", &data->hallic_detect);
	if (ret < 0)
		data->hallic_detect = 0;

	return 0;
}

#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
static int sx9360_ccic_handle_notification(struct notifier_block *nb,
		unsigned long action, void *data)
{
	CC_NOTI_ATTACH_TYPEDEF usb_typec_info =
		*(CC_NOTI_ATTACH_TYPEDEF *)data;
	struct sx9360_p *pdata =
		container_of(nb, struct sx9360_p, cpuidle_ccic_nb);
	static int pre_attach;

	if (usb_typec_info.src != CCIC_NOTIFY_DEV_MUIC ||
		usb_typec_info.dest != CCIC_NOTIFY_DEV_BATTERY)
		return 0;

	if (pre_attach == usb_typec_info.attach)
		return 0;

	if (pdata->init_done == ON) {
		switch (usb_typec_info.cable_type) {
		case ATTACHED_DEV_NONE_MUIC:
		case ATTACHED_DEV_JIG_UART_OFF_MUIC:
		case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:	/* VBUS enabled */
		case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:	/* for otg test */
		case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:	/* for fuelgauge test */
		case ATTACHED_DEV_JIG_UART_ON_MUIC:
		case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:	/* VBUS enabled */
		case ATTACHED_DEV_JIG_USB_OFF_MUIC:
		case ATTACHED_DEV_JIG_USB_ON_MUIC:
			pr_info("[SX9360_WIFI]: %s skip cable = %d, attach = %d\n",
				__func__, usb_typec_info.cable_type, usb_typec_info.attach);
			break;
		default:
			pr_info("[SX9360_WIFI]: %s accept cable = %d, attach = %d\n",
				__func__, usb_typec_info.cable_type, usb_typec_info.attach);
			sx9360_set_offset_calibration(pdata);
			break;
		}
	}

	pre_attach = usb_typec_info.attach;

	return 0;
}
#endif

static int sx9360_check_chip_id(struct sx9360_p *data)
{
	int ret;
	u8 value = 0;
	
	ret = sx9360_i2c_read(data, SX9360_WHOAMI_REG, &value);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: whoami[0x%x] read failed %d\n", value, ret);
		return ret;
	}
	if(value != WHO_AM_I)
	{
		pr_err("[SX9360_WIFI]: invalid whoami(%x)\n", value);
		return -1;
	}

	return 0;
}

static int sx9360_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
	int ret = -ENODEV;
	struct sx9360_p *data = NULL;

	pr_info("[SX9360_WIFI]: %s - Probe Start!\n", __func__);
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		pr_err("[SX9360_WIFI]: %s - i2c_check_functionality error\n",
			__func__);
		goto exit;
	}

	/* create memory for main struct */
	data = kzalloc(sizeof(struct sx9360_p), GFP_KERNEL);
	if (data == NULL) {
		pr_err("[SX9360_WIFI]: %s - kzalloc error\n", __func__);
		ret = -ENOMEM;
		goto exit_kzalloc;
	}

	i2c_set_clientdata(client, data);
	data->client = client;
	data->factory_device = &client->dev;

	ret = sx9360_input_init(data);
	if (ret < 0)
		goto exit_input_init;

	wake_lock_init(&data->grip_wake_lock,
		WAKE_LOCK_SUSPEND, "grip_wake_lock");
	mutex_init(&data->mode_mutex);
	mutex_init(&data->read_mutex);

	ret = sx9360_parse_dt(data, &client->dev);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - of_node error\n", __func__);
		ret = -ENODEV;
		goto exit_of_node;
	}

	ret = sx9360_setup_pin(data);
	if (ret) {
		pr_err("[SX9360_WIFI]: %s - could not setup pin\n", __func__);
		goto exit_setup_pin;
	}

	/* read chip id */
	ret = sx9360_check_chip_id(data);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - chip id check failed %d\n", __func__, ret);
		goto exit_chip_reset;
	}

	ret = sx9360_i2c_write(data, SX9360_SOFTRESET_REG, SX9360_SOFTRESET);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - chip reset failed %d\n", __func__, ret);
		goto exit_chip_reset;
	}

	sx9360_initialize_variable(data);
	INIT_DELAYED_WORK(&data->init_work, sx9360_init_work_func);
	INIT_DELAYED_WORK(&data->irq_work, sx9360_irq_work_func);
	INIT_DELAYED_WORK(&data->debug_work, sx9360_debug_work_func);

	data->irq = gpio_to_irq(data->gpio_nirq);
	/* initailize interrupt reporting */
	ret = request_threaded_irq(data->irq, NULL, sx9360_interrupt_thread,
			IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
			"sx9360_irq", data);
	if (ret < 0) {
		pr_err("[SX9360_WIFI]: %s - failed to set request_threaded_irq %d"
			" as returning (%d)\n", __func__, data->irq, ret);
		goto exit_request_threaded_irq;
	}
	disable_irq(data->irq);

	ret = sensors_register(&data->factory_device,
		data, sensor_attrs, MODULE_NAME);
	if (ret) {
		pr_err("[SX9360_WIFI] %s - cound not register sensor(%d).\n",
			__func__, ret);
		goto exit_register_failed;
	}

	schedule_delayed_work(&data->init_work, msecs_to_jiffies(800));
	sx9360_set_debug_work(data, ON, 20000);

#if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
	manager_notifier_register(&data->cpuidle_ccic_nb,
					sx9360_ccic_handle_notification,
					MANAGER_NOTIFY_CCIC_SENSORHUB);
#endif

	pr_info("[SX9360_WIFI]: %s - Probe done!\n", __func__);

	return 0;

exit_register_failed:
	free_irq(data->irq, data);
exit_request_threaded_irq:
exit_chip_reset:
	gpio_free(data->gpio_nirq);
exit_setup_pin:
exit_of_node:
	mutex_destroy(&data->mode_mutex);
	mutex_destroy(&data->read_mutex);
	wake_lock_destroy(&data->grip_wake_lock);
	sysfs_remove_group(&data->input->dev.kobj, &sx9360_attribute_group);
	sensors_remove_symlink(&data->input->dev.kobj, data->input->name);
	input_unregister_device(data->input);
exit_input_init:
	kfree(data);
exit_kzalloc:
exit:
	pr_err("[SX9360_WIFI]: %s - Probe fail!\n", __func__);
	return ret;
}

static int sx9360_remove(struct i2c_client *client)
{
	struct sx9360_p *data = (struct sx9360_p *)i2c_get_clientdata(client);

	if (atomic_read(&data->enable) == ON)
		sx9360_set_enable(data, OFF);

	sx9360_set_mode(data, SX9360_MODE_SLEEP);

	cancel_delayed_work_sync(&data->init_work);
	cancel_delayed_work_sync(&data->irq_work);
	cancel_delayed_work_sync(&data->debug_work);
	free_irq(data->irq, data);
	gpio_free(data->gpio_nirq);

	wake_lock_destroy(&data->grip_wake_lock);
	sensors_unregister(data->factory_device, sensor_attrs);
	sensors_remove_symlink(&data->input->dev.kobj, data->input->name);
	sysfs_remove_group(&data->input->dev.kobj, &sx9360_attribute_group);
	input_unregister_device(data->input);
	mutex_destroy(&data->mode_mutex);
	mutex_destroy(&data->read_mutex);

	kfree(data);

	return 0;
}

static int sx9360_suspend(struct device *dev)
{
	struct sx9360_p *data = dev_get_drvdata(dev);
	int cnt = 0;

	pr_info("[SX9360_WIFI]: %s\n", __func__);
	/* before go to sleep, make the interrupt pin as high*/
	while ((sx9360_get_nirq_state(data) == 0) && (cnt++ < 3)) {
		sx9360_read_irqstate(data);
		msleep(20);
	}
	if (cnt >= 3)
		pr_err("[SX9360_WIFI]: %s - s/w reset fail(%d)\n", __func__, cnt);

	sx9360_set_debug_work(data, OFF, 1000);

	return 0;
}

static int sx9360_resume(struct device *dev)
{
	struct sx9360_p *data = dev_get_drvdata(dev);

	pr_info("[SX9360_WIFI]: %s\n", __func__);
	sx9360_set_debug_work(data, ON, 1000);

	return 0;
}

static void sx9360_shutdown(struct i2c_client *client)
{
	struct sx9360_p *data = i2c_get_clientdata(client);

	pr_info("[SX9360_WIFI]: %s\n", __func__);
	sx9360_set_debug_work(data, OFF, 1000);	
	if (atomic_read(&data->enable) == ON)
		sx9360_set_enable(data, OFF);

	sx9360_set_mode(data, SX9360_MODE_SLEEP);
}

static struct of_device_id sx9360_match_table[] = {
	{ .compatible = "sx9360_wifi",},
	{},
};

static const struct i2c_device_id sx9360_id[] = {
	{ "sx9360_match_table", 0 },
	{ }
};

static const struct dev_pm_ops sx9360_pm_ops = {
	.suspend = sx9360_suspend,
	.resume = sx9360_resume,
};

static struct i2c_driver sx9360_driver = {
	.driver = {
		.name	= MODEL_NAME,
		.owner	= THIS_MODULE,
		.of_match_table = sx9360_match_table,
		.pm = &sx9360_pm_ops
	},
	.probe		= sx9360_probe,
	.remove		= sx9360_remove,
	.shutdown	= sx9360_shutdown,
	.id_table	= sx9360_id,
};

static int __init sx9360_init(void)
{
	return i2c_add_driver(&sx9360_driver);
}

static void __exit sx9360_exit(void)
{
	i2c_del_driver(&sx9360_driver);
}

module_init(sx9360_init);
module_exit(sx9360_exit);

MODULE_DESCRIPTION("Semtech Corp. SX9360 Capacitive Touch Controller Driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");