/* drivers/input/touchscreen/maxim_sti.c
 *
 * Maxim SmartTouch Imager Touchscreen Driver
 *
 * Copyright (c)2013 Maxim Integrated Products, Inc.
 * Copyright (C) 2013, NVIDIA Corporation.  All Rights Reserved.
 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * 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/module.h>
#include <linux/moduleparam.h>
#include <linux/kmod.h>
#include <linux/kthread.h>
#include <linux/spi/spi.h>
#include <linux/firmware.h>
#include <linux/crc16.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/regulator/consumer.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include <uapi/linux/maxim_sti.h>
#include <linux/input/mt.h>
#include <asm/byteorder.h>  /* MUST include this header to get byte order */
#ifdef CONFIG_OF
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#endif

/****************************************************************************\
* Custom features                                                            *
\****************************************************************************/

#define VTG_DVDD_MIN_UV		1800000
#define VTG_DVDD_MAX_UV		1800000
#define VTG_AVDD_MIN_UV		2850000
#define VTG_AVDD_MAX_UV		2850000
#define INPUT_DEVICES		1
#define CFG_FILE_NAME_MAX	64

#define BUF_SIZE		4100
#define DEF_NL_MC_GROUPS	5
#define DEF_CHIP_ACC_METHOD	1
#define TOUCH_FUSION		"touch_fusion"

#if defined(CONFIG_FB)
#include <linux/fb.h>
#endif

/****************************************************************************\
* Device context structure, globals, and macros                              *
\****************************************************************************/
#define MAXIM_STI_NAME  "maxim_sti"

struct maxim_sti_pdata {
	char      *touch_fusion;
	char      *config_file;
	char      *nl_family;
	char      *fw_name;
	u32       nl_mc_groups;
	u32       chip_access_method;
	u32       default_reset_state;
	u32       tx_buf_size;
	u32       rx_buf_size;
	int       gpio_reset;
	int       gpio_irq;
	int       (*init)(struct maxim_sti_pdata *pdata, bool init);
	void      (*reset)(struct maxim_sti_pdata *pdata, int value);
	int       (*irq)(struct maxim_sti_pdata *pdata);
	bool	  mt_type_b_enabled;
};

struct dev_data;

struct chip_access_method {
	int (*read)(struct dev_data *dd, u16 address, u8 *buf, u16 len);
	int (*write)(struct dev_data *dd, u16 address, u8 *buf, u16 len);
};

struct dev_data {
	u8                           *tx_buf;
	u8                           *rx_buf;
	u8                           send_fail_count;
	u32                          nl_seq;
	u8                           nl_mc_group_count;
	bool                         nl_enabled;
	bool                         start_fusion;
	bool                         suspended;
	bool                         suspend_in_progress;
	bool                         resume_in_progress;
	bool                         expect_resume_ack;
	bool                         eraser_active;
	bool                         legacy_acceleration;
	bool                         input_no_deconfig;
	bool                         irq_registered;
	u16                          irq_param[MAX_IRQ_PARAMS];
	pid_t                        fusion_process;
	char                         input_phys[128];
	struct input_dev             *input_dev[INPUT_DEVICES];
	struct completion            suspend_resume;
	struct chip_access_method    chip;
	struct spi_device            *spi;
	struct genl_family           nl_family;
	struct genl_ops              *nl_ops;
	struct genl_multicast_group  *nl_mc_groups;
	struct sk_buff               *outgoing_skb;
	struct sk_buff_head          incoming_skb_queue;
	struct task_struct           *thread;
	struct sched_param           thread_sched;
	struct list_head             dev_list;
	struct regulator             *reg_avdd;
	struct regulator             *reg_dvdd;
	bool                         supply;
	void                         (*service_irq)(struct dev_data *dd);
	struct notifier_block        fb_notifier;
	struct kobject               *parent;
	char                         config_file[CFG_FILE_NAME_MAX];
	u8                           sysfs_created;
	u16                          chip_id;
	u16                          glove_enabled;
	u16                          charger_mode_en;
	u16                          lcd_fps;
	u16                          tf_ver;
	u16                          drv_ver;
	u16                          fw_ver;
	u16            	             fw_protocol;
	u32                          tf_status;
	bool                         idle;
	bool                         gesture_reset;
	bool			     touch_status[INPUT_DEVICES]
						[MAX_INPUT_EVENTS];
	u16                          gesture_en;
	unsigned long int            sysfs_update_type;
	struct completion            sysfs_ack_glove;
	struct completion            sysfs_ack_charger;
	struct completion            sysfs_ack_lcd_fps;
	struct mutex                 sysfs_update_mutex;
	struct pinctrl_state *pinctrl_state_active;
	struct pinctrl_state *pinctrl_state_suspended;
	struct pinctrl *ts_pinctrl;
};

static unsigned short    panel_id;
static struct list_head  dev_list;
static spinlock_t        dev_lock;

static irqreturn_t irq_handler(int irq, void *context);
static void service_irq(struct dev_data *dd);
static void service_irq_legacy_acceleration(struct dev_data *dd);
static int send_sysfs_info(struct dev_data *dd);

#define ERROR(a, b...) dev_err(&dd->spi->dev, "maxim: %s (ERROR:%s:%d): " \
				a "\n", dd->nl_family.name, __func__, \
				__LINE__, ##b)
#define INFO(a, b...) dev_info(&dd->spi->dev, "maxim: %s: " a "\n", \
			     dd->nl_family.name, ##b)
#define DBG(a, b...) dev_dbg(&dd->spi->dev, "maxim: %s: " a "\n", \
			     dd->nl_family.name, ##b)

#define DRV_MSG_DBG(...)
#define DRV_PT_DBG(...)

/****************************************************************************\
* Chip access methods                                                        *
\****************************************************************************/

static int
spi_read_123(struct dev_data *dd, u16 address, u8 *buf, u16 len, bool add_len)
{
	struct spi_message   message;
	struct spi_transfer  transfer;
	u16                  *tx_buf = (u16 *)dd->tx_buf;
	u16                  *rx_buf = (u16 *)dd->rx_buf;
	u16                  words = len / sizeof(u16), header_len = 1;
	u16                  *ptr2 = rx_buf + 1;
	int                  ret;

	if (tx_buf == NULL || rx_buf == NULL)
		return -ENOMEM;

	tx_buf[0] = (address << 1) | 0x0001;

	if (add_len) {
		tx_buf[1] = words;
		ptr2++;
		header_len++;
	}

	spi_message_init(&message);
	memset(&transfer, 0, sizeof(transfer));

	transfer.len = len + header_len * sizeof(u16);
	transfer.tx_buf = tx_buf;
	transfer.rx_buf = rx_buf;
	spi_message_add_tail(&transfer, &message);

	do {
		ret = spi_sync(dd->spi, &message);
	} while (ret == -EAGAIN);

	memcpy(buf, ptr2, len);

	return ret;
}

static int
spi_write_123(struct dev_data *dd, u16 address, u8 *buf, u16 len,
	      bool add_len)
{
	struct maxim_sti_pdata  *pdata = dd->spi->dev.platform_data;
	u16                     *tx_buf = (u16 *)dd->tx_buf;
	u16                     words = len / sizeof(u16), header_len = 1;
	int  ret;

	if (tx_buf == NULL)
		return -ENOMEM;

	tx_buf[0] = address << 1;
	if (add_len) {
		tx_buf[1] = words;
		header_len++;
	}
	memcpy(tx_buf + header_len, buf, len);

	do {
		ret = spi_write(dd->spi, tx_buf,
				len + header_len * sizeof(u16));
	} while (ret == -EAGAIN);

	memset(dd->tx_buf, 0xFF, pdata->tx_buf_size);
	return ret;
}

/* ======================================================================== */

static int
spi_read_1(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_read_123(dd, address, buf, len, true);
}

static int
spi_write_1(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_write_123(dd, address, buf, len, true);
}

/* ======================================================================== */

static inline int
stop_legacy_acceleration(struct dev_data *dd)
{
	u16  value = 0xDEAD, status, i;
	int  ret;

	ret = spi_write_123(dd, 0x0003, (u8 *)&value,
				sizeof(value), false);
	if (ret < 0)
		return -1;
	usleep_range(100, 120);

	for (i = 0; i < 200; i++) {
		ret = spi_read_123(dd, 0x0003, (u8 *)&status, sizeof(status),
				   false);
		if (ret < 0)
			return -1;
		if (status == 0xABCD)
			return 0;
	}

	return -2;
}

static inline int
start_legacy_acceleration(struct dev_data *dd)
{
	u16  value = 0xBEEF;
	int  ret;

	ret = spi_write_123(dd, 0x0003, (u8 *)&value, sizeof(value), false);
	usleep_range(100, 120);

	return ret;
}

static inline int
spi_rw_2_poll_status(struct dev_data *dd)
{
	u16  status, i;
	int  ret;

	for (i = 0; i < 200; i++) {
		ret = spi_read_123(dd, 0x0000, (u8 *)&status, sizeof(status),
				   false);
		if (ret < 0)
			return -1;
		if (status == 0xABCD)
			return 0;
	}

	return -2;
}

static inline int
spi_read_2_page(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	u16  request[] = {0xFEDC, (address << 1) | 0x0001, len / sizeof(u16)};
	int  ret;

	/* write read request header */
	ret = spi_write_123(dd, 0x0000, (u8 *)request, sizeof(request),
			    false);
	if (ret < 0)
		return -1;

	/* poll status */
	ret = spi_rw_2_poll_status(dd);
	if (ret < 0)
		return ret;

	/* read data */
	ret = spi_read_123(dd, 0x0004, (u8 *)buf, len, false);
	return ret;
}

static inline int
spi_write_2_page(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	u16  page[254];
	int  ret;

	page[0] = 0xFEDC;
	page[1] = address << 1;
	page[2] = len / sizeof(u16);
	page[3] = 0x0000;
	memcpy(page + 4, buf, len);

	/* write data with write request header */
	ret = spi_write_123(dd, 0x0000, (u8 *)page, len + 4 * sizeof(u16),
			    false);
	if (ret < 0)
		return -1;

	/* poll status */
	return spi_rw_2_poll_status(dd);
}

static inline int
spi_rw_2(struct dev_data *dd, u16 address, u8 *buf, u16 len,
	 int (*func)(struct dev_data *dd, u16 address, u8 *buf, u16 len))
{
	u16  rx_len, rx_limit = 250 * sizeof(u16), offset = 0;
	int  ret;

	while (len > 0) {
		rx_len = (len > rx_limit) ? rx_limit : len;
		if (dd->legacy_acceleration)
			stop_legacy_acceleration(dd);
		ret = func(dd, address + (offset / sizeof(u16)), buf + offset,
			   rx_len);
		if (dd->legacy_acceleration)
			start_legacy_acceleration(dd);
		if (ret < 0)
			return ret;
		offset += rx_len;
		len -= rx_len;
	}

	return 0;
}

static int
spi_read_2(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_rw_2(dd, address, buf, len, spi_read_2_page);
}

static int
spi_write_2(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_rw_2(dd, address, buf, len, spi_write_2_page);
}

/* ======================================================================== */

static int
spi_read_3(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_read_123(dd, address, buf, len, false);
}

static int
spi_write_3(struct dev_data *dd, u16 address, u8 *buf, u16 len)
{
	return spi_write_123(dd, address, buf, len, false);
}

/* ======================================================================== */

static struct chip_access_method chip_access_methods[] = {
	{
		.read = spi_read_1,
		.write = spi_write_1,
	},
	{
		.read = spi_read_2,
		.write = spi_write_2,
	},
	{
		.read = spi_read_3,
		.write = spi_write_3,
	},
};

static int
set_chip_access_method(struct dev_data *dd, u8 method)
{
	if (method == 0 || method > ARRAY_SIZE(chip_access_methods))
		return -1;

	memcpy(&dd->chip, &chip_access_methods[method - 1], sizeof(dd->chip));
	return 0;
}

/* ======================================================================== */

static inline int
stop_legacy_acceleration_canned(struct dev_data *dd)
{
	u16  value = dd->irq_param[18];

	return dd->chip.write(dd, dd->irq_param[16], (u8 *)&value,
			      sizeof(value));
}

static inline int
start_legacy_acceleration_canned(struct dev_data *dd)
{
	u16  value = dd->irq_param[17];

	return dd->chip.write(dd, dd->irq_param[16], (u8 *)&value,
			      sizeof(value));
}

/* ======================================================================== */

#define FLASH_BLOCK_SIZE  64      /* flash write buffer in words */
#define FIRMWARE_SIZE     0xC000  /* fixed 48Kbytes */

static int bootloader_wait_ready(struct dev_data *dd)
{
	u16  status, i;

	for (i = 0; i < 15; i++) {
		if (spi_read_3(dd, 0x00FF, (u8 *)&status,
			       sizeof(status)) != 0)
			return -1;
		if (status == 0xABCC)
			return 0;
		if (i >= 3)
			usleep_range(500, 700);
	}
	ERROR("unexpected status %04X", status);
	return -1;
}

static int bootloader_complete(struct dev_data *dd)
{
	u16  value = 0x5432;

	return spi_write_3(dd, 0x00FF, (u8 *)&value, sizeof(value));
}

static int bootloader_read_data(struct dev_data *dd, u16 *value)
{
	u16  buffer[2];

	if (spi_read_3(dd, 0x00FE, (u8 *)buffer, sizeof(buffer)) != 0)
		return -1;
	if (buffer[1] != 0xABCC)
		return -1;

	*value = buffer[0];
	return bootloader_complete(dd);
}

static int bootloader_write_data(struct dev_data *dd, u16 value)
{
	u16  buffer[2] = {value, 0x5432};

	if (bootloader_wait_ready(dd) != 0)
		return -1;
	return spi_write_3(dd, 0x00FE, (u8 *)buffer, sizeof(buffer));
}

static int bootloader_wait_command(struct dev_data *dd)
{
	u16  value, i;

	for (i = 0; i < 15; i++) {
		if (bootloader_read_data(dd, &value) == 0 && value == 0x003E)
			return 0;
		if (i >= 3)
			usleep_range(500, 700);
	}
	return -1;
}

static int bootloader_enter(struct dev_data *dd)
{
	int i;
	u16 enter[3] = {0x0047, 0x00C7, 0x0007};

	for (i = 0; i < 3; i++) {
		if (spi_write_3(dd, 0x7F00, (u8 *)&enter[i],
				sizeof(enter[i])) != 0)
			return -1;
	}

	if (bootloader_wait_command(dd) != 0)
		return -1;
	return 0;
}

static int bootloader_exit(struct dev_data *dd)
{
	u16  value = 0x0000;

	if (bootloader_write_data(dd, 0x0001) != 0)
		return -1;
	return spi_write_3(dd, 0x7F00, (u8 *)&value, sizeof(value));
}

static int bootloader_get_crc(struct dev_data *dd, u16 *crc16, u16 len)
{
	u16 command[] = {0x0030, 0x0002, 0x0000, 0x0000, len & 0xFF,
			len >> 8}, value[2], i;

	for (i = 0; i < ARRAY_SIZE(command); i++)
		if (bootloader_write_data(dd, command[i]) != 0)
			return -1;
	msleep(200); /* wait 200ms for it to get done */

	for (i = 0; i < 2; i++)
		if (bootloader_read_data(dd, &value[i]) != 0)
			return -1;

	if (bootloader_wait_command(dd) != 0)
		return -1;
	*crc16 = (value[1] << 8) | value[0];
	return 0;
}

static int bootloader_set_byte_mode(struct dev_data *dd)
{
	u16  command[2] = {0x000A, 0x0000}, i;

	for (i = 0; i < ARRAY_SIZE(command); i++)
		if (bootloader_write_data(dd, command[i]) != 0)
			return -1;
	if (bootloader_wait_command(dd) != 0)
		return -1;
	return 0;
}

static int bootloader_erase_flash(struct dev_data *dd)
{
	if (bootloader_write_data(dd, 0x0002) != 0)
		return -1;
	msleep(60); /* wait 60ms */
	if (bootloader_wait_command(dd) != 0)
		return -1;
	return 0;
}

static int bootloader_write_flash(struct dev_data *dd, u16 *image, u16 len)
{
	u16  command[] = {0x00F0, 0x0000, len >> 8, 0x0000, 0x0000};
	u16  i, buffer[FLASH_BLOCK_SIZE];

	for (i = 0; i < ARRAY_SIZE(command); i++)
		if (bootloader_write_data(dd, command[i]) != 0)
			return -1;

	for (i = 0; i < ((len / sizeof(u16)) / FLASH_BLOCK_SIZE); i++) {
		if (bootloader_wait_ready(dd) != 0)
			return -1;
		memcpy(buffer, (void *)(image + i * FLASH_BLOCK_SIZE),
			sizeof(buffer));
		if (spi_write_3(dd, ((i % 2) == 0) ? 0x0000 : 0x0040,
				(u8 *)buffer, sizeof(buffer)) != 0)
			return -1;
		if (bootloader_complete(dd) != 0)
			return -1;
	}

	usleep_range(10000, 11000);
	if (bootloader_wait_command(dd) != 0)
		return -1;
	return 0;
}

static int device_fw_load(struct dev_data *dd, const struct firmware *fw)
{
	u16  fw_crc16, chip_crc16;

	fw_crc16 = crc16(0, fw->data, fw->size);
	INFO("firmware size (%d) CRC16(0x%04X)", (int)fw->size, fw_crc16);
	if (bootloader_enter(dd) != 0) {
		ERROR("failed to enter bootloader");
		return -1;
	}
	if (bootloader_get_crc(dd, &chip_crc16, fw->size) != 0) {
		ERROR("failed to get CRC16 from the chip");
		return -1;
	}
	INFO("chip CRC16(0x%04X)", chip_crc16);
	if (fw_crc16 != chip_crc16) {
		INFO("will reprogram chip");
		if (bootloader_erase_flash(dd) != 0) {
			ERROR("failed to erase chip flash");
			return -1;
		}
		INFO("flash erase OK");
		if (bootloader_set_byte_mode(dd) != 0) {
			ERROR("failed to set byte mode");
			return -1;
		}
		INFO("byte mode OK");
		if (bootloader_write_flash(dd, (u16 *)fw->data,
							fw->size) != 0) {
			ERROR("failed to write flash");
			return -1;
		}
		INFO("flash write OK");
		if (bootloader_get_crc(dd, &chip_crc16, fw->size) != 0) {
			ERROR("failed to get CRC16 from the chip");
			return -1;
		}
		if (fw_crc16 != chip_crc16) {
			ERROR("failed to verify programming! (0x%04X)",
			      chip_crc16);
			return -1;
		}
		INFO("chip programmed successfully, new chip CRC16(0x%04X)",
			chip_crc16);
	}
	if (bootloader_exit(dd) != 0) {
		ERROR("failed to exit bootloader");
		return -1;
	}
	return 0;
}

static int fw_request_load(struct dev_data *dd)
{
	const struct firmware *fw;
	int  ret;

	ret = request_firmware(&fw, "maxim_fp35.bin", &dd->spi->dev);
	if (ret || fw == NULL) {
		ERROR("firmware request failed (%d,%p)", ret, fw);
		return -1;
	}
	if (fw->size != FIRMWARE_SIZE) {
		release_firmware(fw);
		ERROR("incoming firmware is of wrong size (%04X)",
						(unsigned int)fw->size);
		return -1;
	}
	ret = device_fw_load(dd, fw);
	if (ret != 0 && bootloader_exit(dd) != 0)
		ERROR("failed to exit bootloader");
	release_firmware(fw);
	return ret;
}

/* ======================================================================== */

static void stop_idle_scan(struct dev_data *dd)
{
	u16 value;

	value = dd->irq_param[13];
	(void)dd->chip.write(dd, dd->irq_param[19],
				(u8 *)&value, sizeof(value));
}

static void stop_scan_canned(struct dev_data *dd)
{
	u16  value;
	u16  i, clear[2] = { 0 };

	if (dd->legacy_acceleration)
		(void)stop_legacy_acceleration_canned(dd);
	value = dd->irq_param[13];
	(void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value,
			     sizeof(value));
	usleep_range(dd->irq_param[15], dd->irq_param[15] + 1000);
	(void)dd->chip.read(dd, dd->irq_param[0], (u8 *)&clear[0],
			    sizeof(clear[0]));
	(void)dd->chip.write(dd, dd->irq_param[0], (u8 *)&clear,
			     sizeof(clear));

	for (i = 28; i < 32; i++) {
		value = dd->irq_param[i];
		(void)dd->chip.write(dd, dd->irq_param[27],
				(u8 *)&value, sizeof(value));
	}
	value = dd->irq_param[33];
	(void)dd->chip.write(dd, dd->irq_param[32],
				(u8 *)&value, sizeof(value));
	usleep_range(500, 1000);
	for (i = 28; i < 32; i++) {
		value = dd->irq_param[i];
		(void)dd->chip.write(dd, dd->irq_param[27],
				(u8 *)&value, sizeof(value));
	}
	value = dd->irq_param[13];
	(void)dd->chip.write(dd, dd->irq_param[32],
				(u8 *)&value, sizeof(value));
}

static void start_scan_canned(struct dev_data *dd)
{
	u16  value;

	if (dd->legacy_acceleration) {
		(void)start_legacy_acceleration_canned(dd);
	} else {
		value = dd->irq_param[14];
		(void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value,
				     sizeof(value));
	}
}

static void enable_gesture(struct dev_data *dd)
{
	u16  value;

	if (!dd->idle)
		stop_scan_canned(dd);

	value = dd->irq_param[13];
	(void)dd->chip.write(dd, dd->irq_param[34],
			     (u8 *)&value, sizeof(value));
	value = dd->irq_param[36];
	(void)dd->chip.write(dd, dd->irq_param[35],
			     (u8 *)&value, sizeof(value));

	value = dd->irq_param[18];
	(void)dd->chip.write(dd, dd->irq_param[16], (u8 *)&value,
			     sizeof(value));

	value = dd->irq_param[20];
	(void)dd->chip.write(dd, dd->irq_param[19], (u8 *)&value,
			     sizeof(value));
	if (!dd->idle) {
		value = dd->irq_param[26];
		(void)dd->chip.write(dd, dd->irq_param[25], (u8 *)&value,
				     sizeof(value));
	}
}

static void gesture_wake_detect(struct dev_data *dd)
{
	u16    status, value;
	int    ret = 0;

	ret = dd->chip.read(dd, dd->irq_param[0],
			    (u8 *)&status, sizeof(status));
	INFO("%s: %d = %04X", __func__, dd->irq_param[0], status);
	if (status & dd->irq_param[10])
		dd->gesture_reset = true;
	if (status & dd->irq_param[18] && dd->input_dev[INPUT_DEVICES - 1]) {
		ret = dd->chip.read(dd, dd->irq_param[22],
				    (u8 *)&value, sizeof(value));
		INFO("gesture code = 0x%04X", value);
		if (value == dd->irq_param[24]) {
			INFO("gesture detected");
			input_report_key(dd->input_dev[INPUT_DEVICES - 1], KEY_POWER, 1);
			input_sync(dd->input_dev[INPUT_DEVICES - 1]);
			input_report_key(dd->input_dev[INPUT_DEVICES - 1], KEY_POWER, 0);
			input_sync(dd->input_dev[INPUT_DEVICES - 1]);
		}
		value = dd->irq_param[18];
		ret = dd->chip.write(dd, dd->irq_param[0],
				     (u8 *)&value, sizeof(value));
	} else {
		ret = dd->chip.write(dd, dd->irq_param[0],
				     (u8 *)&status, sizeof(status));
	}
}

static void finish_gesture(struct dev_data *dd)
{
	struct maxim_sti_pdata *pdata = dd->spi->dev.platform_data;
	u16 value;
	u8  fail_count = 0;
	int ret;

	value = dd->irq_param[21];
	(void)dd->chip.write(dd, dd->irq_param[19], (u8 *)&value,
			     sizeof(value));
	do {
		msleep(20);
		ret = dd->chip.read(dd, dd->irq_param[25], (u8 *)&value,
				    sizeof(value));
		if (ret < 0 || fail_count >= 20) {
			ERROR("failed to read control register (%d)", ret);
			pdata->reset(pdata, 0);
			msleep(20);
			pdata->reset(pdata, 1);
			return;
		}
		INFO("ctrl = 0x%04X", value);
		fail_count++;
	} while (value);
}

static int regulator_init(struct dev_data *dd)
{
	int ret;

	dd->reg_avdd = devm_regulator_get(&dd->spi->dev, "avdd");
	if (IS_ERR(dd->reg_avdd)) {
		ret = PTR_ERR(dd->reg_avdd);
		dd->reg_avdd = NULL;
		dev_err(&dd->spi->dev, "can't find avdd regulator: %d\n",
				ret);
		return ret;
	}

	ret = regulator_set_voltage(dd->reg_avdd,
			VTG_AVDD_MIN_UV, VTG_AVDD_MAX_UV);
	if (ret) {
		dev_err(&dd->spi->dev,
			"can't set avdd regulator voltage: %d\n", ret);
		goto err_free_avdd;
	}

	dd->reg_dvdd = devm_regulator_get(&dd->spi->dev, "dvdd");
	if (IS_ERR(dd->reg_dvdd)) {
		ret = PTR_ERR(dd->reg_avdd);
		dd->reg_dvdd = NULL;
		dev_err(&dd->spi->dev, "can't find avdd regulator: %d\n",
				ret);
		goto err_free_avdd;
	}

	ret = regulator_set_voltage(dd->reg_dvdd,
			VTG_DVDD_MIN_UV, VTG_DVDD_MAX_UV);
	if (ret) {
		dev_err(&dd->spi->dev,
			"can't set dvdd regulator voltage: %d\n", ret);
		goto err_free_dvdd;
	}
	return 0;

err_free_dvdd:
	devm_regulator_put(dd->reg_dvdd);
	dd->reg_dvdd = NULL;
err_free_avdd:
	devm_regulator_put(dd->reg_avdd);
	dd->reg_avdd = NULL;
	return ret;
}

static void regulator_uninit(struct dev_data *dd)
{
	if (dd->reg_dvdd)
		devm_regulator_put(dd->reg_dvdd);
	if (dd->reg_avdd)
		devm_regulator_put(dd->reg_avdd);
}

static int regulator_control(struct dev_data *dd, bool on)
{
	int ret;

	if (!dd->reg_avdd || !dd->reg_dvdd)
		return 0;

	if (on && !dd->supply) {
		DBG("regulator ON.");
		ret = regulator_enable(dd->reg_dvdd);
		if (ret < 0) {
			ERROR("failed to enable dvdd regulator: %d", ret);
			return ret;
		}
		usleep_range(1000, 1020);

		ret = regulator_enable(dd->reg_avdd);
		if (ret < 0) {
			ERROR("failed to enable avdd regulator: %d", ret);
			regulator_disable(dd->reg_dvdd);
			return ret;
		}

		dd->supply = true;
	} else if (!on && dd->supply) {
		DBG("regulator OFF.");

		ret = regulator_disable(dd->reg_avdd);
		if (ret < 0)
			ERROR("Failed to disable regulator avdd: %d", ret);

		ret = regulator_disable(dd->reg_dvdd);
		if (ret < 0)
			ERROR("Failed to disable regulator dvdd: %d", ret);

		dd->supply = false;
	}

	return 0;
}

#define MAXIM_STI_GPIO_ERROR(ret, gpio, op)                                \
	if (ret < 0) {                                                     \
		pr_err("%s: GPIO %d %s failed (%d)\n", __func__, gpio, op, \
			ret);                                              \
		return ret;						   \
	}


int maxim_sti_gpio_init(struct maxim_sti_pdata *pdata, bool init)
{
	int  ret;

	if (init) {
		ret = gpio_request(pdata->gpio_irq, "maxim_sti_irq");
		MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_irq, "request");
		ret = gpio_direction_input(pdata->gpio_irq);
		MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_irq, "direction");
		ret = gpio_request(pdata->gpio_reset, "maxim_sti_reset");
		MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_reset, "request");
		ret = gpio_direction_output(pdata->gpio_reset,
					    pdata->default_reset_state);
		MAXIM_STI_GPIO_ERROR(ret, pdata->gpio_reset, "direction");
	} else {
		gpio_free(pdata->gpio_irq);
		gpio_free(pdata->gpio_reset);
	}

	return 0;
}

void maxim_sti_gpio_reset(struct maxim_sti_pdata *pdata, int value)
{
	gpio_set_value(pdata->gpio_reset, !!value);
}

int maxim_sti_gpio_irq(struct maxim_sti_pdata *pdata)
{
	return gpio_get_value(pdata->gpio_irq);
}

/****************************************************************************\
* Device Tree Support
\****************************************************************************/

#ifdef CONFIG_OF
static int maxim_parse_dt(struct device *dev, struct maxim_sti_pdata *pdata)
{
	struct device_node *np = dev->of_node;
	u32 flags;
	const char *str;
	int ret;

	pdata->gpio_reset = of_get_named_gpio_flags(np,
			"maxim_sti,reset-gpio", 0, &flags);
	pdata->default_reset_state = (u8)flags;

	pdata->gpio_irq = of_get_named_gpio_flags(np,
			"maxim_sti,irq-gpio", 0, &flags);

	pdata->mt_type_b_enabled =
		of_property_read_bool(np, "maxim_sti,mt_type_b_enabled");

	ret = of_property_read_string(np, "maxim_sti,touch_fusion", &str);
	if (ret) {
		dev_err(dev, "%s: unable to read touch_fusion location (%d)\n",
								__func__, ret);
		goto fail;
	}
	pdata->touch_fusion = (char *)str;

	ret = of_property_read_string(np, "maxim_sti,config_file", &str);
	if (ret) {
		dev_err(dev, "%s: unable to read config_file location (%d)\n",
								__func__, ret);
		goto fail;
	}
	pdata->config_file = (char *)str;

	ret = of_property_read_string(np, "maxim_sti,fw_name", &str);
	if (ret) {
		dev_err(dev, "%s: unable to read fw_name (%d)\n",
								__func__, ret);
	}
	pdata->fw_name = (char *)str;

	return 0;

fail:
	return ret;
}
#endif

/****************************************************************************\
* Suspend/resume processing                                                  *
\****************************************************************************/

#ifdef CONFIG_PM_SLEEP
static int suspend(struct device *dev)
{
	struct dev_data  *dd = spi_get_drvdata(to_spi_device(dev));

	DBG("%s: suspending", __func__);

	if (dd->suspended) {
		DBG("%s: already suspended", __func__);
		return 0;
	}

	if (dd->suspend_in_progress) {
		dev_err(dev, "suspend already in progress!");
		return -EINVAL;
	}

	dd->suspend_in_progress = true;
	wake_up_process(dd->thread);
	wait_for_completion(&dd->suspend_resume);

	DBG("%s: suspended", __func__);

	return 0;
}

static int resume(struct device *dev)
{
	struct dev_data  *dd = spi_get_drvdata(to_spi_device(dev));

	DBG("%s: resuming", __func__);

	if (!dd->suspended && !dd->suspend_in_progress) {
		DBG("%s: not suspended", __func__);
		return 0;
	}

	dd->resume_in_progress = true;
	wake_up_process(dd->thread);
	wait_for_completion(&dd->suspend_resume);
	DBG("%s: resumed", __func__);
	return 0;
}

static const struct dev_pm_ops pm_ops = {
	.suspend = suspend,
	.resume = resume,
};

#if defined(CONFIG_FB)
static int fb_notifier_callback(struct notifier_block *self,
				unsigned long event, void *data)
{
	struct fb_event  *evdata = data;
	int              *blank;
	struct dev_data  *dd = container_of(self,
					    struct dev_data, fb_notifier);

	DBG("%s: event = %lu", __func__, event);
	if (evdata && evdata->data && event == FB_EVENT_BLANK) {
		blank = evdata->data;
		if (*blank == FB_BLANK_UNBLANK)
			resume(&dd->spi->dev);
		else if (*blank == FB_BLANK_POWERDOWN)
			suspend(&dd->spi->dev);
	}
	return 0;
}
#endif
#endif

/****************************************************************************\
* Netlink processing                                                         *
\****************************************************************************/

static inline int
nl_msg_new(struct dev_data *dd, u8 dst)
{
	dd->outgoing_skb = alloc_skb(NL_BUF_SIZE, GFP_KERNEL);
	if (dd->outgoing_skb == NULL)
		return -ENOMEM;
	nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id, dd->nl_seq++,
		    dst);
	if (dd->nl_seq == 0)
		dd->nl_seq++;
	return 0;
}

static int
nl_callback_noop(struct sk_buff *skb, struct genl_info *info)
{
	return 0;
}

static void
release_slot_events(struct dev_data *dd, bool *active_touch, int inp)
{
	int i;

	for (i = 0; i < MAX_INPUT_EVENTS; i++) {
		if (dd->touch_status[inp][i] == true &&
					active_touch[i] == false) {
			input_mt_slot(dd->input_dev[inp], i);
			input_mt_report_slot_state(dd->input_dev[inp],
				MT_TOOL_FINGER, 0);
		}
		dd->touch_status[inp][i] = active_touch[i];
	}
}

static inline bool
nl_process_driver_msg(struct dev_data *dd, u16 msg_id, void *msg)
{
	struct maxim_sti_pdata        *pdata = dd->spi->dev.platform_data;
	struct dr_echo_request        *echo_msg;
	struct fu_echo_response       *echo_response;
	struct dr_chip_read           *read_msg;
	struct fu_chip_read_result    *read_result;
	struct dr_chip_write          *write_msg;
	struct dr_chip_access_method  *chip_access_method_msg;
	struct dr_delay               *delay_msg;
	struct fu_irqline_status      *irqline_status;
	struct dr_config_irq          *config_irq_msg;
	struct dr_config_input        *config_input_msg;
	struct dr_config_watchdog     *config_watchdog_msg;
	struct dr_input               *input_msg;
	struct dr_legacy_acceleration *legacy_acceleration_msg;
	struct dr_handshake           *handshake_msg;
	struct fu_handshake_response  *handshake_response;
	struct dr_config_fw           *config_fw_msg;
	struct dr_sysfs_ack           *sysfs_ack_msg;
	struct dr_idle                *idle_msg;
	struct dr_tf_status           *tf_status_msg;
	u8			      i, inp;
	int                           ret, id;
	u16                           read_value[2] = { 0 };
	bool	active_touch[INPUT_DEVICES][MAX_INPUT_EVENTS] = { {false} };

	if (dd->expect_resume_ack && msg_id != DR_DECONFIG &&
	    msg_id != DR_RESUME_ACK)
		return false;

	switch (msg_id) {
	case DR_ADD_MC_GROUP:
		INFO("warning: deprecated dynamic mc group add request!");
		return false;
	case DR_ECHO_REQUEST:
		DRV_MSG_DBG("msg: %s", "DR_ECHO_REQUEST");
		echo_msg = msg;
		echo_response = nl_alloc_attr(dd->outgoing_skb->data,
					      FU_ECHO_RESPONSE,
					      sizeof(*echo_response));
		if (echo_response == NULL)
			goto alloc_attr_failure;
		echo_response->cookie = echo_msg->cookie;
		return true;
	case DR_CHIP_READ:
		DRV_MSG_DBG("msg: %s", "DR_CHIP_READ");
		read_msg = msg;
		read_result = nl_alloc_attr(dd->outgoing_skb->data,
				FU_CHIP_READ_RESULT,
				sizeof(*read_result) + read_msg->length);
		if (read_result == NULL)
			goto alloc_attr_failure;
		read_result->address = read_msg->address;
		read_result->length = read_msg->length;
		ret = dd->chip.read(dd, read_msg->address, read_result->data,
				    read_msg->length);
		if (ret < 0)
			ERROR("failed to read from chip (%d, %d, %d)",
				read_msg->address, read_msg->length, ret);
		return true;
	case DR_CHIP_WRITE:
		DRV_MSG_DBG("msg: %s", "DR_CHIP_WRITE");
		write_msg = msg;
		if (write_msg->address == dd->irq_param[12] &&
		    write_msg->data[0] == dd->irq_param[13]) {
			ret = dd->chip.write(dd, write_msg->address,
					     write_msg->data,
					     write_msg->length);
			if (ret < 0)
				ERROR("failed to write chip (%d)", ret);
			msleep(15);
			ret = dd->chip.read(dd, dd->irq_param[0],
					    (u8 *)read_value,
					    sizeof(read_value));
			if (ret < 0)
				ERROR("failed to read from chip (%d)", ret);
			ret = dd->chip.write(dd, dd->irq_param[0],
					     (u8 *)read_value,
					     sizeof(read_value));
			return false;
		}
		if (write_msg->address == dd->irq_param[0]) {
			if (((u16 *)write_msg->data)[0] == dd->irq_param[11]) {
				ret = dd->chip.read(dd, dd->irq_param[0],
						(u8 *)read_value,
						sizeof(read_value));
				if (ret < 0)
					ERROR("failed to read from chip (%d)", ret);
				ret = dd->chip.write(dd, dd->irq_param[0],
						(u8 *)read_value,
						sizeof(read_value));
				return false;
			}
			read_value[0] = ((u16 *)write_msg->data)[0];
			ret = dd->chip.write(dd, write_msg->address,
					     (u8 *)read_value,
					     sizeof(read_value));
			return false;
		}
		ret = dd->chip.write(dd, write_msg->address, write_msg->data,
				     write_msg->length);
		if (ret < 0)
			ERROR("failed to write chip (%d, %d, %d)",
				write_msg->address, write_msg->length, ret);
		return false;
	case DR_CHIP_RESET:
		DRV_MSG_DBG("msg: %s", "DR_CHIP_RESET");
		pdata->reset(pdata, ((struct dr_chip_reset *)msg)->state);
		return false;
	case DR_GET_IRQLINE:
		DRV_MSG_DBG("msg: %s", "DR_GET_IRQLINE");
		irqline_status = nl_alloc_attr(dd->outgoing_skb->data,
					       FU_IRQLINE_STATUS,
					       sizeof(*irqline_status));
		if (irqline_status == NULL)
			goto alloc_attr_failure;
		irqline_status->status = pdata->irq(pdata);
		return true;
	case DR_DELAY:
		DRV_MSG_DBG("msg: %s", "DR_DELAY");
		delay_msg = msg;
		if (delay_msg->period > 1000)
			msleep(delay_msg->period / 1000);
		usleep_range(delay_msg->period % 1000,
			    (delay_msg->period % 1000) + 10);
		return false;
	case DR_CHIP_ACCESS_METHOD:
		DRV_MSG_DBG("msg: %s", "DR_CHIP_ACCESS_METHOD");
		chip_access_method_msg = msg;
		ret = set_chip_access_method(dd,
					     chip_access_method_msg->method);
		if (ret < 0)
			ERROR("failed to set chip access method (%d) (%d)",
			      ret, chip_access_method_msg->method);
		return false;
	case DR_CONFIG_IRQ:
		DRV_MSG_DBG("msg: %s", "DR_CONFIG_IRQ");
		config_irq_msg = msg;
		if (config_irq_msg->irq_params > MAX_IRQ_PARAMS) {
			ERROR("too many IRQ parameters");
			return false;
		}
		memcpy(dd->irq_param, config_irq_msg->irq_param,
		       config_irq_msg->irq_params * sizeof(dd->irq_param[0]));
		if (dd->irq_registered)
			return false;
		dd->service_irq = service_irq;
		ret = request_irq(dd->spi->irq, irq_handler,
			(config_irq_msg->irq_edge == DR_IRQ_RISING_EDGE) ?
				IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING,
						pdata->nl_family, dd);
		if (ret < 0) {
			ERROR("failed to request IRQ (%d)", ret);
		} else {
			dd->irq_registered = true;
			wake_up_process(dd->thread);
		}
		return false;
	case DR_CONFIG_INPUT:
		DRV_MSG_DBG("msg: %s", "DR_CONFIG_INPUT");
		config_input_msg = msg;
		for (i = 0; i < INPUT_DEVICES; i++)
			if (dd->input_dev[i] != NULL)
				return false;
		for (i = 0; i < INPUT_DEVICES; i++) {
			dd->input_dev[i] = input_allocate_device();
			if (dd->input_dev[i] == NULL) {
				ERROR("failed to allocate input device");
				continue;
			}
			snprintf(dd->input_phys, sizeof(dd->input_phys),
				 "%s/input%d", dev_name(&dd->spi->dev), i);
			dd->input_dev[i]->name = pdata->nl_family;
			dd->input_dev[i]->phys = dd->input_phys;
			dd->input_dev[i]->id.bustype = BUS_SPI;
			__set_bit(EV_SYN, dd->input_dev[i]->evbit);
			__set_bit(EV_ABS, dd->input_dev[i]->evbit);
			__set_bit(INPUT_PROP_DIRECT, dd->input_dev[i]->propbit);
			if (i == (INPUT_DEVICES - 1)) {
				__set_bit(EV_KEY, dd->input_dev[i]->evbit);
				__set_bit(BTN_TOOL_RUBBER,
					  dd->input_dev[i]->keybit);
				__set_bit(KEY_POWER,
					  dd->input_dev[i]->keybit);
			}
			if (pdata->mt_type_b_enabled &&
				input_mt_init_slots(dd->input_dev[i],
						MAX_INPUT_EVENTS, 0)) {
					ERROR("Error in initialising slots\n");
					input_free_device(dd->input_dev[i]);
					dd->input_dev[i] = NULL;
					continue;
			} else {
				input_set_abs_params(dd->input_dev[i],
						ABS_MT_TRACKING_ID, 0,
						MAX_INPUT_EVENTS, 0, 0);
			}

			input_set_abs_params(dd->input_dev[i],
					     ABS_MT_POSITION_X, 0,
					     config_input_msg->x_range, 0, 0);
			input_set_abs_params(dd->input_dev[i],
					     ABS_MT_POSITION_Y, 0,
					     config_input_msg->y_range, 0, 0);
			input_set_abs_params(dd->input_dev[i],
					     ABS_MT_PRESSURE, 0, 0xFF, 0, 0);

			if (i == (INPUT_DEVICES - 1))
				input_set_abs_params(dd->input_dev[i],
						     ABS_MT_TOOL_TYPE, 0,
						     MT_TOOL_MAX, 0, 0);
			else
				input_set_abs_params(dd->input_dev[i],
						     ABS_MT_TOOL_TYPE, 0,
						     MT_TOOL_FINGER, 0, 0);

			ret = input_register_device(dd->input_dev[i]);
			if (ret < 0) {
				input_free_device(dd->input_dev[i]);
				dd->input_dev[i] = NULL;
				ERROR("failed to register input device");
			}
		}
#if defined(CONFIG_FB)
		dd->fb_notifier.notifier_call = fb_notifier_callback;
		fb_register_client(&dd->fb_notifier);
#endif
		/* create symlink */
		if (dd->parent == NULL && dd->input_dev[0] != NULL) {
			dd->parent = dd->input_dev[0]->dev.kobj.parent;
			ret = sysfs_create_link(dd->parent, &dd->spi->dev.kobj,
								MAXIM_STI_NAME);
			if (ret) {
				ERROR("sysfs_create_link error\n");
				dd->parent = NULL;
			}
		}
		return false;
	case DR_CONFIG_WATCHDOG:
		DRV_MSG_DBG("msg: %s", "DR_CONFIG_WATCHDOG");
		config_watchdog_msg = msg;
		dd->fusion_process = (pid_t)config_watchdog_msg->pid;
		return false;
	case DR_DECONFIG:
		DRV_MSG_DBG("msg: %s", "DR_DECONFIG");
		if (dd->irq_registered) {
			free_irq(dd->spi->irq, dd);
			dd->irq_registered = false;
		}
		stop_scan_canned(dd);

		if (!dd->input_no_deconfig) {
			if (dd->parent != NULL) {
				sysfs_remove_link(
					dd->input_dev[0]->dev.kobj.parent,
					MAXIM_STI_NAME);
				dd->parent = NULL;
			}
		}

		if (!dd->input_no_deconfig) {
			for (i = 0; i < INPUT_DEVICES; i++) {
				if (dd->input_dev[i] == NULL)
					continue;
				input_unregister_device(dd->input_dev[i]);
				dd->input_dev[i] = NULL;
			}
#if defined(CONFIG_FB)
			fb_unregister_client(&dd->fb_notifier);
#endif
		}
		dd->expect_resume_ack = false;
		dd->eraser_active = false;
		dd->legacy_acceleration = false;
		dd->service_irq = service_irq;
		dd->fusion_process = (pid_t)0;
		dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE;
		return false;
	case DR_INPUT:
		DRV_MSG_DBG("msg: %s", "DR_INPUT");
		input_msg = msg;
		if (input_msg->events == 0) {
			if (dd->eraser_active) {
				input_report_key(
					dd->input_dev[INPUT_DEVICES - 1],
					BTN_TOOL_RUBBER, 0);
				dd->eraser_active = false;
			}
			for (i = 0; i < INPUT_DEVICES; i++) {
				if (pdata->mt_type_b_enabled)
					release_slot_events(dd,
							active_touch[i], i);
				else
					input_mt_sync(dd->input_dev[i]);

				input_sync(dd->input_dev[i]);
			}
		} else {
			for (i = 0; i < input_msg->events; i++) {
				id = input_msg->event[i].id;
				switch (input_msg->event[i].tool_type) {
				case DR_INPUT_FINGER:
					inp = 0;
					if (pdata->mt_type_b_enabled) {
						input_mt_slot(
							dd->input_dev[inp], id);
						input_mt_report_slot_state(
							dd->input_dev[inp],
							MT_TOOL_FINGER, 1);
						active_touch[inp][id] = true;
					} else {
						input_report_abs(
							dd->input_dev[inp],
							ABS_MT_TOOL_TYPE,
							MT_TOOL_FINGER);
					}
					break;
				case DR_INPUT_STYLUS:
					inp = INPUT_DEVICES - 1;
					if (pdata->mt_type_b_enabled) {
						input_mt_slot(
							dd->input_dev[inp], id);
						input_mt_report_slot_state(
							dd->input_dev[inp],
							MT_TOOL_PEN, 1);
						active_touch[inp][id] = true;
					} else {
						input_report_abs(
							dd->input_dev[inp],
							ABS_MT_TOOL_TYPE,
							MT_TOOL_PEN);
					}
					break;
				case DR_INPUT_ERASER:
					inp = INPUT_DEVICES - 1;
					input_report_key(dd->input_dev[inp],
						BTN_TOOL_RUBBER, 1);
					dd->eraser_active = true;
					break;
				default:
					inp = 0;
					ERROR("invalid input tool type (%d)",
					      input_msg->event[i].tool_type);
					break;
				}
				if (!pdata->mt_type_b_enabled)
					input_report_abs(dd->input_dev[inp],
						ABS_MT_TRACKING_ID, id);
				input_report_abs(dd->input_dev[inp],
						 ABS_MT_POSITION_X,
						 input_msg->event[i].x);
				input_report_abs(dd->input_dev[inp],
						 ABS_MT_POSITION_Y,
						 input_msg->event[i].y);
				input_report_abs(dd->input_dev[inp],
						 ABS_MT_PRESSURE,
						 input_msg->event[i].z);
				if (!pdata->mt_type_b_enabled)
					input_mt_sync(dd->input_dev[inp]);
			}

			if (pdata->mt_type_b_enabled) {
				for (i = 0; i < INPUT_DEVICES; i++)
					release_slot_events(dd,
						active_touch[i], i);
			}

			for (i = 0; i < INPUT_DEVICES; i++)
				input_sync(dd->input_dev[i]);
		}
		return false;
	case DR_RESUME_ACK:
		DRV_MSG_DBG("msg: %s", "DR_RESUME_ACK");
		dd->expect_resume_ack = false;
		if (dd->irq_registered
		    && !dd->gesture_en
		   )
			enable_irq(dd->spi->irq);
		return false;
	case DR_LEGACY_FWDL:
		DRV_MSG_DBG("msg: %s", "DR_LEGACY_FWDL");
		ret = fw_request_load(dd);
		if (ret < 0)
			ERROR("firmware download failed (%d)", ret);
		else
			INFO("firmware download OK");
		return false;
	case DR_LEGACY_ACCELERATION:
		DRV_MSG_DBG("msg: %s", "DR_LEGACY_ACCELERATION");
		legacy_acceleration_msg = msg;
		if (legacy_acceleration_msg->enable) {
			dd->service_irq = service_irq_legacy_acceleration;
			start_legacy_acceleration(dd);
			dd->legacy_acceleration = true;
		} else {
			stop_legacy_acceleration(dd);
			dd->legacy_acceleration = false;
			dd->service_irq = service_irq;
		}
		return false;
	case DR_HANDSHAKE:
		DRV_MSG_DBG("msg: %s", "DR_HANDSHAKE");
		handshake_msg = msg;
		dd->tf_ver = handshake_msg->tf_ver;
		dd->chip_id = handshake_msg->chip_id;
		dd->drv_ver = DRIVER_VERSION_NUM;
		handshake_response = nl_alloc_attr(dd->outgoing_skb->data,
						FU_HANDSHAKE_RESPONSE,
						sizeof(*handshake_response));
		if (handshake_response == NULL)
			goto alloc_attr_failure;
		handshake_response->driver_ver = dd->drv_ver;
		handshake_response->panel_id = panel_id;
		handshake_response->driver_protocol = DRIVER_PROTOCOL;
		return true;
	case DR_CONFIG_FW:
		DRV_MSG_DBG("msg: %s", "DR_CONFIG_FW");
		config_fw_msg = msg;
		dd->fw_ver = config_fw_msg->fw_ver;
		dd->fw_protocol = config_fw_msg->fw_protocol;
		return false;
	case DR_SYSFS_ACK:
		sysfs_ack_msg = msg;
		if (sysfs_ack_msg->type == DR_SYSFS_ACK_GLOVE)
			complete(&dd->sysfs_ack_glove);
		if (sysfs_ack_msg->type == DR_SYSFS_ACK_CHARGER)
			complete(&dd->sysfs_ack_charger);
		if (sysfs_ack_msg->type == DR_SYSFS_ACK_LCD_FPS)
			complete(&dd->sysfs_ack_lcd_fps);
		return false;
	case DR_IDLE:
		DRV_MSG_DBG("msg: %s", "DR_IDLE");
		idle_msg = msg;
		dd->idle = !!(idle_msg->idle);
		return false;
	case DR_TF_STATUS:
		tf_status_msg = msg;
		dd->tf_status = tf_status_msg->tf_status;
		return false;
	default:
		ERROR("unexpected message %d", msg_id);
		return false;
	}

alloc_attr_failure:
	ERROR("failed to allocate response for msg_id %d", msg_id);
	return false;
}

static int nl_process_msg(struct dev_data *dd, struct sk_buff *skb)
{
	struct nlattr  *attr;
	bool           send_reply = false;
	int            ret = 0, ret2;

	/* process incoming message */
	attr = NL_ATTR_FIRST(skb->data);
	for (; attr < NL_ATTR_LAST(skb->data); attr = NL_ATTR_NEXT(attr)) {
		if (nl_process_driver_msg(dd, attr->nla_type,
					  NL_ATTR_VAL(attr, void)))
			send_reply = true;
	}

	/* send back reply if requested */
	if (send_reply) {
		(void)skb_put(dd->outgoing_skb,
			      NL_SIZE(dd->outgoing_skb->data));
		if (NL_SEQ(skb->data) == 0)
			ret = genlmsg_unicast(&init_net,
					      dd->outgoing_skb,
					      NETLINK_CB(skb).portid);
		else
			ret = genlmsg_multicast(&dd->nl_family,
					dd->outgoing_skb, 0,
					MC_FUSION, GFP_KERNEL);
		if (ret < 0)
			ERROR("could not reply to fusion (%d)", ret);

		/* allocate new outgoing skb */
		ret2 = nl_msg_new(dd, MC_FUSION);
		if (ret2 < 0)
			ERROR("could not allocate outgoing skb (%d)", ret2);
	}

	/* free incoming message */
	kfree_skb(skb);
	return ret;
}

static int
nl_callback_driver(struct sk_buff *skb, struct genl_info *info)
{
	struct dev_data  *dd;
	struct sk_buff   *skb2;
	unsigned long    flags;

	/* locate device structure */
	spin_lock_irqsave(&dev_lock, flags);
	list_for_each_entry(dd, &dev_list, dev_list)
		if (dd->nl_family.id == NL_TYPE(skb->data))
			break;
	spin_unlock_irqrestore(&dev_lock, flags);
	if (&dd->dev_list == &dev_list)
		return -ENODEV;
	if (!dd->nl_enabled)
		return -EAGAIN;

	/* queue incoming skb and wake up processing thread */
	skb2 = skb_clone(skb, GFP_ATOMIC);
	if (skb2 == NULL) {
		ERROR("failed to clone incoming skb");
		return -ENOMEM;
	} else {
		skb_queue_tail(&dd->incoming_skb_queue, skb2);
		wake_up_process(dd->thread);
		return 0;
	}
}

static int
nl_callback_fusion(struct sk_buff *skb, struct genl_info *info)
{
	struct dev_data  *dd;
	unsigned long    flags;

	/* locate device structure */
	spin_lock_irqsave(&dev_lock, flags);
	list_for_each_entry(dd, &dev_list, dev_list)
		if (dd->nl_family.id == NL_TYPE(skb->data))
			break;
	spin_unlock_irqrestore(&dev_lock, flags);
	if (&dd->dev_list == &dev_list)
		return -ENODEV;
	if (!dd->nl_enabled)
		return -EAGAIN;

	(void)genlmsg_multicast(&dd->nl_family,
			skb_clone(skb, GFP_ATOMIC), 0,
			MC_FUSION, GFP_ATOMIC);
	return 0;
}

/****************************************************************************\
* Interrupt processing                                                       *
\****************************************************************************/

static unsigned long interrupt_count;
static irqreturn_t irq_handler(int irq, void *context)
{
	struct dev_data  *dd = context;

	DBG("%s: interrupt #%lu", __func__, interrupt_count++);

	wake_up_process(dd->thread);
	return IRQ_HANDLED;
}

static void service_irq_legacy_acceleration(struct dev_data *dd)
{
	struct fu_async_data  *async_data;
	u16                   len, rx_len = 0, offset = 0;
	u16                   buf[255], rx_limit = 250 * sizeof(u16);
	int                   ret = 0, counter = 0;

	async_data = nl_alloc_attr(dd->outgoing_skb->data, FU_ASYNC_DATA,
				   sizeof(*async_data) + dd->irq_param[4] +
				   2 * sizeof(u16));
	if (async_data == NULL) {
		ERROR("can't add data to async IRQ buffer");
		return;
	}
	async_data->length = dd->irq_param[4] + 2 * sizeof(u16);
	len = async_data->length;
	async_data->address = 0;

	while (len > 0) {
		rx_len = (len > rx_limit) ? rx_limit : len;
		ret = spi_read_123(dd, 0x0000, (u8 *)&buf,
					rx_len + 4 * sizeof(u16), false);
		if (ret < 0)
			break;

		if (buf[3] == 0xBABE) {
			dd->legacy_acceleration = false;
			dd->service_irq = service_irq;
			nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id,
				    dd->nl_seq - 1, MC_FUSION);
			return;
		}

		if (rx_limit == rx_len)
			usleep_range(200, 300);

		if (buf[0] == 0x6060) {
			ERROR("data not ready");
			start_legacy_acceleration_canned(dd);
			ret = -EBUSY;
			break;
		} else if (buf[0] == 0x8070) {
			if (buf[1] == dd->irq_param[1] ||
					buf[1] == dd->irq_param[2])
				async_data->address = buf[1];

			if (async_data->address +
					offset / sizeof(u16) != buf[1]) {
				ERROR("sequence number incorrect %04X", buf[1]);
				start_legacy_acceleration_canned(dd);
				ret = -EBUSY;
				break;
			}
		}
		counter++;
		memcpy(async_data->data + offset, buf + 4, rx_len);
		offset += rx_len;
		len -= rx_len;
	}
	async_data->status = *(buf + rx_len / sizeof(u16) + 2);

	if (ret < 0) {
		ERROR("can't read IRQ buffer (%d)", ret);
		nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id,
			    dd->nl_seq - 1, MC_FUSION);
	} else {
		(void)skb_put(dd->outgoing_skb,
			      NL_SIZE(dd->outgoing_skb->data));
		ret = genlmsg_multicast(&dd->nl_family, dd->outgoing_skb, 0,
				MC_FUSION, GFP_KERNEL);
		if (ret < 0) {
			ERROR("can't send IRQ buffer %d", ret);
			msleep(300);
			if (++dd->send_fail_count >= 10 &&
			    dd->fusion_process != (pid_t)0) {
				(void)kill_pid(
					find_get_pid(dd->fusion_process),
					SIGKILL, 1);
				wake_up_process(dd->thread);
			}
		} else {
			dd->send_fail_count = 0;
		}
		ret = nl_msg_new(dd, MC_FUSION);
		if (ret < 0)
			ERROR("could not allocate outgoing skb (%d)", ret);
	}
}

static void service_irq(struct dev_data *dd)
{
	struct fu_async_data  *async_data;
	u16                   status, test, xbuf, value;
	u16                   address[2] = { 0 };
	u16                   clear[2] = { 0 };
	bool                  read_buf[2] = {true, false};
	int                   ret, ret2;

	DBG("%s", __func__);
	ret = dd->chip.read(dd, dd->irq_param[0], (u8 *)&status,
			    sizeof(status));
	if (ret < 0) {
		ERROR("can't read IRQ status (%d)", ret);
		return;
	}
	DBG("%s: status = 0x%04X", __func__, status);

	if (dd->gesture_reset) {
		read_buf[0] = false;
		clear[0] = dd->irq_param[10];
		status = dd->irq_param[10];
		dd->gesture_reset = false;
	} else
	if (status & dd->irq_param[10]) {
		read_buf[0] = false;
		clear[0] = dd->irq_param[10];
	} else if (status & dd->irq_param[18]) {
		(void)dd->chip.read(dd, dd->irq_param[22], (u8 *)&value,
				    sizeof(value));
		if (value != dd->irq_param[23]) {
			ERROR("unrecognized value [%#x] => %#x",
					dd->irq_param[22], value);
			clear[0] = dd->irq_param[18];
			ret2 = dd->chip.write(dd, dd->irq_param[0],
					      (u8 *)clear, sizeof(clear));
			return;
		}

		test = status & (dd->irq_param[6] | dd->irq_param[7]);

		if (test == 0)
			return;
		else if (test == (dd->irq_param[6] | dd->irq_param[7]))
			xbuf = ((status & dd->irq_param[5]) == 0) ? 0 : 1;
		else if (test == dd->irq_param[6])
			xbuf = 0;
		else if (test == dd->irq_param[7])
			xbuf = 1;
		else {
			ERROR("unexpected IRQ handler case 0x%04X ", status);
			return;
		}

		read_buf[1] = true;
		address[1] = xbuf ? dd->irq_param[2] : dd->irq_param[1];
		address[0] = dd->irq_param[3];
		clear[0] = xbuf ? dd->irq_param[7] : dd->irq_param[6];
		clear[0] |= dd->irq_param[8];
		clear[0] |= dd->irq_param[18];

		value = dd->irq_param[17];
		(void)dd->chip.write(dd, dd->irq_param[16], (u8 *)&value,
				     sizeof(value));
	} else if (status & dd->irq_param[9]) {
		test = status & (dd->irq_param[6] | dd->irq_param[7]);

		if (test == (dd->irq_param[6] | dd->irq_param[7]))
			xbuf = ((status & dd->irq_param[5]) != 0) ? 0 : 1;
		else if (test == dd->irq_param[6])
			xbuf = 0;
		else if (test == dd->irq_param[7])
			xbuf = 1;
		else {
			ERROR("unexpected IRQ handler case");
			return;
		}
		read_buf[1] = true;
		address[1] = xbuf ? dd->irq_param[2] : dd->irq_param[1];

		address[0] = dd->irq_param[3];
		clear[0] = dd->irq_param[6] | dd->irq_param[7] |
			dd->irq_param[8] | dd->irq_param[9];
		value = dd->irq_param[13];
		(void)dd->chip.write(dd, dd->irq_param[12], (u8 *)&value,
				     sizeof(value));
	} else {
		test = status & (dd->irq_param[6] | dd->irq_param[7]);

		if (test == 0)
			return;
		else if (test == (dd->irq_param[6] | dd->irq_param[7]))
			xbuf = ((status & dd->irq_param[5]) == 0) ? 0 : 1;
		else if (test == dd->irq_param[6])
			xbuf = 0;
		else if (test == dd->irq_param[7])
			xbuf = 1;
		else {
			ERROR("unexpected IRQ handler case 0x%04X ",status);
			return;
		}

		address[0] = xbuf ? dd->irq_param[2] : dd->irq_param[1];
		clear[0] = xbuf ? dd->irq_param[7] : dd->irq_param[6];
		clear[0] |= dd->irq_param[8];
	}

	async_data = nl_alloc_attr(dd->outgoing_skb->data, FU_ASYNC_DATA,
				   sizeof(*async_data) + dd->irq_param[4]);
	if (async_data == NULL) {
		ERROR("can't add data to async IRQ buffer 1");
		return;
	}

	async_data->status = status;
	if (read_buf[0]) {
		async_data->address = address[0];
		async_data->length = dd->irq_param[4];
		ret = dd->chip.read(dd, address[0], async_data->data,
				    dd->irq_param[4]);
	}

	if (read_buf[1] && ret == 0) {
		async_data = nl_alloc_attr(dd->outgoing_skb->data,
					   FU_ASYNC_DATA,
					   sizeof(*async_data) +
						dd->irq_param[4]);
		if (async_data == NULL) {
			ERROR("can't add data to async IRQ buffer 2");
			nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id,
				    dd->nl_seq - 1, MC_FUSION);
			return;
		}
		async_data->address = address[1];
		async_data->length = dd->irq_param[4];
		async_data->status = status;
		ret = dd->chip.read(dd, address[1], async_data->data,
				    dd->irq_param[4]);
	}

	ret2 = dd->chip.write(dd, dd->irq_param[0], (u8 *)clear,
			     sizeof(clear));
	if (ret2 < 0)
		ERROR("can't clear IRQ status (%d)", ret2);

	if (ret < 0) {
		ERROR("can't read IRQ buffer (%d)", ret);
		nl_msg_init(dd->outgoing_skb->data, dd->nl_family.id,
			    dd->nl_seq - 1, MC_FUSION);
	} else {
		DBG("%s: sending buffer", __func__);
		(void)skb_put(dd->outgoing_skb,
			      NL_SIZE(dd->outgoing_skb->data));
		ret = genlmsg_multicast(&dd->nl_family, dd->outgoing_skb, 0,
				MC_FUSION, GFP_KERNEL);
		if (ret < 0) {
			ERROR("can't send IRQ buffer %d", ret);
			msleep(300);
			if (read_buf[0] == false ||
			    (++dd->send_fail_count >= 10 &&
			     dd->fusion_process != (pid_t)0)) {
				(void)kill_pid(
					find_get_pid(dd->fusion_process),
					SIGKILL, 1);
				wake_up_process(dd->thread);
			}
		} else {
			dd->send_fail_count = 0;
		}
		ret = nl_msg_new(dd, MC_FUSION);
		if (ret < 0)
			ERROR("could not allocate outgoing skb (%d)", ret);
	}
}

/****************************************************************************\
* Processing thread                                                          *
\****************************************************************************/

static int processing_thread(void *arg)
{
	struct dev_data         *dd = arg;
	struct maxim_sti_pdata  *pdata = dd->spi->dev.platform_data;
	struct sk_buff          *skb;
	char                    *argv[] = { pdata->touch_fusion, "daemon",
					    pdata->nl_family,
					    dd->config_file, NULL };
	int                     ret = 0, ret2 = 0;
	bool                    fusion_dead;

	DRV_PT_DBG("%s started", __FUNCTION__);
	sched_setscheduler(current, SCHED_FIFO, &dd->thread_sched);

	while (!kthread_should_stop()) {
		set_current_state(TASK_INTERRUPTIBLE);
		DRV_PT_DBG("Begin");

		/* ensure that we have outgoing skb */
		if (dd->outgoing_skb == NULL)
			if (nl_msg_new(dd, MC_FUSION) < 0) {
				schedule();
				continue;
			}

		/* priority 1: start up fusion process */
		if (dd->fusion_process != (pid_t)0 && get_pid_task(
					find_get_pid(dd->fusion_process),
					PIDTYPE_PID) == NULL) {
			DRV_PT_DBG("Re-Starting TF");
			stop_scan_canned(dd);
			dd->start_fusion = true;
			dd->fusion_process = (pid_t)0;
			dd->input_no_deconfig = true;
		}
		if (dd->start_fusion) {
			DRV_PT_DBG("Spawning TF");
			do {
				ret = call_usermodehelper(argv[0], argv, NULL,
							  UMH_WAIT_EXEC);
				if (ret != 0)
					msleep(100);
			} while (ret != 0 && !kthread_should_stop());

			if (ret == 0) {
				/* power-up and reset-high */
				ret = regulator_control(dd, true);
				if (ret < 0)
					ERROR("failed to enable regulators");

				usleep_range(300, 400);
				pdata->reset(pdata, 1);

				dd->start_fusion = false;
				dd->tf_status |= TF_STATUS_BUSY;
			}
		}
		if (kthread_should_stop())
			break;

		/* priority 2: process pending Netlink messages */
		while ((skb = skb_dequeue(&dd->incoming_skb_queue)) != NULL) {
			if (kthread_should_stop())
				break;
			if (nl_process_msg(dd, skb) < 0)
				skb_queue_purge(&dd->incoming_skb_queue);
		}
		if (kthread_should_stop())
			break;

		/* priority 3: suspend/resume */
		if (dd->suspend_in_progress && !(dd->tf_status & TF_STATUS_BUSY)) {
			if (dd->irq_registered) {
				disable_irq(dd->spi->irq);
				if (dd->gesture_en) {
					enable_gesture(dd);
					enable_irq(dd->spi->irq);
				} else
				if (!dd->idle)
					stop_scan_canned(dd);
				else
					stop_idle_scan(dd);
			}

			dd->suspended = true;
			complete(&dd->suspend_resume);
			dd->expect_resume_ack = true;
			dd->gesture_reset = false;
			while (!dd->resume_in_progress) {
				/* the line below is a MUST */
				set_current_state(TASK_INTERRUPTIBLE);
				if (kthread_should_stop())
					break;
				if (dd->gesture_en && dd->irq_registered &&
				    pdata->irq(pdata) == 0)
					gesture_wake_detect(dd);
				schedule();
			}
			if (dd->irq_registered) {
				if (dd->gesture_en)
					finish_gesture(dd);
				else
				if (!dd->idle)
					start_scan_canned(dd);
			}
			dd->suspended = false;
			dd->resume_in_progress = false;
			dd->suspend_in_progress = false;
			complete(&dd->suspend_resume);

			fusion_dead = false;
			dd->send_fail_count = 0;
			do {
				if (dd->fusion_process != (pid_t)0 &&
				    get_pid_task(find_get_pid(
							dd->fusion_process),
						 PIDTYPE_PID) == NULL) {
					fusion_dead = true;
					break;
				}
				ret = nl_add_attr(dd->outgoing_skb->data,
						  FU_RESUME, NULL, 0);
				if (ret < 0) {
					ERROR("can't add data to resume " \
					      "buffer");
					nl_msg_init(dd->outgoing_skb->data,
						    dd->nl_family.id,
						    dd->nl_seq - 1, MC_FUSION);
					msleep(100);
					continue;
				}
				(void)skb_put(dd->outgoing_skb,
					      NL_SIZE(dd->outgoing_skb->data));
				ret = genlmsg_multicast(&dd->nl_family,
						dd->outgoing_skb, 0, MC_FUSION,
						GFP_KERNEL);
				if (ret < 0) {
					ERROR("can't send resume message %d",
					      ret);
					msleep(100);
					if (++dd->send_fail_count >= 10) {
						fusion_dead = true;
						ret = 0;
					}
				}
				ret2 = nl_msg_new(dd, MC_FUSION);
				if (ret2 < 0)
					ERROR("could not allocate outgoing " \
					      "skb (%d)", ret2);
			} while (ret != 0 && !kthread_should_stop());

			if (!ret && dd->send_fail_count>= 10 &&
			    dd->fusion_process != (pid_t)0)
			       (void)kill_pid(find_get_pid(dd->fusion_process),
					      SIGKILL, 1);

			dd->send_fail_count = 0;
			if (fusion_dead)
				continue;
		}
		if (kthread_should_stop())
			break;

		/* priority 4: update /sys/device information */
		if (dd->sysfs_update_type != DR_SYSFS_UPDATE_NONE)
			if (!dd->expect_resume_ack) {
				if (send_sysfs_info(dd) < 0)
					ERROR(
					      "Can not send sysfs update to touch fusion"
					     );
				dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE;
			}

		/* priority 5: service interrupt */
		if (dd->irq_registered && !dd->expect_resume_ack &&
		    ((pdata->irq(pdata) == 0)
		     || dd->gesture_reset
		    ))
			dd->service_irq(dd);
		if (dd->irq_registered && !dd->expect_resume_ack &&
		    pdata->irq(pdata) == 0)
			continue;

		DRV_PT_DBG("End");
		/* nothing more to do; sleep */
		schedule();
	}

	return 0;
}

/****************************************************************************\
* SYSFS interface                                                            *
\****************************************************************************/

static ssize_t chip_id_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->chip_id);
}

static ssize_t fw_ver_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{

	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->fw_ver);
}

static ssize_t driver_ver_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{

	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->drv_ver);
}

static ssize_t tf_ver_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "0x%04X\n", dd->tf_ver);
}

static ssize_t panel_id_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "0x%04X\n", panel_id);
}

static ssize_t glove_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "%u\n", dd->glove_enabled);
}

static ssize_t glove_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	u32 temp;
	unsigned long           time_left;

	mutex_lock(&dd->sysfs_update_mutex);

	if (sscanf(buf, "%u", &temp) != 1) {
		ERROR("Invalid (%s)", buf);
		return -EINVAL;
	}
	dd->glove_enabled = !!temp;
	set_bit(DR_SYSFS_UPDATE_BIT_GLOVE, &(dd->sysfs_update_type));
	wake_up_process(dd->thread);
	time_left = wait_for_completion_timeout(&dd->sysfs_ack_glove, 3*HZ);

	mutex_unlock(&dd->sysfs_update_mutex);

	if (time_left > 0)
		return strnlen(buf, PAGE_SIZE);
	else
		return -EAGAIN;
}

static ssize_t gesture_wakeup_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "%u\n", dd->gesture_en);
}

static ssize_t gesture_wakeup_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	u32 temp;

	if (sscanf(buf, "%u", &temp) != 1) {
		ERROR("Invalid (%s)", buf);
		return -EINVAL;
	}
	dd->gesture_en = temp;

	return strnlen(buf, PAGE_SIZE);
}

static ssize_t charger_mode_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "%u\n", dd->charger_mode_en);
}

static ssize_t charger_mode_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	u32 temp;
	unsigned long           time_left;

	mutex_lock(&dd->sysfs_update_mutex);

	if (sscanf(buf, "%u", &temp) != 1) {
		ERROR("Invalid (%s)", buf);
		return -EINVAL;
	}
	if (temp > DR_WIRELESS_CHARGER) {
		ERROR("Charger Mode Value Out of Range");
		return -EINVAL;
	}
	dd->charger_mode_en = temp;
	set_bit(DR_SYSFS_UPDATE_BIT_CHARGER, &(dd->sysfs_update_type));
	wake_up_process(dd->thread);
	time_left = wait_for_completion_timeout(&dd->sysfs_ack_charger, 3*HZ);

	mutex_unlock(&dd->sysfs_update_mutex);

	if (time_left > 0)
		return strnlen(buf, PAGE_SIZE);
	else
		return -EAGAIN;
}

static ssize_t info_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "chip id: 0x%04X driver ver: 0x%04X "
					"fw ver: 0x%04X panel id: 0x%04X "
					"tf ver: 0x%04X\n",
					dd->chip_id, dd->drv_ver, dd->fw_ver,
					panel_id, dd->tf_ver);
}

static ssize_t screen_status_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	u32 temp;

	if (sscanf(buf, "%u", &temp) != 1) {
		ERROR("Invalid (%s)", buf);
		return -EINVAL;
	}
	if (temp)
		resume(dev);
	else
		suspend(dev);

	return strnlen(buf, PAGE_SIZE);
}

static ssize_t tf_status_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "0x%08X", dd->tf_status);
}

static ssize_t default_loaded_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "%d",
			(dd->tf_status & TF_STATUS_DEFAULT_LOADED));
}

static ssize_t lcd_fps_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	return snprintf(buf, PAGE_SIZE, "%u\n", dd->lcd_fps);
}

static ssize_t lcd_fps_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct spi_device       *spi = to_spi_device(dev);
	struct dev_data         *dd = spi_get_drvdata(spi);
	u32 temp;
	unsigned long           time_left;

	mutex_lock(&dd->sysfs_update_mutex);

	if (sscanf(buf, "%u", &temp) != 1) {
		ERROR("Invalid (%s)", buf);
		return -EINVAL;
	}

	dd->lcd_fps = temp;
	set_bit(DR_SYSFS_UPDATE_BIT_LCD_FPS, &(dd->sysfs_update_type));
	wake_up_process(dd->thread);
	time_left = wait_for_completion_timeout(&dd->sysfs_ack_lcd_fps, 3*HZ);

	mutex_unlock(&dd->sysfs_update_mutex);

	if (time_left > 0)
		return strnlen(buf, PAGE_SIZE);
	else
		return -EAGAIN;
}

static struct device_attribute dev_attrs[] = {
	__ATTR(fw_ver, S_IRUGO, fw_ver_show, NULL),
	__ATTR(chip_id, S_IRUGO, chip_id_show, NULL),
	__ATTR(tf_ver, S_IRUGO, tf_ver_show, NULL),
	__ATTR(driver_ver, S_IRUGO, driver_ver_show, NULL),
	__ATTR(panel_id, S_IRUGO, panel_id_show, NULL),
	__ATTR(glove_en, S_IRUGO | S_IWUSR, glove_show, glove_store),
	__ATTR(gesture_wakeup, S_IRUGO | S_IWUSR, gesture_wakeup_show, gesture_wakeup_store),
	__ATTR(charger_mode, S_IRUGO | S_IWUSR, charger_mode_show, charger_mode_store),
	__ATTR(info, S_IRUGO, info_show, NULL),
	__ATTR(screen_status, S_IWUSR, NULL, screen_status_store),
	__ATTR(tf_status, S_IRUGO, tf_status_show, NULL),
	__ATTR(default_loaded, S_IRUGO, default_loaded_show, NULL),
	__ATTR(lcd_fps, S_IRUGO | S_IWUSR, lcd_fps_show, lcd_fps_store),
};

static int create_sysfs_entries(struct dev_data *dd)
{
	int i, ret = 0;

	for (i = 0; i < ARRAY_SIZE(dev_attrs); i++) {
		ret = device_create_file(&dd->spi->dev, &dev_attrs[i]);
		if (ret) {
			for (; i >= 0; --i) {
				device_remove_file(&dd->spi->dev,
							&dev_attrs[i]);
				dd->sysfs_created--;
			}
			break;
		}
		dd->sysfs_created++;
	}
	return ret;
}

static void remove_sysfs_entries(struct dev_data *dd)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(dev_attrs); i++)
		if (dd->sysfs_created && dd->sysfs_created--)
			device_remove_file(&dd->spi->dev, &dev_attrs[i]);
}

static int send_sysfs_info(struct dev_data *dd)
{
	struct fu_sysfs_info       *sysfs_info;
	int                        ret, ret2;

	sysfs_info = nl_alloc_attr(dd->outgoing_skb->data,
					FU_SYSFS_INFO,
					sizeof(*sysfs_info));
	if (sysfs_info == NULL) {
		ERROR("Can't allocate sysfs_info");
		return -EAGAIN;
	}
	sysfs_info->type = dd->sysfs_update_type;

	sysfs_info->glove_value = dd->glove_enabled;
	sysfs_info->charger_value = dd->charger_mode_en;
	sysfs_info->lcd_fps_value = dd->lcd_fps;

	(void)skb_put(dd->outgoing_skb,
			      NL_SIZE(dd->outgoing_skb->data));
	ret = genlmsg_multicast(&dd->nl_family,
			dd->outgoing_skb, 0,
			MC_FUSION, GFP_KERNEL);
	if (ret < 0)
		ERROR("could not reply to fusion (%d)", ret);

	/* allocate new outgoing skb */
	ret2 = nl_msg_new(dd, MC_FUSION);
	if (ret2 < 0)
		ERROR("could not allocate outgoing skb (%d)", ret2);

	return 0;
}

/****************************************************************************\
* Driver initialization                                                      *
\****************************************************************************/

static int probe(struct spi_device *spi)
{
	struct maxim_sti_pdata  *pdata = spi->dev.platform_data;
	struct dev_data         *dd;
	unsigned long           flags;
	int                     ret, i;
	void                    *ptr;

#ifdef CONFIG_OF
	if (pdata == NULL && spi->dev.of_node) {
		pdata = devm_kzalloc(&spi->dev,
			sizeof(struct maxim_sti_pdata), GFP_KERNEL);
		if (!pdata) {
			dev_err(&spi->dev, "failed to allocate memory\n");
			return -ENOMEM;
		}

		ret = maxim_parse_dt(&spi->dev, pdata);
		if (ret)
			goto of_parse_failure;
		spi->dev.platform_data = pdata;
	}
#endif

	pdata->init = maxim_sti_gpio_init;
	pdata->reset = maxim_sti_gpio_reset;
	pdata->irq = maxim_sti_gpio_irq;

	pdata->nl_mc_groups = DEF_NL_MC_GROUPS;
	pdata->chip_access_method = DEF_CHIP_ACC_METHOD;
	pdata->tx_buf_size = BUF_SIZE;
	pdata->rx_buf_size = BUF_SIZE;
	pdata->nl_family = TOUCH_FUSION;

	dev_dbg(&spi->dev, "maxim_sti,reset-gpio: %d", pdata->gpio_reset);
	dev_dbg(&spi->dev, "maxim_sti,irq-gpio: %d", pdata->gpio_irq);
	dev_dbg(&spi->dev, "maxim_sti,nl_mc_groups: %d", pdata->nl_mc_groups);
	dev_dbg(&spi->dev, "maxim_sti,chip_access_method: %d", pdata->chip_access_method);
	dev_dbg(&spi->dev, "maxim_sti,default_reset_state: %d", pdata->default_reset_state);
	dev_dbg(&spi->dev, "maxim_sti,tx_buf_size: %d", pdata->tx_buf_size);
	dev_dbg(&spi->dev, "maxim_sti,rx_buf_size: %d", pdata->rx_buf_size);
	dev_dbg(&spi->dev, "maxim_sti,touch_fusion: %s", pdata->touch_fusion);
	dev_dbg(&spi->dev, "maxim_sti,config_file: %s", pdata->config_file);
	dev_dbg(&spi->dev, "maxim_sti,nl_family: %s", pdata->nl_family);
	dev_dbg(&spi->dev, "maxim_sti,fw_name: %s", pdata->fw_name);

	/* validate platform data */
	if (pdata == NULL || pdata->init == NULL || pdata->reset == NULL ||
		pdata->irq == NULL || pdata->touch_fusion == NULL ||
		pdata->config_file == NULL || pdata->nl_family == NULL ||
		GENL_CHK(pdata->nl_family) ||
		pdata->nl_mc_groups < MC_GROUPS ||
		pdata->chip_access_method == 0 ||
		pdata->chip_access_method > ARRAY_SIZE(chip_access_methods) ||
		pdata->default_reset_state > 1 ||
		pdata->tx_buf_size == 0 ||
		pdata->rx_buf_size == 0) {
		dev_err(&spi->dev, "invalid platform data!");
		ret = -EINVAL;
		goto of_parse_failure;
	}

	/* device context: allocate structure */
	dd = kzalloc(sizeof(*dd) +
		     sizeof(*dd->nl_ops) * pdata->nl_mc_groups +
		     sizeof(*dd->nl_mc_groups) * pdata->nl_mc_groups,
		     GFP_KERNEL);
	if (dd == NULL) {
		dev_err(&spi->dev, "cannot allocate memory!");
		ret = -ENOMEM;
		goto of_parse_failure;
	}

	/* device context: set up dynamic allocation pointers */
	ptr = (void *)dd + sizeof(*dd);
	dd->nl_ops = ptr;
	ptr += sizeof(*dd->nl_ops) * pdata->nl_mc_groups;
	dd->nl_mc_groups = ptr;

	/* device context: initialize structure members */
	spi_set_drvdata(spi, dd);
	spi->bits_per_word = 16;
	spi_setup(spi);
	dd->spi = spi;
	dd->nl_seq = 1;
	init_completion(&dd->suspend_resume);

	/* allocate DMA memory */
	dd->tx_buf = kzalloc(pdata->tx_buf_size + pdata->rx_buf_size,
				GFP_KERNEL | GFP_DMA);
	if (dd->tx_buf == NULL) {
		dev_err(&spi->dev, "cannot allocate DMA accesible memory");
		ret = -ENOMEM;
		goto buf_alloc_failure;
	}
	dd->rx_buf = dd->tx_buf + pdata->tx_buf_size;
	memset(dd->tx_buf, 0xFF, pdata->tx_buf_size);
	(void)set_chip_access_method(dd, pdata->chip_access_method);

	/* initialize regulators */
	regulator_init(dd);

	/* initialize platform */

	/* Get pinctrl if target uses pinctrl */
	dd->ts_pinctrl = devm_pinctrl_get(&(spi->dev));
	if (IS_ERR_OR_NULL(dd->ts_pinctrl)) {
		ret = PTR_ERR(dd->ts_pinctrl);
		pr_debug("Target does not use pinctrl %d\n", ret);
	}

	dd->pinctrl_state_active
		= pinctrl_lookup_state(dd->ts_pinctrl, "pmx_ts_active");
	if (IS_ERR_OR_NULL(dd->pinctrl_state_active)) {
		ret = PTR_ERR(dd->pinctrl_state_active);
		pr_debug("Can not lookup active pinstate %d\n",	ret);
	}

	dd->pinctrl_state_suspended
		= pinctrl_lookup_state(dd->ts_pinctrl, "pmx_ts_suspend");
	if (IS_ERR_OR_NULL(dd->pinctrl_state_suspended)) {
		ret = PTR_ERR(dd->pinctrl_state_suspended);
		pr_debug("Can not lookup active pinstate %d\n",	ret);
	}
	ret = pinctrl_select_state(dd->ts_pinctrl,
			dd->pinctrl_state_active);
	if (ret < 0)
		pr_debug("pinctrl set active fail\n");


	ret = pdata->init(pdata, true);
	if (ret < 0)
		goto pinit_failure;

	/* Netlink: initialize incoming skb queue */
	skb_queue_head_init(&dd->incoming_skb_queue);

	/* Netlink: register GENL family */
	dd->nl_family.id      = GENL_ID_GENERATE;
	dd->nl_family.version = NL_FAMILY_VERSION;
	GENL_COPY(dd->nl_family.name, pdata->nl_family);

	/* Netlink: register family ops */
	for (i = 0; i < MC_GROUPS; i++) {
		dd->nl_ops[i].cmd = i;
		dd->nl_ops[i].doit = nl_callback_noop;
	}
	dd->nl_ops[MC_DRIVER].doit = nl_callback_driver;
	dd->nl_ops[MC_FUSION].doit = nl_callback_fusion;
	dd->nl_ops[MC_EVENT_BROADCAST].doit = nl_callback_noop;

	/* Netlink: register family multicast groups */
	GENL_COPY(dd->nl_mc_groups[MC_DRIVER].name, MC_DRIVER_NAME);
	GENL_COPY(dd->nl_mc_groups[MC_FUSION].name, MC_FUSION_NAME);
	GENL_COPY(dd->nl_mc_groups[MC_EVENT_BROADCAST].name,
				MC_EVENT_BROADCAST_NAME);
	ret = _genl_register_family_with_ops_grps(&dd->nl_family,
			dd->nl_ops,
			MC_GROUPS,
			dd->nl_mc_groups,
			MC_GROUPS);
	if (ret < 0) {
		dev_err(&spi->dev, "error registering nl_mc_group");
		goto nl_family_failure;
	}
	dd->nl_mc_group_count = MC_GROUPS;

	/* Netlink: pre-allocate outgoing skb */
	ret = nl_msg_new(dd, MC_FUSION);
	if (ret < 0) {
		dev_err(&spi->dev, "error alloc msg");
		goto nl_failure;
	}

	/* start processing thread */
	dd->thread_sched.sched_priority = MAX_USER_RT_PRIO / 2;
	dd->thread = kthread_run(processing_thread, dd, pdata->nl_family);
	if (IS_ERR(dd->thread)) {
		dev_err(&spi->dev, "error creating kthread!");
		ret = PTR_ERR(dd->thread);
		goto kthread_failure;
	}

	/* Netlink: ready to start processing incoming messages */
	dd->nl_enabled = true;

	/* No sysfs update initially */
	dd->sysfs_update_type = DR_SYSFS_UPDATE_NONE;
	init_completion(&dd->sysfs_ack_glove);
	init_completion(&dd->sysfs_ack_charger);
	init_completion(&dd->sysfs_ack_lcd_fps);
	mutex_init(&dd->sysfs_update_mutex);

	/* add us to the devices list */
	spin_lock_irqsave(&dev_lock, flags);
	list_add_tail(&dd->dev_list, &dev_list);
	spin_unlock_irqrestore(&dev_lock, flags);

	ret = create_sysfs_entries(dd);
	if (ret) {
		dev_err(&spi->dev, "failed to create sysfs file");
		goto sysfs_failure;
	}
	snprintf(dd->config_file, CFG_FILE_NAME_MAX, pdata->config_file, panel_id);
	dev_dbg(&spi->dev, "configuration file: %s", dd->config_file);

	/* start up Touch Fusion */
	dd->start_fusion = true;
	wake_up_process(dd->thread);
	dev_info(&spi->dev, "%s: driver loaded; version %s; release date %s",
			dd->nl_family.name, DRIVER_VERSION, DRIVER_RELEASE);

	return 0;

sysfs_failure:
	dd->nl_enabled = false;
	spin_lock_irqsave(&dev_lock, flags);
	list_del(&dd->dev_list);
	spin_unlock_irqrestore(&dev_lock, flags);
	(void)kthread_stop(dd->thread);
kthread_failure:
	if (dd->outgoing_skb)
		kfree_skb(dd->outgoing_skb);
nl_failure:
	genl_unregister_family(&dd->nl_family);
nl_family_failure:
	pdata->init(pdata, false);
pinit_failure:
	regulator_uninit(dd);
	kfree(dd->tx_buf);
buf_alloc_failure:
	spi_set_drvdata(spi, NULL);
	kfree(dd);
of_parse_failure:
	spi->dev.platform_data = NULL;
	devm_kfree(&spi->dev, pdata);
	return ret;
}

static int remove(struct spi_device *spi)
{
	struct maxim_sti_pdata  *pdata = spi->dev.platform_data;
	struct dev_data         *dd = spi_get_drvdata(spi);
	unsigned long           flags;
	u8                      i;

	dev_dbg(&spi->dev, "remove");

	/* BEWARE: tear-down sequence below is carefully staged:            */
	/* 1) first the feeder of Netlink messages to the processing thread */
	/*    is turned off                                                 */
	/* 2) then the thread itself is shut down                           */
	/* 3) then Netlink family is torn down since no one would be using  */
	/*    it at this point                                              */
	/* 4) above step (3) insures that all Netlink senders are           */
	/*    definitely gone and it is safe to free up outgoing skb buffer */
	/*    and incoming skb queue                                        */
	dev_dbg(&spi->dev, "stopping thread..");
	dd->nl_enabled = false;
	(void)kthread_stop(dd->thread);
	dev_dbg(&spi->dev, "kthread stopped.");
	genl_unregister_family(&dd->nl_family);
	if (dd->outgoing_skb)
		kfree_skb(dd->outgoing_skb);
	skb_queue_purge(&dd->incoming_skb_queue);

	if (dd->fusion_process != (pid_t)0)
		(void)kill_pid(find_get_pid(dd->fusion_process), SIGKILL, 1);

	dev_dbg(&spi->dev, "removing sysfs entries..");
	if (dd->parent != NULL)
		sysfs_remove_link(dd->input_dev[0]->dev.kobj.parent,
						MAXIM_STI_NAME);
	remove_sysfs_entries(dd);

	dev_dbg(&spi->dev, "unregister input devices..");
	for (i = 0; i < INPUT_DEVICES; i++)
		if (dd->input_dev[i])
			input_unregister_device(dd->input_dev[i]);
#if defined(CONFIG_FB)
	fb_unregister_client(&dd->fb_notifier);
#endif

	if (dd->irq_registered) {
		dev_dbg(&spi->dev, "disabling interrupt");
		disable_irq(dd->spi->irq);
		dev_dbg(&spi->dev, "stopping scan");
		stop_scan_canned(dd);
		free_irq(dd->spi->irq, dd);
	}

	spin_lock_irqsave(&dev_lock, flags);
	list_del(&dd->dev_list);
	spin_unlock_irqrestore(&dev_lock, flags);

	pdata->reset(pdata, 0);
	usleep_range(100, 120);
	regulator_control(dd, false);
	pdata->init(pdata, false);
	regulator_uninit(dd);

	dev_dbg(&spi->dev, "detaching from spi..");
	spi_set_drvdata(spi, NULL);
	kzfree(dd);
	spi->dev.platform_data = NULL;
	devm_kfree(&spi->dev, pdata);

	dev_info(&spi->dev, "driver unloaded");
	return 0;
}

static void shutdown(struct spi_device *spi)
{
	struct maxim_sti_pdata  *pdata = spi->dev.platform_data;
	struct dev_data         *dd = spi_get_drvdata(spi);

	if (dd->parent != NULL)
		sysfs_remove_link(dd->input_dev[0]->dev.kobj.parent,
						MAXIM_STI_NAME);
	remove_sysfs_entries(dd);

	pdata->reset(pdata, 0);
	usleep_range(100, 120);
	regulator_control(dd, false);
}

/****************************************************************************\
* Module initialization                                                      *
\****************************************************************************/

static const struct spi_device_id id[] = {
	{ MAXIM_STI_NAME, 0 },
	{ }
};

MODULE_DEVICE_TABLE(spi, id);

#ifdef CONFIG_OF
static struct of_device_id maxim_match_table[] = {
	{ .compatible = "maxim,maxim_sti",},
	{ },
};
#endif

static struct spi_driver driver = {
	.probe          = probe,
	.remove         = remove,
	.shutdown       = shutdown,
	.id_table       = id,
	.driver = {
		.name   = MAXIM_STI_NAME,
#ifdef CONFIG_OF
		.of_match_table = maxim_match_table,
#endif
		.owner  = THIS_MODULE,
#ifdef CONFIG_PM_SLEEP
		.pm     = &pm_ops,
#endif
	},
};

static int maxim_sti_init(void)
{
	INIT_LIST_HEAD(&dev_list);
	spin_lock_init(&dev_lock);
	return spi_register_driver(&driver);
}

static void maxim_sti_exit(void)
{
	spi_unregister_driver(&driver);
}

module_param(panel_id, ushort, S_IRUGO);
MODULE_PARM_DESC(panel_id, "Touch Panel ID Configuration");

module_init(maxim_sti_init);
module_exit(maxim_sti_exit);

MODULE_AUTHOR("Maxim Integrated Products, Inc.");
MODULE_DESCRIPTION("Maxim SmartTouch Imager Touchscreen Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRIVER_VERSION);
