/*
 * pt_core.c
 * Parade TrueTouch(TM) Standard Product Core Module.
 * For use with Parade touchscreen controllers.
 * Supported parts include:
 * TMA5XX
 * TMA448
 * TMA445A
 * TT21XXX
 * TT31XXX
 * TT4XXXX
 * TT7XXX
 * TC3XXX
 *
 * Copyright (C) 2015-2020 Parade Technologies
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2, and only 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.
 *
 * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
 */

#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/kthread.h>
#include <linux/suspend.h>
#include <glink_interface.h>
#include <linux/remoteproc/qcom_rproc.h>
#include "pt_regs.h"
#if defined(CONFIG_PANEL_NOTIFIER)
#include <linux/soc/qcom/panel_event_notifier.h>
#endif

#define PINCTRL_STATE_ACTIVE    "pmx_ts_active"
#define PINCTRL_STATE_SUSPEND   "pmx_ts_suspend"
#define PINCTRL_STATE_RELEASE   "pmx_ts_release"

#define FT_VTG_MIN_UV           2800000
#define FT_VTG_MAX_UV           2800000
#define FT_I2C_VTG_MIN_UV       1800000
#define FT_I2C_VTG_MAX_UV       1800000

#define PWR_SUSPEND_LOAD_UA  165
#define I2C_SUSPEND_LOAD_UA  100
#define PWR_ACTIVE_LOAD_MA  12000
#define I2C_ACTIVE_LOAD_MA  30000

#define PT_CORE_STARTUP_RETRY_COUNT		3

#define PT_STATUS_STR_LEN (50)

#define PT_DATA_SIZE  (2 * 256)


#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
static struct drm_panel *active_panel;
#endif

MODULE_FIRMWARE(PT_FW_FILE_NAME);

#define ENABLE_I2C_REG_ONLY

enum core_states {
		STATE_NONE,
		STATE_RESUME,
		STATE_SUSPEND
};
#ifdef ENABLE_I2C_REG_ONLY
static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en);
#endif

#define PT_AMBIENT_MODE

#ifdef PT_AMBIENT_MODE
static int pt_device_exit(struct i2c_client *client);
int pt_device_entry(struct device *dev,
		u16 irq, size_t xfer_buf_size);
#endif

static const char *pt_driver_core_name = PT_CORE_NAME;
static const char *pt_driver_core_version = PT_DRIVER_VERSION;
static const char *pt_driver_core_date = PT_DRIVER_DATE;
enum core_states pt_core_state = STATE_NONE;

uint32_t pt_slate_resp_ack;
struct pt_hid_field {
	int report_count;
	int report_size;
	int size; /* report_count * report_size */
	int offset;
	int data_type;
	int logical_min;
	int logical_max;
	/* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */
	u32 usage_page;
	u32 collection_usage_pages[PT_HID_MAX_COLLECTIONS];
	struct pt_hid_report *report;
	bool record_field;
};

struct pt_hid_report {
	u8 id;
	u8 type;
	int size;
	struct pt_hid_field *fields[PT_HID_MAX_FIELDS];
	int num_fields;
	int record_field_index;
	int header_size;
	int record_size;
	u32 usage_page;
};

struct atten_node {
	struct list_head node;
	char *id;
	struct device *dev;

	int (*func)(struct device *dev);
	int mode;
};

struct param_node {
	struct list_head node;
	u8 id;
	u32 value;
	u8 size;
};

struct module_node {
	struct list_head node;
	struct pt_module *module;
	void *data;
};

struct pt_hid_cmd {
	u8 opcode;
	u8 report_type;
	union {
		u8 report_id;
		u8 power_state;
	};
	u8 has_data_register;
	size_t write_length;
	u8 *write_buf;
	u8 *read_buf;
	u8 wait_interrupt;
	u8 reset_cmd;
	u16 timeout_ms;
};

struct pt_hid_output {
	u8 cmd_type;
	u16 length;
	u8 command_code;
	size_t write_length;
	u8 *write_buf;
	u8 novalidate;
	u8 reset_expected;
	u16 timeout_ms;
};

#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode)
#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4))
#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id)

#define CREATE_PIP1_FW_CMD(command) \
	.cmd_type = PIP1_CMD_TYPE_FW, \
	.command_code = command

#define CREATE_PIP1_BL_CMD(command) \
	.cmd_type = PIP1_CMD_TYPE_BL, \
	.command_code = command

#define PT_MAX_PR_BUF_SIZE  2048
/*******************************************************************************
 * FUNCTION: pt_pr_buf
 *
 * SUMMARY: Print out the contents of a buffer to kmsg based on the debug level
 *
 * RETURN: Void
 *
 * PARAMETERS:
 *	*dev         - pointer to Device structure
 *	 debug_level - requested debug level to print at
 *	*buf         - pointer to buffer to print
 *	 buf_len     - size of buf
 *	*data_name   - Descriptive name of data prefixed to data
 ******************************************************************************/
void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf,
	u16 buf_len, const char *data_name)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int i;
	ssize_t pr_buf_index = 0;
	int max_size;

	/* only proceed if valid debug level and there is data to print */
	if (debug_level <= cd->debug_level && buf_len > 0) {
		char *pr_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL);

		if (!pr_buf)
			return;

		/*
		 * With a space each printed char takes 3 bytes, subtract
		 * the length of the data_name prefix as well as 11 bytes
		 * for the " [0..xxx]: " printed before the data.
		 */
		max_size = (PT_MAX_PR_BUF_SIZE - sizeof(data_name) - 11) / 3;

		/* Ensure pr_buf_index stays within the 1018 size */
		pr_buf_index += scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ",
			data_name);
		for (i = 0; i < buf_len && i < max_size; i++)
			pr_buf_index += scnprintf(pr_buf + pr_buf_index,
				PT_MAX_PR_BUF_SIZE, "%02X ", buf[i]);

		pt_debug(dev, debug_level, "%s\n", pr_buf);
		kfree(pr_buf);
	}
}
EXPORT_SYMBOL_GPL(pt_pr_buf);

#ifdef TTHE_TUNER_SUPPORT
/*******************************************************************************
 * FUNCTION: tthe_print
 *
 * SUMMARY: Format data name and time stamp as the header and format the
 *  content of input buffer with hex base to "tthe_buf". And then wake up event
 *  semaphore for tthe debugfs node.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd         - pointer to core data
 *  *buf        - pointer to input buffer
 *   buf_len    - size of input buffer
 *  *data_name  - pointer to data name
 ******************************************************************************/
static int tthe_print(struct pt_core_data *cd, u8 *buf, int buf_len,
		const u8 *data_name)
{
	int name_len = strlen(data_name);
	int i, n;
	u8 *p;
	int remain;
	u8 data_name_with_time_stamp[100];

	/* Prepend timestamp, if requested, to data_name */
	if (cd->show_timestamp) {
		scnprintf(data_name_with_time_stamp,
			sizeof(data_name_with_time_stamp),
			"[%u] %s", pt_get_time_stamp(), data_name);
		data_name = data_name_with_time_stamp;
		name_len = strlen(data_name);
	}

	mutex_lock(&cd->tthe_lock);
	if (!cd->tthe_buf)
		goto exit;

	/* Add 1 due to the '\n' that is appended at the end */
	if (cd->tthe_buf_len + name_len + buf_len + 1 > cd->tthe_buf_size)
		goto exit;

	if (name_len + buf_len == 0)
		goto exit;

	remain = cd->tthe_buf_size - cd->tthe_buf_len;
	if (remain < name_len)
		name_len = remain;

	p = cd->tthe_buf + cd->tthe_buf_len;
	memcpy(p, data_name, name_len);
	cd->tthe_buf_len += name_len;
	p += name_len;
	remain -= name_len;

	*p = 0;
	for (i = 0; i < buf_len; i++) {
		n = scnprintf(p, remain, "%02X ", buf[i]);
		if (n <= 0)
			break;
		p += n;
		remain -= n;
		cd->tthe_buf_len += n;
	}

	n = scnprintf(p, remain, "\n");
	cd->tthe_buf_len += n;
exit:
	wake_up(&cd->wait_q);
	mutex_unlock(&cd->tthe_lock);
	return 0;
}

/*******************************************************************************
 * FUNCTION: _pt_request_tthe_print
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request to print data to the "tthe_buffer".
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *dev - pointer to device structure
 ******************************************************************************/
static int _pt_request_tthe_print(struct device *dev, u8 *buf,
		int buf_len, const u8 *data_name)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return tthe_print(cd, buf, buf_len, data_name);
}
#endif

/*******************************************************************************
 * FUNCTION: pt_platform_detect_read
 *
 * SUMMARY: To be passed to platform dectect function to perform a read
 *  operation.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *dev  - pointer to Device structure
 *  *buf  - pointer to buffer where the data read will be stored
 *   size - size to be read
 ******************************************************************************/
static int pt_platform_detect_read(struct device *dev, void *buf, int size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return pt_adap_read_default(cd, buf, size);
}

/*******************************************************************************
 * FUNCTION: pt_add_parameter
 *
 * SUMMARY: Adds a parameter that has been altered to the parameter linked list.
 *	On every reset of the DUT this linked list is traversed and all
 *	parameters in it are restored to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd          - pointer to core data
 *	 param_id    - parameter ID to add
 *	 param_value - Value corresponding to the ID
 *	 param_size  - Size of param_value
 ******************************************************************************/
static int pt_add_parameter(struct pt_core_data *cd,
		u8 param_id, u32 param_value, u8 param_size)
{
	struct param_node *param, *param_new;

	/* Check if parameter already exists in the list */
	spin_lock(&cd->spinlock);

	list_for_each_entry(param, &cd->param_list, node) {
		if (param->id == param_id) {
			/* Update parameter */
			param->value = param_value;
			pt_debug(cd->dev, DL_INFO,
				"%s: Update parameter id:%d value:%d size:%d\n",
				 __func__, param_id, param_value, param_size);
			goto exit_unlock;
		}
	}
	spin_unlock(&cd->spinlock);

	param_new = kzalloc(sizeof(*param_new), GFP_KERNEL);
	if (!param_new)
		return -ENOMEM;

	param_new->id = param_id;
	param_new->value = param_value;
	param_new->size = param_size;

	pt_debug(cd->dev, DL_INFO,
		"%s: Add parameter id:%d value:%d size:%d\n",
		__func__, param_id, param_value, param_size);

	spin_lock(&cd->spinlock);
	list_add(&param_new->node, &cd->param_list);
exit_unlock:
	spin_unlock(&cd->spinlock);

	return 0;
}

#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
 * FUNCTION: pt_erase_parameter_list
 *
 * SUMMARY: Empty out the entire parameter linked list of all parameter/value
 *	pairs. In some test cases this functionality is needed to ensure DUT
 *	returns to a virgin state after a reset and no parameters are restored.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static int pt_erase_parameter_list(struct pt_core_data *cd)
{
	struct param_node *pos, *temp;

	spin_lock(&cd->spinlock);
	list_for_each_entry_safe(pos, temp, &cd->param_list, node) {
		pt_debug(cd->dev, DL_INFO,
			"%s: Parameter Restore List - remove 0x%02x\n",
			__func__, pos->id);
		list_del(&pos->node);
		kfree(pos);
	}
	spin_unlock(&cd->spinlock);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_count_parameter_list
 *
 * SUMMARY: Count the items in the RAM parameter restor list
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static int pt_count_parameter_list(struct pt_core_data *cd)
{
	struct param_node *pos, *temp;
	int entries = 0;

	spin_lock(&cd->spinlock);
	list_for_each_entry_safe(pos, temp, &cd->param_list, node)
		entries++;
	spin_unlock(&cd->spinlock);

	return entries;
}
#endif /* TTDL_DIAGNOSTICS */

/*******************************************************************************
 * FUNCTION: request_exclusive
 *
 * SUMMARY: Request exclusive access to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*ownptr     - pointer to device
 *	 timeout_ms - Timeout value
 ******************************************************************************/
int request_exclusive(struct pt_core_data *cd, void *ownptr,
		int timeout_ms)
{
	int t = msecs_to_jiffies(timeout_ms);
	bool with_timeout = (timeout_ms != 0);

	pt_debug(cd->dev, DL_INFO, "%s: Attempt to Request EXCLUSIVE t=%d\n",
		__func__, timeout_ms);

	mutex_lock(&cd->system_lock);
	if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
		cd->exclusive_dev = ownptr;
		goto exit;
	}

	cd->exclusive_waits++;
wait:
	mutex_unlock(&cd->system_lock);
	if (with_timeout) {
		t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
		if (IS_TMO(t)) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: tmo waiting exclusive access\n", __func__);
			return -ETIME;
		}
	} else {
		wait_event(cd->wait_q, !cd->exclusive_dev);
	}
	mutex_lock(&cd->system_lock);
	if (cd->exclusive_dev)
		goto wait;
	cd->exclusive_dev = ownptr;
	cd->exclusive_waits--;
exit:
	mutex_unlock(&cd->system_lock);
	pt_debug(cd->dev, DL_DEBUG, "%s: request exclusive ok=%p\n",
		__func__, ownptr);

	return 0;
}

/*******************************************************************************
 * FUNCTION: release_exclusive_
 *
 * SUMMARY: Release exclusive access to the DUT
 *
 * RETURN:
 *	 0 = success
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*ownptr     - pointer to device
 ******************************************************************************/
static int release_exclusive_(struct pt_core_data *cd, void *ownptr)
{
	pt_debug(cd->dev, DL_INFO, "%s: Attempt to Release EXCLUSIVE\n",
		__func__);
	if (cd->exclusive_dev != ownptr)
		return -EINVAL;

	pt_debug(cd->dev, DL_DEBUG, "%s: exclusive_dev %p freed\n",
		__func__, cd->exclusive_dev);
	cd->exclusive_dev = NULL;
	wake_up(&cd->wait_q);
	return 0;
}

/*******************************************************************************
 * FUNCTION: release_exclusive
 *
 * SUMMARY: Protected wrapper to release_exclusive_()
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*ownptr     - pointer to device
 ******************************************************************************/
int release_exclusive(struct pt_core_data *cd, void *ownptr)
{
	int rc;

	mutex_lock(&cd->system_lock);
	rc = release_exclusive_(cd, ownptr);
	mutex_unlock(&cd->system_lock);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_exec_cmd_
 *
 * SUMMARY: Send the HID command to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data
 *	*hid_cmd - pointer to the HID command to send
 ******************************************************************************/
static int pt_hid_exec_cmd_(struct pt_core_data *cd,
		struct pt_hid_cmd *hid_cmd)
{
	int rc = 0;
	u8 *cmd;
	u16 cmd_length;
	u8 cmd_offset = 0;

	cmd_length = 2 /* command register */
		+ 2    /* command */
		+ (hid_cmd->report_id >= 0XF ? 1 : 0)   /* Report ID */
		+ (hid_cmd->has_data_register ? 2 : 0)	/* Data register */
		+ hid_cmd->write_length;                /* Data length */

	if (cmd_length < 4 || cmd_length > PT_MAX_PIP1_MSG_SIZE)
		return -EPROTO;

	cmd = kzalloc(cmd_length, GFP_KERNEL);
	if (!cmd)
		return -ENOMEM;

	/* Set Command register */
	memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register,
			sizeof(cd->hid_desc.command_register));
	cmd_offset += sizeof(cd->hid_desc.command_register);

	/* Set Command */
	SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type);

	if (hid_cmd->report_id >= 0XF)
		SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF);
	else
		SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id);
	cmd_offset++;

	SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode);
	cmd_offset++;

	if (hid_cmd->report_id >= 0XF) {
		cmd[cmd_offset] = hid_cmd->report_id;
		cmd_offset++;
	}

	/* Set Data register */
	if (hid_cmd->has_data_register) {
		memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register,
				sizeof(cd->hid_desc.data_register));
		cmd_offset += sizeof(cd->hid_desc.data_register);
	}

	/* Set Data */
	if (hid_cmd->write_length && hid_cmd->write_buf) {
		memcpy(&cmd[cmd_offset], hid_cmd->write_buf,
				hid_cmd->write_length);
		cmd_offset += hid_cmd->write_length;
	}

	pt_debug(cd->dev, DL_INFO,
		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
		__func__, cmd_length, hid_cmd->report_id);

	pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> CMD");
	rc = pt_adap_write_read_specific(cd, cmd_length, cmd,
			hid_cmd->read_buf);
	if (rc)
		pt_debug(cd->dev, DL_ERROR,
		"%s: Fail pt_adap_transfer\n", __func__);

	kfree(cmd);
	return rc;
}
#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
 * FUNCTION: pt_toggle_err_gpio
 *
 * SUMMARY: Toggles the pre-defined error GPIO
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	 type - type of err that occurred
 ******************************************************************************/
void pt_toggle_err_gpio(struct pt_core_data *cd, u8 type)
{
	pt_debug(cd->dev, DL_DEBUG, "%s called with type = %d\n",
		__func__, type);
	if (cd->err_gpio && type == cd->err_gpio_type) {
		pt_debug(cd->dev, DL_WARN, "%s: Toggle ERR GPIO\n", __func__);
		gpio_direction_output(cd->err_gpio,
			!gpio_get_value(cd->err_gpio));
	}
}

/*******************************************************************************
 * FUNCTION: _pt_request_toggle_err_gpio
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request to toggle the err_gpio
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	 type - type of err that occurred
 ******************************************************************************/
void _pt_request_toggle_err_gpio(struct device *dev, u8 type)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_toggle_err_gpio(cd, type);
}
#endif /* TTDL_DIAGNOSTICS */

/*******************************************************************************
 * FUNCTION: pt_hid_exec_cmd_and_wait_
 *
 * SUMMARY: Send the HID command to the DUT and wait for the response
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data
 *	*hid_cmd - pointer to the HID command to send
 ******************************************************************************/
static int pt_hid_exec_cmd_and_wait_(struct pt_core_data *cd,
		struct pt_hid_cmd *hid_cmd)
{
	int rc = 0;
	int t;
	u16 timeout_ms;
	int *cmd_state;

	if (hid_cmd->reset_cmd)
		cmd_state = &cd->hid_reset_cmd_state;
	else
		cmd_state = &cd->hid_cmd_state;

	if (hid_cmd->wait_interrupt) {
		mutex_lock(&cd->system_lock);
		*cmd_state = 1;
		mutex_unlock(&cd->system_lock);
	}

	rc = pt_hid_exec_cmd_(cd, hid_cmd);
	if (rc) {
		if (hid_cmd->wait_interrupt)
			goto error;

		goto exit;
	}

	if (!hid_cmd->wait_interrupt)
		goto exit;

	if (hid_cmd->timeout_ms)
		timeout_ms = hid_cmd->timeout_ms;
	else
		timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT;

	t = wait_event_timeout(cd->wait_q, (*cmd_state == 0),
			msecs_to_jiffies(timeout_ms));
	if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
		cd->bus_transmit_error_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: HID output cmd execution timed out\n",
			__func__);
		rc = -ETIME;
		goto error;
	}

	goto exit;

error:
	mutex_lock(&cd->system_lock);
	*cmd_state = 0;
	mutex_unlock(&cd->system_lock);

exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_cmd_reset_
 *
 * SUMMARY: Send the HID RESET command to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data
 ******************************************************************************/
static int pt_hid_cmd_reset_(struct pt_core_data *cd)
{
	struct pt_hid_cmd hid_cmd = {
		.opcode = HID_CMD_RESET,
		.wait_interrupt = 1,
		.reset_cmd = 1,
		.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
	};

	return pt_hid_exec_cmd_and_wait_(cd, &hid_cmd);
}

/*******************************************************************************
 * FUNCTION: pt_hid_cmd_reset
 *
 * SUMMARY: Wrapper function for pt_hid_cmd_reset_ that guarantees exclusive
 *	access.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data
 ******************************************************************************/
static int pt_hid_cmd_reset(struct pt_core_data *cd)
{
	int rc = 0;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	pt_debug(cd->dev, DL_INFO, "%s: Send HID Reset command\n", __func__);
	rc = pt_hid_cmd_reset_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_cmd_set_power_
 *
 * SUMMARY: Send hid cmd to set power state for the DUT and wait for response
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd           - pointer to core data
 *   power_state  - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
 ******************************************************************************/
static int pt_hid_cmd_set_power_(struct pt_core_data *cd,
		u8 power_state)
{
	int rc = 0;
	struct pt_hid_cmd hid_cmd = {
		.opcode = HID_CMD_SET_POWER,
		.wait_interrupt = 1,
		.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
	};
	hid_cmd.power_state = power_state;

	/* The chip won't give response if goes to Deep Standby */
	if (power_state == HID_POWER_STANDBY) {
		rc =  pt_hid_exec_cmd_(cd, &hid_cmd);
		if (rc)
			pt_debug(cd->dev, DL_ERROR,
				"%s: Failed to set power to state:%d\n",
				__func__, power_state);
		else
			cd->fw_sys_mode_in_standby_state = true;
		return rc;
	}
	cd->fw_sys_mode_in_standby_state = false;

	rc =  pt_hid_exec_cmd_and_wait_(cd, &hid_cmd);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Failed to set power to state:%d\n",
			__func__, power_state);
		return rc;
	}

	/* validate */
	if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID)
			|| ((cd->response_buf[3] & 0x3) != power_state)
			|| ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER))
		rc = -EINVAL;

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_cmd_set_power
 *
 * SUMMARY: Wrapper function for pt_hid_cmd_set_power_ that guarantees
 *  exclusive access.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd           - pointer to core data
 *   power_state  - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
 ******************************************************************************/
static int pt_hid_cmd_set_power(struct pt_core_data *cd,
		u8 power_state)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_cmd_set_power_(cd, power_state);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

static const u16 crc_table[16] = {
	0x0000, 0x1021, 0x2042, 0x3063,
	0x4084, 0x50a5, 0x60c6, 0x70e7,
	0x8108, 0x9129, 0xa14a, 0xb16b,
	0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
};

/*******************************************************************************
 * FUNCTION: _pt_compute_crc
 *
 * SUMMARY: Calculate CRC by CRC table.
 *
 * RETURN:
 *   CRC calculation result
 *
 * PARAMETERS:
 *  *buf   - pointer to the data array to be calculated
 *   size  - size of data array
 ******************************************************************************/
static u16 _pt_compute_crc(u8 *buf, u32 size)
{
	u16 remainder = 0xFFFF;
	u16 xor_mask = 0x0000;
	u32 index;
	u32 byte_value;
	u32 table_index;
	u32 crc_bit_width = sizeof(u16) * 8;

	/* Divide the message by polynomial, via the table. */
	for (index = 0; index < size; index++) {
		byte_value = buf[index];
		table_index = ((byte_value >> 4) & 0x0F)
			^ (remainder >> (crc_bit_width - 4));
		remainder = crc_table[table_index] ^ (remainder << 4);
		table_index = (byte_value & 0x0F)
			^ (remainder >> (crc_bit_width - 4));
		remainder = crc_table[table_index] ^ (remainder << 4);
	}

	/* Perform the final remainder CRC. */
	return remainder ^ xor_mask;
}

u16 ccitt_Table[] = {
	0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
	0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
	0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
	0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
	0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
	0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
	0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
	0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
	0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
	0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
	0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
	0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
	0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
	0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
	0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
	0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
	0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
	0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
	0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
	0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
	0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
	0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
	0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
	0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
	0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
	0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
	0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
	0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
	0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
	0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
	0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
	0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
};

/*******************************************************************************
 * FUNCTION: crc_ccitt_calculate
 *
 * SUMMARY: Calculate CRC with ccitt standard by CRC table.
 *
 * RETURN:
 *   CRC calculation result
 *
 * PARAMETERS:
 *  *q    - pointer to the data array to be calculated
 *   len  - size of data array
 ******************************************************************************/
static unsigned short crc_ccitt_calculate(unsigned char *q, int len)
{
	unsigned short crc = 0xffff;

	while (len-- > 0)
		crc = ccitt_Table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8);
	return crc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_cmd_calculate_crc
 *
 * SUMMARY: Calculate the CRC of a command packet
 *
 * RETURN: void
 *
 * PARAMETERS:
 *	*cmd         - pointer to command data
 *	 extra_bytes - Extra bytes included in command length
 ******************************************************************************/
static void pt_pip2_cmd_calculate_crc(struct pip2_cmd_structure *cmd,
	u8 extra_bytes)
{
	u8 buf[PT_MAX_PIP2_MSG_SIZE + 1] = {0};
	unsigned short crc;

	buf[0] = cmd->len & 0xff;
	buf[1] = (cmd->len & 0xff00) >> 8;
	buf[2] = cmd->seq;
	buf[3] = cmd->id;
	memcpy(&buf[4], cmd->data, cmd->len - extra_bytes);
	/* Calculate the CRC for the first 4 bytes above and the data payload */
	crc = crc_ccitt_calculate(buf, 4 + (cmd->len - extra_bytes));
	cmd->crc[0] = (crc & 0xff00) >> 8;
	cmd->crc[1] = (crc & 0xff);
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_next_cmd_seq
 *
 * SUMMARY: Gets the next sequence number for a PIP2 command. The sequence
 *	number is a 3 bit value (bits [0-2]) but because TTDL will always have
 *	the TAG bit set (bit 3), the counter starts at 0x08 and goes to 0x0F.
 *	If the "force_pip2_seq" holds a valid seq value (0x08-0x0F) then do not
 *	increment, just use the forced value.
 *
 * RETURN: Next command sequence number [0x08-0x0F]
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static u8 pt_pip2_get_next_cmd_seq(struct pt_core_data *cd)
{
#ifdef TTDL_DIAGNOSTICS
	if (cd->force_pip2_seq <= 0x07) {
		cd->pip2_cmd_tag_seq++;
		if (cd->pip2_cmd_tag_seq > 0x0F)
			cd->pip2_cmd_tag_seq = 0x08;
	} else {
		cd->pip2_cmd_tag_seq = cd->force_pip2_seq;
	}
#else
	cd->pip2_cmd_tag_seq++;
	if (cd->pip2_cmd_tag_seq > 0x0F)
		cd->pip2_cmd_tag_seq = 0x08;
#endif
	return cd->pip2_cmd_tag_seq;
}

/*
 * Following macros are to define the response time (the interval between PIP2
 * command finishes sending and INT pin falls). The unit is in microsecond.
 * It has different time settings between the solution GPIO polling and Bus
 * polling due to the considration for system load.
 */
#ifdef PT_POLL_RESP_BY_BUS
#define POLL_RETRY_DEFAULT_INTERVAL    50
#define PIP2_RESP_DEFAULT_TIME_MIN     50
#define PIP2_RESP_DEFAULT_TIME_MAX     (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
#define PIP2_RESP_FILE_WRITE_TIME_MIN  220
#define PIP2_RESP_FILE_IOCTL_TIME_MAX  (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
#else
#define POLL_RETRY_DEFAULT_INTERVAL    20
#define PIP2_RESP_DEFAULT_TIME_MIN     20
#define PIP2_RESP_DEFAULT_TIME_MAX     (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
#define PIP2_RESP_FILE_WRITE_TIME_MIN  20
#define PIP2_RESP_FILE_IOCTL_TIME_MAX  (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
#endif
/*
 * id: the command id defined in PIP2
 * response_len: the (maximum) length of response.
 * response_time_min: minimum response time in microsecond
 * response_time_max: maximum response time in microsecond
 */
static const struct pip2_cmd_response_structure pip2_cmd_response[] = {
	{.id = PIP2_CMD_ID_PING,
	 .response_len = 255,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_STATUS,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 5,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_CTRL,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PT_PIP2_CMD_FILE_ERASE_TIMEOUT},
	{.id = PIP2_CMD_ID_CONFIG,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_CLEAR,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 0,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_RESET,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_VERSION,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 23,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_FILE_OPEN,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 2,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_FILE_CLOSE,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_FILE_READ,
	 .response_len = 255,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_FILE_WRITE,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_FILE_WRITE_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_FILE_IOCTL,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 10,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_FILE_IOCTL_TIME_MAX},
	{.id = PIP2_CMD_ID_FLASH_INFO,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 17,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_EXECUTE,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_GET_LAST_ERRNO,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 3,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	 {.id = PIP2_CMD_ID_EXIT_HOST_MODE,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_READ_GPIO,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 5,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_EXECUTE_SCAN,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_SET_PARAMETER,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_GET_PARAMETER,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 7,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_SET_DDI_REG,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_GET_DDI_REG,
	 .response_len = PIP2_EXTRA_BYTES_NUM + 249,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
	{.id = PIP2_CMD_ID_END,
	 .response_len = 255,
	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}
};

/*******************************************************************************
 * FUNCTION: pt_pip2_get_cmd_response_len
 *
 * SUMMARY: Gets the expected response length based on the command ID
 *
 * RETURN: Expected response length
 *
 * PARAMETERS:
 *	id - Command ID (-1 means input ID is not in list of PIP2 command)
 ******************************************************************************/
static int pt_pip2_get_cmd_response_len(u8 id)
{
	const struct pip2_cmd_response_structure *p = pip2_cmd_response;

	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
		p++;

	if (p->id != PIP2_CMD_ID_END)
		return p->response_len;
	else
		return -EPERM;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_cmd_resp_time_min
 *
 * SUMMARY: Gets the minimum response time (the interval between PIP2 command
 * finishes sending and INT pin falls) based on the command ID
 *
 * RETURN: Estimated minimum response time in microsecond
 *
 * PARAMETERS:
 *	id - Command ID
 ******************************************************************************/
static u32 pt_pip2_get_cmd_resp_time_min(u8 id)
{
	const struct pip2_cmd_response_structure *p = pip2_cmd_response;

	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
		p++;

	if (p->id != PIP2_CMD_ID_END)
		return p->response_time_min;
	else
		return PIP2_RESP_DEFAULT_TIME_MIN;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_cmd_resp_time_max
 *
 * SUMMARY: Gets the maximum response time (the interval between PIP2 command
 * finishes sending and INT pin falls) based on the command ID
 *
 * RETURN: Estimated maximum response time in microsecond
 *
 * PARAMETERS:
 *	id - Command ID
 ******************************************************************************/
static u32 pt_pip2_get_cmd_resp_time_max(u8 id)
{
	const struct pip2_cmd_response_structure *p = pip2_cmd_response;

	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
		p++;

	if (p->id != PIP2_CMD_ID_END)
		return p->response_time_max;
	else
		return PIP2_RESP_DEFAULT_TIME_MAX;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_validate_response
 *
 * SUMMARY: Validate the response of PIP2 command.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd              - pointer to core data
 *	*pip2_cmd        - pointer to PIP2 command to send
 *	*read_buf        - pointer to response buffer
 *	 actual_read_len - actual read length of the response
 ******************************************************************************/
static int pt_pip2_validate_response(struct pt_core_data *cd,
		struct pip2_cmd_structure *pip2_cmd, u8 *read_buf,
		u16 actual_read_len)
{
	int rc = 0;
	u8 response_seq = 0;
	u8 reserved_bits = 0;
	u8 cmd_id = 0;
	u8 response_bit = 0;
	unsigned short calc_crc = 0;
	unsigned short resp_crc = 0;

	/* Verify the length of response buffer */
	if (actual_read_len < PT_MIN_PIP2_PACKET_SIZE) {
		pt_debug(cd->dev, DL_ERROR,
			"%s cmd[0x%02X] read length ERR: read_len = %d\n",
			__func__, pip2_cmd->id, actual_read_len);
		rc = -EINVAL;
		goto exit;
	}

	/* Verify the CRC */
	calc_crc = crc_ccitt_calculate(read_buf, actual_read_len - 2);
	resp_crc  = read_buf[actual_read_len - 2] << 8;
	resp_crc |= read_buf[actual_read_len - 1];
	if (resp_crc != calc_crc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: cmd[0x%02X] CRC ERR: calc=0x%04X rsp=0x%04X\n",
			__func__, pip2_cmd->id, calc_crc, resp_crc);
#ifdef TTDL_DIAGNOSTICS
		cd->pip2_crc_error_count++;
#endif /* TTDL_DIAGNOSTICS */
		rc = -EINVAL;
		goto exit;
	}

	/* Verify the response bit is set */
	response_bit = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x80;
	if (!response_bit) {
		pt_debug(cd->dev, DL_ERROR,
			"%s cmd[0x%02X] response bit ERR: response_bit = %d\n",
			__func__, pip2_cmd->id, response_bit);
		rc = -EINVAL;
		goto exit;
	}

	/* Verify the command ID matches from command to response */
	cmd_id = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x7F;
	if (cmd_id != pip2_cmd->id) {
		pt_debug(cd->dev, DL_ERROR,
			"%s cmd[0x%02X] command ID ERR: cmd_id = 0x%02X\n",
			__func__, pip2_cmd->id, cmd_id);
		rc = -EINVAL;
		goto exit;
	}

	/* Verify the SEQ number matches from command to response */
	response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F;
	if ((pip2_cmd->seq & 0x0F) != response_seq) {
		pt_debug(cd->dev, DL_ERROR,
			"%s cmd[0x%02X] send_seq = 0x%02X, resp_seq = 0x%02X\n",
			__func__, pip2_cmd->id,
			pip2_cmd->seq, response_seq);
		rc = -EINVAL;
		goto exit;
	}

	/* Verify the reserved bits are 0 */
	reserved_bits = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0xF0;
	if (reserved_bits)
		pt_debug(cd->dev, DL_WARN,
			"%s cmd[0x%02X] reserved_bits = 0x%02X\n",
			__func__, pip2_cmd->id, reserved_bits);

exit:
	if (rc)
		pt_pr_buf(cd->dev, DL_WARN, cd->input_buf, actual_read_len,
				"PIP RSP:");
	return rc;
}


/*******************************************************************************
 * FUNCTION: pt_hid_output_validate_bl_response
 *
 * SUMMARY: Validate the response of bootloader command.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd          - pointer to core data
 *  *hid_output  - pointer to hid output data structure
 ******************************************************************************/
static int pt_hid_output_validate_bl_response(
		struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	u16 size;
	u16 crc;
	u8 status;

	size = get_unaligned_le16(&cd->response_buf[0]);

	if (hid_output->reset_expected && !size)
		return 0;

	if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
			!= PT_PIP_BL_RESPONSE_REPORT_ID) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: BL output response, wrong report_id\n", __func__);
		return -EPROTO;
	}

	if (cd->response_buf[4] != PIP1_BL_SOP) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: BL output response, wrong SOP\n", __func__);
		return -EPROTO;
	}

	if (cd->response_buf[size - 1] != PIP1_BL_EOP) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: BL output response, wrong EOP\n", __func__);
		return -EPROTO;
	}

	crc = _pt_compute_crc(&cd->response_buf[4], size - 7);
	if (cd->response_buf[size - 3] != LOW_BYTE(crc)
			|| cd->response_buf[size - 2] != HI_BYTE(crc)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: BL output response, wrong CRC 0x%X\n",
			__func__, crc);
		return -EPROTO;
	}

	status = cd->response_buf[5];
	if (status) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: BL output response, ERROR:%d\n",
			__func__, status);
		return -EPROTO;
	}

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_validate_app_response
 *
 * SUMMARY: Validate the response of application command.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd          - pointer to core data
 *  *hid_output  - pointer to hid output data structure
 ******************************************************************************/
static int pt_hid_output_validate_app_response(
		struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	int command_code;
	u16 size;

	size = get_unaligned_le16(&cd->response_buf[0]);

	if (hid_output->reset_expected && !size)
		return 0;

	if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
			!= PT_PIP_NON_HID_RESPONSE_ID) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: APP output response, wrong report_id\n", __func__);
		return -EPROTO;
	}

	command_code = cd->response_buf[PIP1_RESP_COMMAND_ID_OFFSET]
		& PIP1_RESP_COMMAND_ID_MASK;
	if (command_code != hid_output->command_code) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: APP output response, wrong command_code:%X\n",
			__func__, command_code);
		return -EPROTO;
	}

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_check_set_parameter
 *
 * SUMMARY: Check command input and response for Set Parameter command.And
 *  store the parameter to the list for resume work if pass the check.
 *
 * PARAMETERS:
 *  *cd          - pointer to core data
 *  *hid_output  - pointer to hid output data structure
 *   raw         - flag to show if output cmd is user cmd(1:user cmd)
 ******************************************************************************/
static void pt_check_set_parameter(struct pt_core_data *cd,
		struct pt_hid_output *hid_output, bool raw)
{
	u8 *param_buf;
	u32 param_value = 0;
	u8 param_size;
	u8 param_id;
	int i = 0;

	if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
		return;

	/* Check command input for Set Parameter command */
	if (raw && hid_output->length >= 10 && hid_output->length <= 13
			&& !memcmp(&hid_output->write_buf[0],
					&cd->hid_desc.output_register,
					sizeof(cd->hid_desc.output_register))
			&& hid_output->write_buf[4] ==
					PT_PIP_NON_HID_COMMAND_ID
			&& hid_output->write_buf[6] ==
					PIP1_CMD_ID_SET_PARAM)
		param_buf = &hid_output->write_buf[7];
	else if (!raw && hid_output->cmd_type == PIP1_CMD_TYPE_FW
			&& hid_output->command_code == PIP1_CMD_ID_SET_PARAM
			&& hid_output->write_length >= 3
			&& hid_output->write_length <= 6)
		param_buf = &hid_output->write_buf[0];
	else
		return;

	/* Get parameter ID, size and value */
	param_id = param_buf[0];
	param_size = param_buf[1];
	if (param_size > 4) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Invalid parameter size\n", __func__);
		return;
	}

	param_buf = &param_buf[2];
	while (i < param_size)
		param_value += *(param_buf++) << (8 * i++);

	/* Check command response for Set Parameter command */
	if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID
			|| (cd->response_buf[4] &
			    PIP1_RESP_COMMAND_ID_MASK) !=
				PIP1_CMD_ID_SET_PARAM
			|| cd->response_buf[5] != param_id
			|| cd->response_buf[6] != param_size) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Set Parameter command not successful\n",
			__func__);
		return;
	}

	pt_add_parameter(cd, param_id, param_value, param_size);
}

/*******************************************************************************
 * FUNCTION: pt_check_command
 *
 * SUMMARY: Check the output command. The function pt_check_set_parameter() is
 *  called here to check output command and store parameter to the list.
 *
 * PARAMETERS:
 *  *cd          - pointer to core data
 *  *hid_output  - pointer to hid output data structure
 *   raw         - flag to show if output cmd is user cmd(1:user cmd)
 ******************************************************************************/
static void pt_check_command(struct pt_core_data *cd,
		struct pt_hid_output *hid_output, bool raw)
{
	pt_check_set_parameter(cd, hid_output, raw);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_validate_response
 *
 * SUMMARY: Validate the response of application or bootloader command.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd          - pointer to core data
 *  *hid_output  - pointer to hid output data structure
 ******************************************************************************/
static int pt_hid_output_validate_response(struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL)
		return pt_hid_output_validate_bl_response(cd, hid_output);

	return pt_hid_output_validate_app_response(cd, hid_output);

}

/*******************************************************************************
 * FUNCTION: pt_hid_send_output_user_
 *
 * SUMMARY: Blindly send user data to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hid_output - pointer to the command to send
 ******************************************************************************/
static int pt_hid_send_output_user_(struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	int rc = 0;
	int cmd;

	if (!hid_output->length || !hid_output->write_buf)
		return -EINVAL;

	if (cd->pip2_prot_active) {
		cmd = hid_output->write_buf[PIP2_CMD_COMMAND_ID_OFFSET];
		cmd &= PIP2_CMD_COMMAND_ID_MASK;
	} else
		cmd = hid_output->write_buf[PIP1_CMD_COMMAND_ID_OFFSET];

	pt_debug(cd->dev, DL_INFO,
		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
		__func__, hid_output->length, cmd);

	pt_pr_buf(cd->dev, DL_DEBUG, hid_output->write_buf,
		hid_output->length, ">>> User CMD");

	rc = pt_adap_write_read_specific(cd, hid_output->length,
			hid_output->write_buf, NULL);
	if (rc)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Fail pt_adap_transfer\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_send_output_user_and_wait_
 *
 * SUMMARY: Blindly send user data to the DUT and wait for the response.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hid_output - pointer to the command to send
 ******************************************************************************/
static int pt_hid_send_output_user_and_wait_(struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	int rc = 0;
	int t;

	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = PIP1_CMD_ID_USER_CMD + 1;
	mutex_unlock(&cd->system_lock);

	rc = pt_hid_send_output_user_(cd, hid_output);
	if (rc)
		goto error;

	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
		msecs_to_jiffies(cd->pip_cmd_timeout));
	if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
		cd->bus_transmit_error_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: HID output cmd execution timed out\n",
			__func__);
		rc = -ETIME;
		goto error;
	}
	pt_check_command(cd, hid_output, true);
	goto exit;

error:
	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = 0;
	mutex_unlock(&cd->system_lock);

exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_check_irq_asserted
 *
 * SUMMARY: Checks if the IRQ GPIO is asserted or not. There are times when
 *	the FW can hold the INT line low ~150us after the read is complete.
 *	NOTE: if irq_stat is not defined this function will return false
 *
 * RETURN:
 *	true  = IRQ asserted
 *	false = IRQ not asserted
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static bool pt_check_irq_asserted(struct pt_core_data *cd)
{
#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP
	/*
	 * Workaround for FW defect, CDT165308
	 * bl_launch app creates a glitch in IRQ line
	 */
	if (cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1
		&& cd->cpdata->irq_stat) {
		/*
		 * in X1S panel and GC1546 panel, the width for the INT
		 * glitch is about 4us,the normal INT width of response
		 * will last more than 200us, so use 10us delay
		 * for distinguish the glitch the normal INT is enough.
		 */
		udelay(10);
	}
#endif
	if (cd->cpdata->irq_stat) {
		if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
		    == PT_IRQ_ASSERTED_VALUE) {
			/* Debounce to allow FW to release INT */
			usleep_range(100, 200);
		}
		if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
		    == PT_IRQ_ASSERTED_VALUE)
			return true;
		else
			return false;
	}
	return true;
}

/*******************************************************************************
 * FUNCTION: pt_flush_bus
 *
 * SUMMARY: Force flushing the bus by reading len bytes or forced 255 bytes
 *	Used if IRQ is found to be stuck low
 *
 * RETURN: Length of bytes read from bus
 *
 * PARAMETERS:
 *      *cd         - pointer to core data
 *	 flush_type - type of flush
 *			- PT_FLUSH_BUS_BASED_ON_LEN (two reads)
 *			- PT_FLUSH_BUS_FULL_256_READ
 *      *read_buf  - pointer to store read data
 ******************************************************************************/
static ssize_t pt_flush_bus(struct pt_core_data *cd,
		u8 flush_type, u8 *read_buf)
{
	u8 buf[PT_MAX_PIP2_MSG_SIZE];
	u16 pip_len;
	int bytes_read;
	int rc = 0;

	if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) {
		rc = pt_adap_read_default(cd, buf, 2);
		if (rc) {
			bytes_read = 0;
			goto exit;
		}

		pip_len = get_unaligned_le16(&buf[0]);

		if (pip_len == 2 || pip_len >= PT_PIP_1P7_EMPTY_BUF) {
#ifdef TTDL_DIAGNOSTICS
			pt_toggle_err_gpio(cd, PT_ERR_GPIO_EMPTY_PACKET);
#endif
			bytes_read = 2;
			pt_debug(cd->dev, DL_INFO,
				"%s: Empty buf detected - len=0x%04X\n",
				__func__, pip_len);
		} else if (pip_len == 0) {
			bytes_read = 0;
			pt_debug(cd->dev, DL_INFO,
				"%s: Sentinel detected\n", __func__);
		} else if (pip_len > PT_MAX_PIP2_MSG_SIZE) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Illegal len=0x%04x, force %d byte read\n",
				__func__, pip_len, PT_MAX_PIP2_MSG_SIZE);
			rc = pt_adap_read_default(cd, buf,
				PT_MAX_PIP2_MSG_SIZE);
			if (!rc)
				bytes_read = PT_MAX_PIP2_MSG_SIZE;
			else
				bytes_read = 0;
		} else {
			pt_debug(cd->dev, DL_INFO,
				"%s: Flush read of %d bytes...\n",
				__func__, pip_len);

			rc = pt_adap_read_default(cd, buf, pip_len);
			if (!rc)
				bytes_read = pip_len;
			else
				bytes_read = 0;
		}
	} else {
		pt_debug(cd->dev, DL_INFO,
			"%s: Forced flush of max %d bytes...\n",
			__func__, PT_MAX_PIP2_MSG_SIZE);
		rc = pt_adap_read_default(cd, buf, PT_MAX_PIP2_MSG_SIZE);
		if (!rc)
			bytes_read = PT_MAX_PIP2_MSG_SIZE;
		else
			bytes_read = 0;
	}

	if (read_buf && (bytes_read > 3))
		memcpy(read_buf, buf, bytes_read);

exit:
	return bytes_read;
}

/*******************************************************************************
 * FUNCTION: pt_flush_bus_if_irq_asserted
 *
 * SUMMARY: This function will flush the active bus if the INT is found to be
 *	asserted.
 *
 * RETURN: bytes cleared from bus
 *
 * PARAMETERS:
 *	*cd         - pointer the core data structure
 *	 flush_type - type of flush
 *			- PT_FLUSH_BUS_BASED_ON_LEN
 *			- PT_FLUSH_BUS_FULL_256_READ
 ******************************************************************************/
static int pt_flush_bus_if_irq_asserted(struct pt_core_data *cd, u8 flush_type)
{
	int count = 0;
	int bytes_read = 0;

	while (pt_check_irq_asserted(cd) && count < 5) {
		count++;
		bytes_read = pt_flush_bus(cd, flush_type, NULL);
		if (bytes_read) {
			pt_debug(cd->dev, DL_WARN,
				"%s: Cleared %d bytes off bus\n",
				__func__, bytes_read);
		}
	}
	if (pt_check_irq_asserted(cd)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: IRQ still asserted, %d bytes read\n",
			__func__, bytes_read);
	} else {
		pt_debug(cd->dev, DL_INFO,
			"%s: IRQ cleared, %d bytes read\n",
			__func__, bytes_read);
	}
	return bytes_read;
}

/*******************************************************************************
 * FUNCTION: pt_hid_send_output_
 *
 * SUMMARY: Send a touch application command to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hid_output - pointer to the command to send
 ******************************************************************************/
static int pt_hid_send_output_(struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	int rc = 0;
	u8 *cmd;
	u16 length;
	u16 crc;
	u8 report_id;
	u8 cmd_offset = 0;
	u8 cmd_allocated = 0;

	switch (hid_output->cmd_type) {
	case PIP1_CMD_TYPE_FW:
		report_id = PT_PIP_NON_HID_COMMAND_ID;
		length = 5;
		break;
	case PIP1_CMD_TYPE_BL:
		report_id = PT_PIP_BL_COMMAND_REPORT_ID;
		length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */;
		break;
	default:
		return -EINVAL;
	}

	length += hid_output->write_length;

	if (length + 2 > PT_PREALLOCATED_CMD_BUFFER) {
		cmd = kzalloc(length + 2, GFP_KERNEL);
		if (!cmd)
			return -ENOMEM;
		cmd_allocated = 1;
	} else {
		cmd = cd->cmd_buf;
	}

	/* Set Output register */
	memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register,
			sizeof(cd->hid_desc.output_register));
	cmd_offset += sizeof(cd->hid_desc.output_register);

	cmd[cmd_offset++] = LOW_BYTE(length);
	cmd[cmd_offset++] = HI_BYTE(length);
	cmd[cmd_offset++] = report_id;
	cmd[cmd_offset++] = 0x0; /* reserved */
	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL)
		cmd[cmd_offset++] = PIP1_BL_SOP;
	cmd[cmd_offset++] = hid_output->command_code;

	/* Set Data Length for bootloader */
	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
		cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length);
		cmd[cmd_offset++] = HI_BYTE(hid_output->write_length);
	}
	/* Set Data */
	if (hid_output->write_length && hid_output->write_buf) {
		memcpy(&cmd[cmd_offset], hid_output->write_buf,
				hid_output->write_length);
		cmd_offset += hid_output->write_length;
	}
	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
		crc = _pt_compute_crc(&cmd[6],
				hid_output->write_length + 4);
		cmd[cmd_offset++] = LOW_BYTE(crc);
		cmd[cmd_offset++] = HI_BYTE(crc);
		cmd[cmd_offset++] = PIP1_BL_EOP;
	}

	pt_debug(cd->dev, DL_INFO,
		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
		__func__, length + 2, hid_output->command_code);

	pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD");
	rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL);

	if (rc)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Fail pt_adap_transfer rc=%d\n", __func__, rc);

	if (cmd_allocated)
		kfree(cmd);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip1_send_output_and_wait_
 *
 * SUMMARY: Send valid PIP1 command to the DUT and wait for the response.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hid_output - pointer to the command to send
 ******************************************************************************/
static int pt_pip1_send_output_and_wait_(struct pt_core_data *cd,
		struct pt_hid_output *hid_output)
{
	int rc = 0;
	int t;
	u16 timeout_ms;

	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = hid_output->command_code + 1;
	mutex_unlock(&cd->system_lock);

	if (hid_output->timeout_ms)
		timeout_ms = hid_output->timeout_ms;
	else
		timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT;

	rc = pt_hid_send_output_(cd, hid_output);
	if (rc)
		goto error;

	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
			msecs_to_jiffies(timeout_ms));
	if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
		cd->bus_transmit_error_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: HID output cmd execution timed out (%dms)\n",
			__func__, timeout_ms);
		rc = -ETIME;
		goto error;
	}

	if (!hid_output->novalidate)
		rc = pt_hid_output_validate_response(cd, hid_output);

	pt_check_command(cd, hid_output, false);
	goto exit;

error:
	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = 0;
	mutex_unlock(&cd->system_lock);
exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_user_cmd_
 *
 * SUMMARY: Load the write buffer into a HID structure and send it as a HID cmd
 *	to the DUT waiting for the response and loading it into the read buffer
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_len        - expected read length of the response
 *	*read_buf        - pointer to where the response will be loaded
 *	 write_len       - length of the write buffer
 *	*write_buf       - pointer to the write buffer
 *	*actual_read_len - pointer to the actual amount of data read back
 ******************************************************************************/
static int pt_hid_output_user_cmd_(struct pt_core_data *cd,
		u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
		u16 *actual_read_len)
{
	int rc = 0;
	u16 size;
	struct pt_hid_output hid_output = {
		.length = write_len,
		.write_buf = write_buf,
	};
#ifdef TTHE_TUNER_SUPPORT
	if (!cd->pip2_send_user_cmd) {
		int command_code = 0;
		int len;

		/* Print up to cmd ID */
		len = PIP1_CMD_COMMAND_ID_OFFSET + 1;
		if (write_len < len)
			len = write_len;
		else
			command_code = write_buf[PIP1_CMD_COMMAND_ID_OFFSET]
				& PIP1_CMD_COMMAND_ID_MASK;

		/* Don't print EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */
		if (command_code != PIP1_CMD_ID_EXEC_PANEL_SCAN &&
		    command_code != PIP1_CMD_ID_RETRIEVE_PANEL_SCAN)
			tthe_print(cd, write_buf, len, "CMD=");
	}
#endif
	rc = pt_hid_send_output_user_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	/* Get the response size from the first 2 bytes in the response */
	size = get_unaligned_le16(&cd->response_buf[0]);

	/* Ensure size is not greater than max buffer size */
	if (size > PT_MAX_PIP2_MSG_SIZE)
		size = PT_MAX_PIP2_MSG_SIZE;

	/* Minimum size to read is the 2 byte len field */
	if (size == 0)
		size = 2;

	if (size > read_len) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: PIP2 len field=%d, requested read_len=%d\n",
			__func__, size, read_len);
		*actual_read_len = 0;
		return -EIO;
	}

	memcpy(read_buf, cd->response_buf, size);
	*actual_read_len = size;

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_user_cmd
 *
 * SUMMARY: Protected call to pt_hid_output_user_cmd_ by exclusive access to
 *  the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_len        - length of data to read
 *	*read_buf        - pointer to store read data
 *	 write_len       - length of data to write
 *	*write_buf       - pointer to buffer to write
 *	*actual_read_len - pointer to store data length actually read
 ******************************************************************************/
static int pt_hid_output_user_cmd(struct pt_core_data *cd,
		u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
		u16 *actual_read_len)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_user_cmd_(cd, read_len, read_buf,
			write_len, write_buf, actual_read_len);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip2_send_cmd
 *
 * SUMMARY: Writes a PIP2 command packet to DUT, then waits for the
 *	interrupt and reads response data to read_buf
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to run in protected mode
 *	 id              - ID of PIP command
 *	*data            - pointer to PIP data payload
 *	 report_body_len - report length
 *	*read_buf        - pointer to response buffer
 *	*actual_read_len - pointer to response buffer length
 ******************************************************************************/
static int _pt_request_pip2_send_cmd(struct device *dev,
	int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
	u16 *actual_read_len)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pip2_cmd_structure pip2_cmd;
	int rc = 0;
	u16 i = 0;
	u16 j = 0;
	u16 write_len;
	u8 *write_buf = NULL;
	u16 read_len;
	u8 extra_bytes;

	if (report_body_len > 247)
		return -EPROTO;

	memset(&pip2_cmd, 0, sizeof(pip2_cmd));
	/* Hard coded register for PIP2.x */
	pip2_cmd.reg[0] = 0x01;
	pip2_cmd.reg[1] = 0x01;

	/*
	 * For PIP2.1+ the length field value includes itself:
	 * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC)
	 *
	 * The overall write length must include only the register:
	 * ADD 2: 2 (Register)
	 */
	extra_bytes = 6;
	write_len = 2;

	/* PIP2 the CMD ID is a 7bit field */
	if (id > PIP2_CMD_ID_END) {
		pt_debug(dev, DL_WARN, "%s: Invalid PIP2 CMD ID 0x%02X\n",
			__func__, id);
		rc = -EINVAL;
		goto exit;
	}

	pip2_cmd.len  = report_body_len + extra_bytes;
	pip2_cmd.id   = id & PIP2_CMD_COMMAND_ID_MASK;
	pip2_cmd.seq  = pt_pip2_get_next_cmd_seq(cd);
	pip2_cmd.data = data;

	pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes);

	/* Add the command length to the extra bytes based on PIP version */
	write_len += pip2_cmd.len;

	pt_debug(dev, DL_INFO, "%s Length Field: %d, Write Len: %d",
		__func__, pip2_cmd.len, write_len);

	write_buf = kzalloc(write_len, GFP_KERNEL);

	if (write_buf == NULL) {
		rc = -ENOMEM;
		goto exit;
	}

	write_buf[i++] = pip2_cmd.reg[0];
	write_buf[i++] = pip2_cmd.reg[1];
	write_buf[i++] = pip2_cmd.len & 0xff;
	write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8;
	write_buf[i++] = pip2_cmd.seq;
	write_buf[i++] = pip2_cmd.id;

	for (j = i; j < i + pip2_cmd.len - extra_bytes; j++)
		write_buf[j] = pip2_cmd.data[j-i];
	write_buf[j++] = pip2_cmd.crc[0];
	write_buf[j++] = pip2_cmd.crc[1];

	read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id);
	if (read_len < 0)
		read_len = 255;

	pt_debug(dev, DL_INFO,
		"%s cmd_id[0x%02X] expected response length:%d ",
		__func__, pip2_cmd.id, read_len);

	/*
	 * All PIP2 commands come through this function.
	 * Set flag for PIP2.x interface to allow response parsing to know
	 * how to decode the protocol header.
	 */
	mutex_lock(&cd->system_lock);
	cd->pip2_prot_active = true;
	cd->pip2_send_user_cmd = true;
	mutex_unlock(&cd->system_lock);

	if (protect == PT_CORE_CMD_PROTECTED)
		rc = pt_hid_output_user_cmd(cd, read_len, read_buf,
			write_len, write_buf, actual_read_len);
	else {
		rc = pt_hid_output_user_cmd_(cd, read_len, read_buf,
			write_len, write_buf, actual_read_len);
	}
	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: nonhid_cmd->user_cmd() Error = %d\n",
			__func__, rc);
		goto exit;
	}
	rc = pt_pip2_validate_response(cd, &pip2_cmd, read_buf,
		*actual_read_len);
exit:
	mutex_lock(&cd->system_lock);
	cd->pip2_prot_active = false;
	cd->pip2_send_user_cmd = false;
	mutex_unlock(&cd->system_lock);
	kfree(write_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_send_cmd_no_int
 *
 * SUMMARY: Writes a PIP2 command packet to DUT, then poll the response and
 *	reads response data to read_buf if response is available.
 *
 * NOTE:
 *  Interrupt MUST be disabled before to call this function.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to run in protected mode
 *	 id              - ID of PIP command
 *	*data            - pointer to PIP data payload
 *	 report_body_len - report length
 *	*read_buf        - pointer to response buffer
 *	*actual_read_len - pointer to response buffer length
 ******************************************************************************/
static int _pt_pip2_send_cmd_no_int(struct device *dev,
	int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
	u16 *actual_read_len)
{
	int max_retry = 0;
	int retry = 0;
	int rc = 0;
	u16 i = 0;
	u16 j = 0;
	u16 write_len;
	u8 *write_buf = NULL;
	u16 read_len;
	u16 size = 0;
	u8 response_seq = 0;
	u8 extra_bytes;
	u32 retry_interval = 0;
	u32 retry_total_time = 0;
	u32 resp_time_min = pt_pip2_get_cmd_resp_time_min(id);
	u32 resp_time_max = pt_pip2_get_cmd_resp_time_max(id);
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pip2_cmd_structure pip2_cmd;

	if (report_body_len > 247)
		return -EPROTO;

	if (protect == PT_CORE_CMD_PROTECTED) {
		rc = request_exclusive(cd,
			cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
		if (rc < 0) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
			return rc;
		}
	}

	memset(&pip2_cmd, 0, sizeof(pip2_cmd));

	/* Hard coded register for PIP2.x */
	pip2_cmd.reg[0] = 0x01;
	pip2_cmd.reg[1] = 0x01;

	/*
	 * For PIP2.1+ the length field value includes itself:
	 * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC)
	 *
	 * The overall write length must include only the register:
	 * ADD 2: 2 (Register)
	 */
	extra_bytes = 6;
	write_len = 2;

	pip2_cmd.len  = report_body_len + extra_bytes;
	pip2_cmd.id   = id;
	pip2_cmd.seq  = pt_pip2_get_next_cmd_seq(cd);
	pip2_cmd.data = data;
	pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes);

	/* Add the command length to the extra bytes based on PIP version */
	write_len += pip2_cmd.len;

	write_buf = kzalloc(write_len, GFP_KERNEL);
	if (write_buf == NULL) {
		rc = -ENOMEM;
		goto exit;
	}

	write_buf[i++] = pip2_cmd.reg[0];
	write_buf[i++] = pip2_cmd.reg[1];
	write_buf[i++] = pip2_cmd.len & 0xff;
	write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8;
	write_buf[i++] = pip2_cmd.seq;
	write_buf[i++] = pip2_cmd.id;

	for (j = i; j < i + pip2_cmd.len - extra_bytes; j++)
		write_buf[j] = pip2_cmd.data[j-i];
	write_buf[j++] = pip2_cmd.crc[0];
	write_buf[j++] = pip2_cmd.crc[1];

	read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id);
	if (read_len < 0)
		read_len = 255;
	pt_debug(dev, DL_INFO,
		"%s: ATM - cmd_id[0x%02X] expected response length:%d ",
		__func__, pip2_cmd.id, read_len);

	pt_pr_buf(cd->dev, DL_DEBUG, write_buf, write_len, ">>> NO_INT CMD");

	rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL);
	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: SPI write Error = %d\n",
			__func__, rc);
		goto exit;
	}

#ifdef PT_POLL_RESP_BY_BUS
	/*
	 * Frequent bus read can increase system load obviously. The expected
	 * first bus read should be valid and timely. The tollerance for
	 * usleep_range should be limited. The minimum response delay (between
	 * command finishes sending and INT pin falls) is less than 50
	 * microseconds. So the 10 microseconds should be maximum tollerance
	 * with the consideration that the unit to calculate the response delay
	 * is 10 microseconds and more precise is not necessary. Every
	 * additional 10 microseconds only contribute less than 3 milliseconds
	 * for whole BL.
	 */
	usleep_range(resp_time_min, resp_time_min+10);
	max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
	while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
		rc = pt_adap_read_default(cd, read_buf, read_len);
		if (rc) {
			pt_debug(dev, DL_ERROR, "%s: SPI read Error = %d\n",
				__func__, rc);
			break;
		}
		response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET];
		size = get_unaligned_le16(&read_buf[0]);
		if ((size <= read_len) &&
			(size >= PIP2_EXTRA_BYTES_NUM)  &&
			(pip2_cmd.seq & 0x07) == (response_seq & 0x07)) {
			break;
		}

		/*
		 * To reduce the bus and system load, increase the sleep
		 * step gradually:
		 * 1  ~ 19 : step=50 us, sleep_us=[50, 100, 150, 200, ..950]
		 * 20 ~ 39 : step=1000 us, sleep_us=[1950, 2950, ...20950]
		 * 40 ~ MAX: step=50 ms, sleep_ms=[71, 121, 191,..]
		 */
		retry++;
		if (retry < 20) {
			retry_interval += POLL_RETRY_DEFAULT_INTERVAL;
			usleep_range(retry_interval,
				retry_interval + POLL_RETRY_DEFAULT_INTERVAL);
		} else if (retry < 40) {
			retry_interval += 1000;
			usleep_range(retry_interval, retry_interval + 1000);
		} else {
			retry_interval += 50000;
			msleep(retry_interval/1000);
		}
		retry_total_time += retry_interval;
	}
#else
	/*
	 * Frequent GPIO read will not increase CPU/system load heavily if the
	 * interval is longer than 10 us, so it is safe to poll GPIO with a
	 * fixed interval: 20 us.
	 */
	usleep_range(resp_time_min, resp_time_min+10);
	max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
	while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
		if (!gpio_get_value(cd->cpdata->irq_gpio)) {
			rc = pt_adap_read_default(cd, read_buf, read_len);
			size = get_unaligned_le16(&read_buf[0]);
			if (rc)
				pt_debug(dev, DL_ERROR,
					"%s: SPI read Error = %d\n",
					__func__, rc);
			else if (size > read_len) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: PIP2 len field=%d, requested read_len=%d\n",
					__func__, size, read_len);
				rc = -EIO;
			}
			break;
		}
		/*
		 * Poll GPIO with fixed interval 20 us, and tollerance is
		 * limited to 10 us to speed up the process.
		 */
		retry_interval = POLL_RETRY_DEFAULT_INTERVAL;
		usleep_range(retry_interval, retry_interval+10);
		retry_total_time += retry_interval;
	}
#endif

	*actual_read_len = size;

	if (rc || (retry >= max_retry) || (retry_total_time >= resp_time_max)) {
		pt_debug(dev, DL_ERROR,
			"%s cmd[0x%02X] timed out, send_seq=0x%02X, resp_seq=0x%02X\n",
			__func__, pip2_cmd.id, pip2_cmd.seq, response_seq);
		*actual_read_len = 0;
		rc = -EINVAL;
	}

	pt_pr_buf(cd->dev, DL_DEBUG, read_buf, *actual_read_len,
		"<<< NO_INT Read");

exit:
	kfree(write_buf);
	if (protect == PT_CORE_CMD_PROTECTED) {
		if (release_exclusive(cd, cd->dev) < 0)
			pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);
	}
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_null_
 *
 * SUMMARY: Send the PIP "ping"(0x00) command to the DUT and wait for response.
 *  This function is used by watchdog to check if the fw corrupts.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_pip_null_(struct pt_core_data *cd)
{
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL),
	};
	return pt_pip1_send_output_and_wait_(cd, &hid_output);
}

/*******************************************************************************
 * FUNCTION: pt_pip_null
 *
 * SUMMARY: Wrapper function for pt_pip_null_ that guarantees exclusive access.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_pip_null(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_null_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

static void pt_stop_wd_timer(struct pt_core_data *cd);
/*******************************************************************************
 * FUNCTION: pt_pip_start_bootloader_
 *
 * SUMMARY: Sends the HID command start_bootloader [PIP cmd 0x01] to the DUT
 *
 *	NOTE: The WD MUST be stopped/restarted by the calling Function. Having
 *        the WD active could cause this function to fail!
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static int pt_pip_start_bootloader_(struct pt_core_data *cd)
{
	int rc = 0;

	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_START_BOOTLOADER),
		.timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT,
		.reset_expected = 1,
	};
	if (cd->watchdog_enabled) {
		pt_debug(cd->dev, DL_WARN,
			"%s: watchdog isn't stopped before enter bl\n",
			__func__);
		goto exit;
	}

	/* Reset startup status after entering BL, new DUT enum required */
	cd->startup_status = STARTUP_STATUS_START;
	pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Start BL PIP cmd failed. rc = %d\n",
			__func__, rc);
	}

exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_start_bootloader
 *
 * SUMMARY: Protected function to force DUT to enter the BL
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int pt_pip_start_bootloader(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_start_bootloader_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_start_bl
 *
 * SUMMARY: Function pointer included in core_nonhid_cmds to allow other
 *	modules to request the DUT to enter the BL
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev     - pointer to device structure
 *	 protect - flag to run in protected mode
 ******************************************************************************/
static int _pt_request_pip_start_bl(struct device *dev, int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_start_bootloader(cd);

	return pt_pip_start_bootloader_(cd);
}

/*******************************************************************************
 * FUNCTION: pt_pip2_ver_load_ttdata
 *
 * SUMMARY: Function to load the Version information from the PIP2 VERSION
 *	command into the core data struct.
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *      *cd  - pointer to core data structure
 *	 len - Length of data in response_buf
 ******************************************************************************/
static void pt_pip2_ver_load_ttdata(struct pt_core_data *cd, u16 len)
{
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
	struct pt_pip2_version_full *full_ver;
	struct pt_pip2_version      *ver;

	/*
	 * The PIP2 VERSION command can return different lengths of data.
	 * The additional LOT fields are included when the packet
	 * size is >= 29 bytes. Older FW sends a reduced packet size.
	 * NOTE:
	 *  - The FW would swap the BL and FW versions when reporting
	 * the small packet.
	 *  - Sub Lot bytes 16 and 17 are reserved.
	 */
	if (len >= 0x1D) {
		full_ver = (struct pt_pip2_version_full *)
			&cd->response_buf[PIP2_RESP_STATUS_OFFSET];

		ttdata->pip_ver_major = full_ver->pip2_version_msb;
		ttdata->pip_ver_minor = full_ver->pip2_version_lsb;
		ttdata->bl_ver_major  = full_ver->bl_version_msb;
		ttdata->bl_ver_minor  = full_ver->bl_version_lsb;
		ttdata->fw_ver_major  = full_ver->fw_version_msb;
		ttdata->fw_ver_minor  = full_ver->fw_version_lsb;
		/*
		 * BL PIP 2.02 and greater the version fields are
		 * swapped
		 */
		if (ttdata->pip_ver_major >= 2 && ttdata->pip_ver_minor >= 2) {
			ttdata->chip_rev =
			    get_unaligned_le16(&full_ver->chip_rev);
			ttdata->chip_id =
			    get_unaligned_le16(&full_ver->chip_id);
		} else {
			ttdata->chip_rev =
			    get_unaligned_le16(&full_ver->chip_id);
			ttdata->chip_id =
			    get_unaligned_le16(&full_ver->chip_rev);
		}
		memcpy(ttdata->uid, full_ver->uid, PT_UID_SIZE);

		pt_pr_buf(cd->dev, DL_INFO, (u8 *)full_ver,
			sizeof(struct pt_pip2_version_full),
			"PIP2 VERSION FULL");
	} else {
		ver = (struct pt_pip2_version *)
			&cd->response_buf[PIP2_RESP_STATUS_OFFSET];

		ttdata->pip_ver_major = ver->pip2_version_msb;
		ttdata->pip_ver_minor = ver->pip2_version_lsb;
		ttdata->bl_ver_major  = ver->bl_version_msb;
		ttdata->bl_ver_minor  = ver->bl_version_lsb;
		ttdata->fw_ver_major  = ver->fw_version_msb;
		ttdata->fw_ver_minor  = ver->fw_version_lsb;
		ttdata->chip_rev = get_unaligned_le16(&ver->chip_rev);
		ttdata->chip_id  = get_unaligned_le16(&ver->chip_id);

		pt_pr_buf(cd->dev, DL_INFO, (u8 *)ver,
			sizeof(struct pt_pip2_version), "PIP2 VERSION");
	}
}

/*******************************************************************************
 * FUNCTION: pt_si_get_ttdata
 *
 * SUMMARY: Function to load the version information from the system information
 *	PIP command into the core data struct.
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static void pt_si_get_ttdata(struct pt_core_data *cd)
{
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
	struct pt_ttdata_dev *ttdata_dev =
		(struct pt_ttdata_dev *)
		&cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];

	ttdata->pip_ver_major = ttdata_dev->pip_ver_major;
	ttdata->pip_ver_minor = ttdata_dev->pip_ver_minor;
	ttdata->bl_ver_major = ttdata_dev->bl_ver_major;
	ttdata->bl_ver_minor = ttdata_dev->bl_ver_minor;
	ttdata->fw_ver_major = ttdata_dev->fw_ver_major;
	ttdata->fw_ver_minor = ttdata_dev->fw_ver_minor;

	ttdata->fw_pid = get_unaligned_le16(&ttdata_dev->fw_pid);
	ttdata->fw_ver_conf = get_unaligned_le16(&ttdata_dev->fw_ver_conf);
	ttdata->post_code = get_unaligned_le16(&ttdata_dev->post_code);
	ttdata->revctrl = get_unaligned_le32(&ttdata_dev->revctrl);
	ttdata->jtag_id_l = get_unaligned_le16(&ttdata_dev->jtag_si_id_l);
	ttdata->jtag_id_h = get_unaligned_le16(&ttdata_dev->jtag_si_id_h);

	memcpy(ttdata->mfg_id, ttdata_dev->mfg_id, PT_NUM_MFGID);

	pt_pr_buf(cd->dev, DL_INFO, (u8 *)ttdata_dev,
		sizeof(struct pt_ttdata_dev), "sysinfo_ttdata");
}

/*******************************************************************************
 * FUNCTION: pt_si_get_sensing_conf_data
 *
 * SUMMARY: Function to load the sensing information from the system information
 *	PIP command into the core data struct.
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static void pt_si_get_sensing_conf_data(struct pt_core_data *cd)
{
	struct pt_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data;
	struct pt_sensing_conf_data_dev *scd_dev =
		(struct pt_sensing_conf_data_dev *)
		&cd->response_buf[PIP1_SYSINFO_SENSING_OFFSET];

	scd->electrodes_x = scd_dev->electrodes_x;
	scd->electrodes_y = scd_dev->electrodes_y;
	scd->origin_x = scd_dev->origin_x;
	scd->origin_y = scd_dev->origin_y;

	/* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */
	if (scd->origin_x & 0x02) {
		scd->tx_num = scd->electrodes_x;
		scd->rx_num = scd->electrodes_y;
	} else {
		scd->tx_num = scd->electrodes_y;
		scd->rx_num = scd->electrodes_x;
	}
	/*
	 * When the Panel ID is coming from an XY pin and not a dedicated
	 * GPIO, store the PID in pid_for_loader. This cannot be done for all
	 * other DUTs as the loader will use cd->pid_for_loader to generate
	 * the bin file name but will ignore it if pid_for_loader is still
	 * set to PANEL_ID_NOT_ENABLED
	 */
	if (cd->panel_id_support &
		(PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
		mutex_lock(&cd->system_lock);
		cd->pid_for_loader = scd_dev->panel_id;
		mutex_unlock(&cd->system_lock);
	}

	scd->panel_id = scd_dev->panel_id;
	scd->btn = scd_dev->btn;
	scd->scan_mode = scd_dev->scan_mode;
	scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;

	scd->res_x = get_unaligned_le16(&scd_dev->res_x);
	scd->res_y = get_unaligned_le16(&scd_dev->res_y);
	scd->max_z = get_unaligned_le16(&scd_dev->max_z);
	scd->len_x = get_unaligned_le16(&scd_dev->len_x);
	scd->len_y = get_unaligned_le16(&scd_dev->len_y);

	pt_pr_buf(cd->dev, DL_INFO, (u8 *)scd_dev,
		sizeof(struct pt_sensing_conf_data_dev),
		"sensing_conf_data");
}

/*******************************************************************************
 * FUNCTION: pt_si_setup
 *
 * SUMMARY: Setup the xy_data and xy_mode by allocating the needed memory
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int pt_si_setup(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	int max_tch = si->sensing_conf_data.max_tch;

	if (!si->xy_data)
		si->xy_data = kzalloc(max_tch * si->desc.tch_record_size,
				GFP_KERNEL);
	if (!si->xy_data)
		return -ENOMEM;

	if (!si->xy_mode)
		si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL);
	if (!si->xy_mode) {
		kfree(si->xy_data);
		return -ENOMEM;
	}

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_si_get_btn_data
 *
 * SUMMARY: Setup the core data button information based on the response of the
 *	System Information PIP command.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int pt_si_get_btn_data(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	int num_btns = 0;
	int num_defined_keys;
	u16 *key_table;
	int btn;
	int i;
	int rc = 0;
	unsigned int btns = cd->response_buf[PIP1_SYSINFO_BTN_OFFSET]
		& PIP1_SYSINFO_BTN_MASK;
	size_t btn_keys_size;

	pt_debug(cd->dev, DL_INFO, "%s: get btn data\n", __func__);

	for (i = 0; i < PIP1_SYSINFO_MAX_BTN; i++) {
		if (btns & (1 << i))
			num_btns++;
	}
	si->num_btns = num_btns;

	if (num_btns) {
		btn_keys_size = num_btns * sizeof(struct pt_btn);
		if (!si->btn)
			si->btn = kzalloc(btn_keys_size, GFP_KERNEL);
		if (!si->btn)
			return -ENOMEM;

		if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS] == NULL)
			num_defined_keys = 0;
		else if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS]->data == NULL)
			num_defined_keys = 0;
		else
			num_defined_keys = cd->cpdata->sett
				[PT_IC_GRPNUM_BTN_KEYS]->size;

		for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) {
			key_table = (u16 *)cd->cpdata->sett
				[PT_IC_GRPNUM_BTN_KEYS]->data;
			si->btn[btn].key_code = key_table[btn];
			si->btn[btn].enabled = true;
		}
		for (; btn < num_btns; btn++) {
			si->btn[btn].key_code = KEY_RESERVED;
			si->btn[btn].enabled = true;
		}

		return rc;
	}

	kfree(si->btn);
	si->btn = NULL;
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_si_put_log_data
 *
 * SUMMARY: Prints all sys info data to kmsg log
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static void pt_si_put_log_data(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	struct pt_ttdata *ttdata = &si->ttdata;
	struct pt_sensing_conf_data *scd = &si->sensing_conf_data;
	int i;

	pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_major = 0x%02X (%d)\n",
		__func__, ttdata->pip_ver_major, ttdata->pip_ver_major);
	pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_minor = 0x%02X (%d)\n",
		__func__, ttdata->pip_ver_minor, ttdata->pip_ver_minor);
	pt_debug(cd->dev, DL_DEBUG, "%s: fw_pid = 0x%04X (%d)\n",
		__func__, ttdata->fw_pid, ttdata->fw_pid);
	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_major = 0x%02X (%d)\n",
		__func__, ttdata->fw_ver_major, ttdata->fw_ver_major);
	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_minor = 0x%02X (%d)\n",
		__func__, ttdata->fw_ver_minor, ttdata->fw_ver_minor);
	pt_debug(cd->dev, DL_DEBUG, "%s: revctrl = 0x%08X (%d)\n",
		__func__, ttdata->revctrl, ttdata->revctrl);
	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_conf = 0x%04X (%d)\n",
		__func__, ttdata->fw_ver_conf, ttdata->fw_ver_conf);
	pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_major = 0x%02X (%d)\n",
		__func__, ttdata->bl_ver_major, ttdata->bl_ver_major);
	pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_minor = 0x%02X (%d)\n",
		__func__, ttdata->bl_ver_minor, ttdata->bl_ver_minor);
	pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_h = 0x%04X (%d)\n",
		__func__, ttdata->jtag_id_h, ttdata->jtag_id_h);
	pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_l = 0x%04X (%d)\n",
		__func__, ttdata->jtag_id_l, ttdata->jtag_id_l);

	for (i = 0; i < PT_NUM_MFGID; i++)
		pt_debug(cd->dev, DL_DEBUG,
			"%s: mfg_id[%d] = 0x%02X (%d)\n",
			__func__, i, ttdata->mfg_id[i],
			ttdata->mfg_id[i]);

	pt_debug(cd->dev, DL_DEBUG, "%s: post_code = 0x%04X (%d)\n",
		__func__, ttdata->post_code, ttdata->post_code);
	pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_x = 0x%02X (%d)\n",
		__func__, scd->electrodes_x, scd->electrodes_x);
	pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_y = 0x%02X (%d)\n",
		__func__, scd->electrodes_y, scd->electrodes_y);
	pt_debug(cd->dev, DL_DEBUG, "%s: len_x = 0x%04X (%d)\n",
		__func__, scd->len_x, scd->len_x);
	pt_debug(cd->dev, DL_DEBUG, "%s: len_y = 0x%04X (%d)\n",
		__func__, scd->len_y, scd->len_y);
	pt_debug(cd->dev, DL_DEBUG, "%s: res_x = 0x%04X (%d)\n",
		__func__, scd->res_x, scd->res_x);
	pt_debug(cd->dev, DL_DEBUG, "%s: res_y = 0x%04X (%d)\n",
		__func__, scd->res_y, scd->res_y);
	pt_debug(cd->dev, DL_DEBUG, "%s: max_z = 0x%04X (%d)\n",
		__func__, scd->max_z, scd->max_z);
	pt_debug(cd->dev, DL_DEBUG, "%s: origin_x = 0x%02X (%d)\n",
		__func__, scd->origin_x, scd->origin_x);
	pt_debug(cd->dev, DL_DEBUG, "%s: origin_y = 0x%02X (%d)\n",
		__func__, scd->origin_y, scd->origin_y);
	pt_debug(cd->dev, DL_DEBUG, "%s: panel_id = 0x%02X (%d)\n",
		__func__, scd->panel_id, scd->panel_id);
	pt_debug(cd->dev, DL_DEBUG, "%s: btn =0x%02X (%d)\n",
		__func__, scd->btn, scd->btn);
	pt_debug(cd->dev, DL_DEBUG, "%s: scan_mode = 0x%02X (%d)\n",
		__func__, scd->scan_mode, scd->scan_mode);
	pt_debug(cd->dev, DL_DEBUG,
		"%s: max_num_of_tch_per_refresh_cycle = 0x%02X (%d)\n",
		__func__, scd->max_tch, scd->max_tch);
	pt_debug(cd->dev, DL_DEBUG, "%s: xy_mode = %p\n",
		__func__, si->xy_mode);
	pt_debug(cd->dev, DL_DEBUG, "%s: xy_data = %p\n",
		__func__, si->xy_data);
}

/*******************************************************************************
 * FUNCTION: pt_get_sysinfo_regs
 *
 * SUMMARY: Setup all the core data System information based on the response
 *	of the System Information PIP command.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int pt_get_sysinfo_regs(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	int rc;

	rc = pt_si_get_btn_data(cd);
	if (rc < 0)
		return rc;

	pt_si_get_ttdata(cd);

	pt_si_get_sensing_conf_data(cd);

	pt_si_setup(cd);

	pt_si_put_log_data(cd);

	si->ready = true;
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_free_si_ptrs
 *
 * SUMMARY: Frees all memory associated with the System Information within
 *	core data
 *
 * RETURN: n/a
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static void pt_free_si_ptrs(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;

	kfree(si->btn);
	kfree(si->xy_mode);
	kfree(si->xy_data);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_get_sysinfo_
 *
 * SUMMARY: Sends the PIP Get SYS INFO command to the DUT and waits for the
 *	response.
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd)
{
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
		.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	/* Parse the sysinfo data */
	rc = pt_get_sysinfo_regs(cd);
	if (rc)
		pt_free_si_ptrs(cd);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_get_sysinfo
 *
 * SUMMARY: Protected call to pt_hid_output_get_sysinfo_
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_hid_output_get_sysinfo(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_get_sysinfo_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_suspend_scanning_
 *
 * SUMMARY: Sends the PIP Suspend Scanning command to the DUT
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_pip_suspend_scanning_(struct pt_core_data *cd)
{
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SUSPEND_SCANNING),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Suspend Scan PIP cmd failed. rc = %d\n",
			__func__, rc);
	}
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_suspend_scanning
 *
 * SUMMARY: Protected wrapper for calling pt_hid_output_suspend_scanning_
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_pip_suspend_scanning(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_suspend_scanning_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_suspend_scanning
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_suspend_scanning
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev - pointer to device structure
 *	 protect - 0 = call non-protected function
 *		   1 = call protected function
 ******************************************************************************/
static int _pt_request_pip_suspend_scanning(struct device *dev,
		int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_suspend_scanning(cd);

	return pt_pip_suspend_scanning_(cd);
}

/*******************************************************************************
 * FUNCTION: pt_pip_resume_scanning_
 *
 * SUMMARY: Sends the PIP Resume Scanning command to the DUT
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_pip_resume_scanning_(struct pt_core_data *cd)
{
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RESUME_SCANNING),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Resume Scan PIP cmd failed. rc = %d\n",
			__func__, rc);
	}
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_resume_scanning
 *
 * SUMMARY: Protected wrapper for calling pt_pip_resume_scanning_
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data structure
 ******************************************************************************/
static int pt_pip_resume_scanning(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_resume_scanning_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_resume_scanning
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_resume_scanning
 *
 * RETURN::
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev - pointer to device structure
 *	 protect - 0 = call non-protected function
 *		   1 = call protected function
 ******************************************************************************/
static int _pt_request_pip_resume_scanning(struct device *dev,
		int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_resume_scanning(cd);

	return pt_pip_resume_scanning_(cd);
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_param_
 *
 * SUMMARY: Sends a PIP command 0x05 Get Parameter to the DUT and returns
 *	the 32bit parameter value
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 param_id - parameter ID to retrieve
 *	*value    - value of DUT parameter
 ******************************************************************************/
static int pt_pip_get_param_(struct pt_core_data *cd,
		u8 param_id, u32 *value)
{
	int write_length = 1;
	u8 param[1] = { param_id };
	u8 read_param_id;
	int param_size;
	u8 *ptr;
	int rc = 0;
	int i;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_PARAM),
		.write_length = write_length,
		.write_buf = param,
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	read_param_id = cd->response_buf[5];
	if (read_param_id != param_id)
		return -EPROTO;

	param_size = cd->response_buf[6];
	ptr = &cd->response_buf[7];
	*value = 0;
	for (i = 0; i < param_size; i++)
		*value += ptr[i] << (i * 8);
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_param
 *
 * SUMMARY: Protected call to pt_hid_output_get_param_ by a request exclusive
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 param_id - parameter ID to retrieve
 *	*value    - value of DUT parameter
 ******************************************************************************/
static int pt_pip_get_param(struct pt_core_data *cd,
		u8 param_id, u32 *value)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_get_param_(cd, param_id, value);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_get_param
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_get_param
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev      - pointer to device structure
 *	 protect  - flag to call protected or non protected function
 *	 param_id - parameter ID to retrieve
 *	*value    - value of DUT parameter
 ******************************************************************************/
int _pt_request_pip_get_param(struct device *dev,
		int protect, u8 param_id, u32 *value)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_get_param(cd, param_id, value);

	return pt_pip_get_param_(cd, param_id, value);
}

/*******************************************************************************
 * FUNCTION: pt_pip_set_param_
 *
 * SUMMARY: Sends a PIP command 0x06 Set Parameter to the DUT writing the
 *	passed in value to flash
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 param_id - parameter ID to set
 *	 value    - value to write
 *	 size     - size to write
 ******************************************************************************/
static int pt_pip_set_param_(struct pt_core_data *cd,
		u8 param_id, u32 value, u8 size)
{
	u8 write_buf[6];
	u8 *ptr = &write_buf[2];
	int rc = 0;
	int i;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM),
		.write_buf = write_buf,
	};
	write_buf[0] = param_id;
	write_buf[1] = size;
	for (i = 0; i < size; i++) {
		ptr[i] = value & 0xFF;
		value = value >> 8;
	}

	hid_output.write_length = 2 + size;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (param_id != cd->response_buf[5] || size != cd->response_buf[6])
		return -EPROTO;

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_set_param
 *
 * SUMMARY: Protected call to pt_hid_output_set_param_ by a request exclusive
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 param_id - parameter ID to set
 *	 value    - value to write
 *	 size     - size to write
 ******************************************************************************/
static int pt_pip_set_param(struct pt_core_data *cd,
		u8 param_id, u32 value, u8 size)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_set_param_(cd, param_id, value, size);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_set_param
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_set_param
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev      - pointer to device structure
 *	 protect  - flag to call protected or non-protected
 *	 param_id - parameter ID to set
 *	 value    - value to write
 *	 size     - size to write
 ******************************************************************************/
int _pt_request_pip_set_param(struct device *dev, int protect,
	u8 param_id, u32 value, u8 size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_set_param(cd, param_id, value, size);

	return pt_pip_set_param_(cd, param_id, value, size);
}

/*******************************************************************************
 * FUNCTION: _pt_pip_enter_easywake_state_
 *
 * SUMMARY: Sends a PIP command 0x09 Enter EasyWake State to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 data        - easywake guesture (Only used for PIP1.6 and earlier)
 *	*return_data - return status if easywake was entered
 ******************************************************************************/
static int pt_hid_output_enter_easywake_state_(
		struct pt_core_data *cd, u8 data, u8 *return_data)
{
	int write_length = 1;
	u8 param[1] = { data };
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_ENTER_EASYWAKE_STATE),
		.write_length = write_length,
		.write_buf = param,
	};
	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	*return_data = cd->response_buf[5];
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_verify_config_block_crc_
 *
 * SUMMARY: Sends the PIP "Verify Data Block CRC" (0x20) command to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd             - pointer the core data structure
 *	 ebid           - enumerated block ID
 *	*status         - PIP command status
 *	 calculated_crc - calculated CRC
 *	 stored_crc     - stored CRC in config area
 ******************************************************************************/
static int pt_pip_verify_config_block_crc_(
		struct pt_core_data *cd, u8 ebid, u8 *status,
		u16 *calculated_crc, u16 *stored_crc)
{
	int write_length = 1;
	u8 param[1] = { ebid };
	u8 *ptr;
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
		.write_length = write_length,
		.write_buf = param,
	};
	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	ptr = &cd->response_buf[5];
	*status = ptr[0];
	*calculated_crc = get_unaligned_le16(&ptr[1]);
	*stored_crc = get_unaligned_le16(&ptr[3]);
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_verify_config_block_crc
 *
 * SUMMARY: Protected call to pt_hid_output_verify_config_block_crc_() within
 *	an exclusive access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd             - pointer the core data structure
 *	 ebid           - enumerated block ID
 *	*status         - PIP command status
 *	 calculated_crc - calculated CRC
 *	 stored_crc     - stored CRC in config area
 ******************************************************************************/
static int pt_pip_verify_config_block_crc(
		struct pt_core_data *cd, u8 ebid, u8 *status,
		u16 *calculated_crc, u16 *stored_crc)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_verify_config_block_crc_(cd, ebid, status,
			calculated_crc, stored_crc);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_verify_config_block_crc
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip_verify_config_block_crc_
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev            - pointer to device structure
 *	 protect        - flag to call protected or non-protected
 *	 ebid           - enumerated block ID
 *	*status         - PIP command status
 *	 calculated_crc - calculated CRC
 *	 stored_crc     - stored CRC in config area
 ******************************************************************************/
static int _pt_request_pip_verify_config_block_crc(
		struct device *dev, int protect, u8 ebid, u8 *status,
		u16 *calculated_crc, u16 *stored_crc)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_verify_config_block_crc(cd, ebid,
				status, calculated_crc, stored_crc);

	return pt_pip_verify_config_block_crc_(cd, ebid,
			status, calculated_crc, stored_crc);
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_config_row_size_
 *
 * SUMMARY: Sends the PIP "Get Data Row Size" (0x21) command to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 protect  - flag to call protected or non-protected
 *	*row_size - pointer to store the retrieved row size
 ******************************************************************************/
static int pt_pip_get_config_row_size_(struct pt_core_data *cd,
		u16 *row_size)
{
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_CONFIG_ROW_SIZE),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	*row_size = get_unaligned_le16(&cd->response_buf[5]);
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_config_row_size
 *
 * SUMMARY: Protected call to pt_hid_output_get_config_row_size_ within
 *	an exclusive access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	 protect  - flag to call protected or non-protected
 *	*row_size - pointer to store the retrieved row size
 ******************************************************************************/
static int pt_pip_get_config_row_size(struct pt_core_data *cd,
		u16 *row_size)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_get_config_row_size_(cd, row_size);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_get_config_row_size
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip_get_config_row_size_
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev      - pointer to device structure
 *	 protect  - flag to call protected or non-protected
 *	*row_size - pointer to store the retrieved row size
 ******************************************************************************/
static int _pt_request_pip_get_config_row_size(struct device *dev,
		int protect, u16 *row_size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_get_config_row_size(cd, row_size);

	return pt_pip_get_config_row_size_(cd, row_size);
}

/*******************************************************************************
 * FUNCTION: pt_pip1_read_data_block_
 *
 * SUMMARY: Sends the PIP "Read Data Block" (0x22) command to the DUT and print
 *  output data to the "read_buf" and update "crc".
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 row_number      - row number
 *	 length          - length of data to read
 *	 ebid            - block id
 *	*actual_read_len - Actual data length read
 *	*read_buf        - pointer to the buffer to store read data
 *	 read_buf_size   - size of read_buf
 *	*crc             - pointer to store CRC of row data
 ******************************************************************************/
static int pt_pip1_read_data_block_(struct pt_core_data *cd,
		u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
		u8 *read_buf, u16 read_buf_size, u16 *crc)
{
	int read_ebid;
	int status;
	int rc = 0;
	int write_length = 5;
	u8 write_buf[5];
	u8 cmd_offset = 0;
	u16 calc_crc;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_READ_DATA_BLOCK),
		.write_length = write_length,
		.write_buf = write_buf,
	};

	write_buf[cmd_offset++] = LOW_BYTE(row_number);
	write_buf[cmd_offset++] = HI_BYTE(row_number);
	write_buf[cmd_offset++] = LOW_BYTE(length);
	write_buf[cmd_offset++] = HI_BYTE(length);
	write_buf[cmd_offset++] = ebid;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	status = cd->response_buf[5];
	if (status)
		return status;

	read_ebid = cd->response_buf[6];
	if ((read_ebid != ebid) || (cd->response_buf[9] != 0))
		return -EPROTO;

	*actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
	if (length == 0 || *actual_read_len == 0)
		return 0;

	if (read_buf_size >= *actual_read_len &&
		*actual_read_len < PT_MAX_PIP2_MSG_SIZE)
		memcpy(read_buf, &cd->response_buf[10], *actual_read_len);
	else
		return -EPROTO;

	*crc = get_unaligned_le16(&cd->response_buf[*actual_read_len + 10]);

	/* Validate Row Data CRC */
	calc_crc = _pt_compute_crc(read_buf, *actual_read_len);
	if (*crc == calc_crc) {
		return 0;
	} else {
		pt_debug(cd->dev, DL_ERROR,
			"%s: CRC Mismatch packet=0x%04X calc=0x%04X\n",
			__func__, *crc, calc_crc);
		return -EPROTO;
	}
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_read_data_block
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to pt_pip1_read_data_block_
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 row_number      - row number
 *	 length          - length of data to read
 *	 ebid            - block id
 *	*actual_read_len - Actual data length read
 *	*read_buf        - pointer to the buffer to store read data
 *	*crc             - pointer to store CRC of row data
 ******************************************************************************/
static int _pt_request_pip_read_data_block(struct device *dev,
		u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
		u8 *read_buf, u16 read_buf_size, u16 *crc)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return pt_pip1_read_data_block_(cd, row_number, length,
			ebid, actual_read_len, read_buf, read_buf_size, crc);
}

/*******************************************************************************
 * FUNCTION: pt_pip1_write_data_block_
 *
 * SUMMARY: Sends the PIP "Write Data Block" (0x23) command to the DUT and
 *  write data to the data block.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd               - pointer to core data
 *   row_number       - row in config block to write to
 *   write_length     - length of data to write
 *   ebid             - enumerated block ID
 *  *write_buf        - pointer to buffer to write
 *  *security_key     - pointer to security key to allow write
 *  *actual_write_len - pointer to store data length actually written
 ******************************************************************************/
static int pt_pip1_write_data_block_(struct pt_core_data *cd,
		u16 row_number, u16 write_length, u8 ebid, u8 *write_buf,
		u8 *security_key, u16 *actual_write_len)
{
	/* row_number + write_len + ebid + security_key + crc */
	u16 full_write_length = 2 + 2 + 1 + write_length + 8 + 2;
	u8 *full_write_buf;
	u8 cmd_offset = 0;
	u16 crc;
	int status;
	int rc = 0;
	int read_ebid;
	u8 *data;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_WRITE_DATA_BLOCK),
		.write_length = full_write_length,
		.timeout_ms = PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT,
	};

	if (write_length > PT_CAL_DATA_ROW_SIZE)
		return -EINVAL;

	full_write_buf = kzalloc(full_write_length, GFP_KERNEL);
	if (!full_write_buf)
		return -ENOMEM;

	hid_output.write_buf = full_write_buf;
	full_write_buf[cmd_offset++] = LOW_BYTE(row_number);
	full_write_buf[cmd_offset++] = HI_BYTE(row_number);
	full_write_buf[cmd_offset++] = LOW_BYTE(write_length);
	full_write_buf[cmd_offset++] = HI_BYTE(write_length);
	full_write_buf[cmd_offset++] = ebid;
	data = &full_write_buf[cmd_offset];
	memcpy(data, write_buf, write_length);
	cmd_offset += write_length;
	memcpy(&full_write_buf[cmd_offset], security_key, 8);
	cmd_offset += 8;
	crc = _pt_compute_crc(data, write_length);
	full_write_buf[cmd_offset++] = LOW_BYTE(crc);
	full_write_buf[cmd_offset++] = HI_BYTE(crc);

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		goto exit;

	status = cd->response_buf[5];
	if (status) {
		rc = -EINVAL;
		goto exit;
	}

	read_ebid = cd->response_buf[6];
	if (read_ebid != ebid) {
		rc = -EPROTO;
		goto exit;
	}

	*actual_write_len = get_unaligned_le16(&cd->response_buf[7]);

exit:
	kfree(full_write_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_write_data_block
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip1_write_data_block_
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev              - pointer to device structure
 *	 protect          - flag to call protected or non-protected
 *	 row_number       - row in config block to write to
 *	 write_length     - length of data to write
 *	 ebid             - enumerated block ID
 *	*write_buf        - pointer to buffer to write
 *	*security_key     - pointer to security key to allow write
 *	*actual_write_len - pointer to store data length actually written
 ******************************************************************************/
static int _pt_request_pip_write_data_block(struct device *dev,
		u16 row_number, u16 write_length, u8 ebid,
		u8 *write_buf, u8 *security_key, u16 *actual_write_len)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return pt_pip1_write_data_block_(cd, row_number,
			write_length, ebid, write_buf, security_key,
			actual_write_len);
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_data_structure_
 *
 * SUMMARY: Sends the PIP "Retrieve Data Structure" (0x24) command to the DUT
 *	returning a structure of data defined by data_id
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 data_id         - data ID to read
 *	*status          - pointer to store the read response status
 *	*data_format     - pointer to store format of data read
 *	*actual_read_len - pointer to store data length actually read
 *	*data            - pointer to store data read
 ******************************************************************************/
static int pt_pip_get_data_structure_(
		struct pt_core_data *cd, u16 read_offset, u16 read_length,
		u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
		u8 *data)
{
	int rc = 0;
	u16 total_read_len = 0;
	u16 read_len;
	u16 off_buf = 0;
	u8 write_buf[5];
	u8 read_data_id;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_DATA_STRUCTURE),
		.write_length = 5,
		.write_buf = write_buf,
	};

again:
	write_buf[0] = LOW_BYTE(read_offset);
	write_buf[1] = HI_BYTE(read_offset);
	write_buf[2] = LOW_BYTE(read_length);
	write_buf[3] = HI_BYTE(read_length);
	write_buf[4] = data_id;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS)
		goto set_status;

	read_data_id = cd->response_buf[6];
	if (read_data_id != data_id)
		return -EPROTO;

	read_len = get_unaligned_le16(&cd->response_buf[7]);
	if (read_len && data) {
		memcpy(&data[off_buf], &cd->response_buf[10], read_len);

		total_read_len += read_len;

		if (read_len < read_length) {
			read_offset += read_len;
			off_buf += read_len;
			read_length -= read_len;
			goto again;
		}
	}

	if (data_format)
		*data_format = cd->response_buf[9];
	if (actual_read_len)
		*actual_read_len = total_read_len;
set_status:
	if (status)
		*status = cd->response_buf[5];

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_get_data_structure
 *
 * SUMMARY: Protected call to pt_hid_output_get_data_structure within
 *	an exclusive access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 data_id         - data ID to read
 *	*status          - pointer to store the read response status
 *	*data_format     - pointer to store format of data read
 *	*actual_read_len - pointer to store data length actually read
 *	*data            - pointer to store data read
 ******************************************************************************/
static int pt_pip_get_data_structure(
		struct pt_core_data *cd, u16 read_offset, u16 read_length,
		u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
		u8 *data)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_get_data_structure_(cd, read_offset,
			read_length, data_id, status, data_format,
			actual_read_len, data);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_get_data_structure
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip_get_data_structure
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to call protected or non-protected
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 data_id         - data ID to read
 *	*status          - pointer to store the read response status
 *	*data_format     - pointer to store format of data read
 *	*actual_read_len - pointer to store data length actually read
 *	*data            - pointer to store data read
 ******************************************************************************/
static int _pt_request_pip_get_data_structure(struct device *dev,
		int protect, u16 read_offset, u16 read_length, u8 data_id,
		u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_get_data_structure(cd,
				read_offset, read_length, data_id, status,
				data_format, actual_read_len, data);

	return pt_pip_get_data_structure_(cd,
			read_offset, read_length, data_id, status,
			data_format, actual_read_len, data);
}

/*******************************************************************************
 * FUNCTION: _pt_manage_local_cal_data
 *
 * SUMMARY: This function manages storing or restoring a copy of the Firmware
 *	CALIBRATION data. It stores it in a local static array and can be
 *	cleared, loaded or used to restore the CAL data back to the running FW.
 *	The CAL data is read or restored by use of the PIP1 commands:
 *	   - READ_DATA_BLOCK  (0x22)
 *	   - WRITE_DATA_BLOCK (0x23)
 *
 * RETURN:
 *       0 = success
 *      !0 = failure
 *
 * PARAMETERS:
 *      *dev    - pointer to device structure
 *       action - One of the following actions:
 *                  - PT_CAL_DATA_SAVE
 *                  - PT_CAL_DATA_RESTORE
 *                  - PT_CAL_DATA_CLEAR
 *                  - PT_CAL_DATA_SIZE
 *      *size   - pointer to the number of bytes transferred
 *	*crc    - pointer to Chip ID CRC that the CAL data was retrieved from
 ******************************************************************************/
static int _pt_manage_local_cal_data(struct device *dev, u8 action, u16 *size,
	unsigned short *crc)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
	unsigned short calc_id_crc = 0;
	static u8 *cal_cache_data;
	static u16 cal_cache_len;
	static unsigned short cal_cache_chip_id;
	int rc = 0;
	u8 *tmp_data = NULL;
	u8 row_number = 0;
	u8 prefix[20];
	u16 cal_size = 0;
	u16 transfer_size;
	u16 act_trans_len = 0;
	u16 byte_offset = 0;
	u16 cal_blk_size;
	u16 total_rows;
	u16 remain_bytes;
	u16 data_block_crc;
	u16 buf_size = 12;

	pt_debug(dev, DL_INFO, "%s: ATM - CAL Cache action=%d\n",
		__func__, action);
	switch (action) {
	case PT_CAL_DATA_SAVE:
		/* Read the size of the CAL block and calculate # rows */
		tmp_data = kzalloc(buf_size, GFP_KERNEL);
		if (!tmp_data) {
			rc = -ENOMEM;
			goto exit;
		}
		/*
		 * Don't check rc as doing a read size will give a false
		 * error on the CRC check.
		 */
		rc = pt_pip1_read_data_block_(cd, row_number, 0, PT_CAL_EBID,
			&act_trans_len, tmp_data, buf_size, &data_block_crc);
		cal_blk_size = act_trans_len;
		kfree(tmp_data);

		pt_debug(dev, DL_INFO,
			"%s: CAL Cache size=%d FW CAL Size=%d\n",
			__func__, cal_cache_len, cal_blk_size);

		/* Safety net to ensure we didn't read incorrect size */
		if (cal_blk_size > PT_CAL_DATA_MAX_SIZE) {
			pt_debug(dev, DL_ERROR, "%s: Alloc struct Failed\n",
				__func__);
			rc = 1;
			goto exit;
		}

		/* Panels could have diff CAL sizes, Re-allocate the cache */
		if (cal_blk_size != cal_cache_len) {
			kfree(cal_cache_data);
			cal_cache_data = kzalloc(cal_blk_size + 2,
						GFP_KERNEL);
			if (!cal_cache_data) {
				rc = -ENOMEM;
				goto exit;
			}

			pt_debug(dev, DL_INFO, "%s: CAL Cache Allocated\n",
				__func__);
		}
		memset(&cal_cache_data[0], 0, cal_blk_size + 2);

		/* Calculate how many rows [0-n] (PIP Transactions) */
		total_rows = (cal_blk_size / PT_CAL_DATA_ROW_SIZE) - 1;
		remain_bytes = cal_blk_size % PT_CAL_DATA_ROW_SIZE;
		/* Add row if we have a last partial row */
		if (remain_bytes > 0)
			total_rows++;
		pt_debug(dev, DL_INFO,
			"%s: CAL size=%d rows=[0-%d] partial row bytes=%d\n",
			__func__, cal_blk_size, total_rows, remain_bytes);

		/* Read all rows unless an error occurs */
		rc = 0;
		while (rc == 0 && row_number <= total_rows) {
			act_trans_len = 0;
			if (remain_bytes > 0 && row_number == total_rows)
				transfer_size = remain_bytes;
			else
				transfer_size = PT_CAL_DATA_ROW_SIZE;

			rc = pt_pip1_read_data_block_(cd, row_number,
				transfer_size, PT_CAL_EBID,
				&act_trans_len,
				&cal_cache_data[byte_offset], cal_blk_size + 2,
				&data_block_crc);
			if (rc) {
				/* Error occurred, exit loop */
				cal_size = 0;
				break;
			}

			pt_debug(dev, DL_INFO,
				"%s: CAL read rc=%d actual read len=%d\n",
				__func__, rc, act_trans_len);

			byte_offset += act_trans_len;
			cal_size = byte_offset;

			scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
			pt_pr_buf(dev, DL_INFO,
				&cal_cache_data[byte_offset - act_trans_len],
				act_trans_len, prefix);

			row_number++;
		}

		if (cal_size > 0) {
			/* Save a CRC of the chip info the CAL was saved from */
			calc_id_crc = crc_ccitt_calculate(
				(u8 *)&ttdata->chip_rev, 4 + PT_UID_SIZE);
			cal_cache_chip_id = calc_id_crc;
			cal_cache_len = cal_size;
			pt_debug(dev, DL_INFO,
				"%s: CAL Cache: CRC=0x%04X Total Size=%d\n",
				__func__, calc_id_crc, cal_size);
		}

		*size = cal_size;
		*crc = calc_id_crc;
		break;
	case PT_CAL_DATA_RESTORE:
		cal_size = cal_cache_len;
		while ((rc == 0) && (byte_offset < cal_size)) {
			if (cal_size - byte_offset > PT_CAL_DATA_ROW_SIZE)
				transfer_size = PT_CAL_DATA_ROW_SIZE;
			else
				transfer_size = cal_size - byte_offset;

			rc = pt_pip1_write_data_block_(cd, row_number,
				transfer_size, PT_CAL_EBID,
				&cal_cache_data[byte_offset],
				(u8 *)pt_data_block_security_key,
				&act_trans_len);

			byte_offset += act_trans_len;
			pt_debug(dev, DL_INFO, "%s: CAL write byte offset=%d\n",
				__func__, byte_offset);

			scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
			pt_pr_buf(dev, DL_INFO,
				&cal_cache_data[byte_offset - act_trans_len],
				act_trans_len, prefix);


			if ((byte_offset > cal_size) ||
			    (act_trans_len != transfer_size))
				rc = -EIO;
			row_number++;
		}
		*size = byte_offset;
		*crc = cal_cache_chip_id;
		break;
	case PT_CAL_DATA_CLEAR:
		if (cal_cache_data)
			memset(&cal_cache_data[0], 0, cal_cache_len);
		cal_cache_len = 0;
		cal_cache_chip_id = 0;
		*size = 0;
		*crc = 0;
		break;
	case PT_CAL_DATA_INFO:
	default:
		*size = cal_cache_len;
		*crc = cal_cache_chip_id;
		pt_debug(dev, DL_INFO,
			"%s: CAL Cache: CRC=%04X Total Size=%d\n",
			__func__, cal_cache_chip_id,
			cal_cache_len);
		break;
	}

exit:
	pt_debug(dev, DL_INFO,
		"%s: CAL Cache exit: rc=%d CRC=0x%04X Total Size=%d\n",
		__func__, rc, *crc, *size);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_run_selftest_
 *
 * SUMMARY: Sends the PIP "Run Self Test" (0x26) command to the DUT
 *	to execute a FW built in self test
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd                   - pointer to core data
 *	 test_id              - enumerated test ID to run
 *	 write_idacs_to_flash - flag whether to write new IDACS to flash
 *	*status               - pointer to store the read response status
 *	*summary_results      - pointer to store the results summary
 *	*results_available    - pointer to store if results are available
 *****************************************************************************/
static int pt_pip_run_selftest_(
		struct pt_core_data *cd, u8 test_id,
		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
		u8 *results_available)
{
	int rc = 0;
	u8 write_buf[2];
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RUN_SELF_TEST),
		.write_length = 2,
		.write_buf = write_buf,
		.timeout_ms = PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT,
	};

	write_buf[0] = test_id;
	write_buf[1] = write_idacs_to_flash;

	if (cd->active_dut_generation == DUT_PIP2_CAPABLE)
		hid_output.write_length = 1;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (status)
		*status = cd->response_buf[5];
	if (summary_result)
		*summary_result = cd->response_buf[6];
	/* results_available only available before PIP 1.03 */
	if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 3)) {
		if (results_available)
			*results_available = cd->response_buf[7];
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_run_selftest
 *
 * SUMMARY: Protected call to pt_hid_output_run_selftest within
 *	an exclusive access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd                   - pointer to core data
 *	 test_id              - enumerated test ID to run
 *	 write_idacs_to_flash - flag whether to write new IDACS to flash
 *	*status               - pointer to store the read response status
 *	*summary_results      - pointer to store the results summary
 *	*results_available    - pointer to store if results are available
 ******************************************************************************/
static int pt_pip_run_selftest(
		struct pt_core_data *cd, u8 test_id,
		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
		u8 *results_available)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_run_selftest_(cd, test_id,
			write_idacs_to_flash, status, summary_result,
			results_available);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_run_selftest
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_run_selftest
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev                  - pointer to device structure
 *	 protect              - flag to call protected or non-protected
 *	 test_id              - enumerated test ID to run
 *	 write_idacs_to_flash - flag whether to write new IDACS to flash
 *	*status               - pointer to store the read response status
 *	*summary_results      - pointer to store the results summary
 *	*results_available    - pointer to store if results are available
 ******************************************************************************/
static int _pt_request_pip_run_selftest(struct device *dev,
		int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status,
		u8 *summary_result, u8 *results_available)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_run_selftest(cd, test_id,
				write_idacs_to_flash, status, summary_result,
				results_available);

	return pt_pip_run_selftest_(cd, test_id,
			write_idacs_to_flash, status, summary_result,
			results_available);
}

/*******************************************************************************
 * FUNCTION: _pt_pip_get_selftest_result_
 *
 * SUMMARY: Sends the PIP "Get Self Test Results" (0x27) command to the DUT
 *	to retrieve the self test results from the self test already executed
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 test_id         - enumerated test ID to read selftest results from
 *	*status          - pointer to store the read response status
 *	*actual_read_len - pointer to store data length actually read
 *	*status          - pointer to where the cmd response statas is stored
 ******************************************************************************/
static int pt_pip_get_selftest_result_(
		struct pt_core_data *cd, u16 read_offset, u16 read_length,
		u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
{
	int rc = 0;
	u16 total_read_len = 0;
	u16 read_len;
	u16 off_buf = 0;
	u8 write_buf[5];
	u8 read_test_id;
	bool repeat;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SELF_TEST_RESULT),
		.write_length = 5,
		.write_buf = write_buf,
	};

	/*
	 * Do not repeat reading for Auto Shorts test
	 * when PIP version < 1.3
	 */
	repeat = IS_PIP_VER_GE(&cd->sysinfo, 1, 3)
			|| test_id != PT_ST_ID_AUTOSHORTS;

again:
	write_buf[0] = LOW_BYTE(read_offset);
	write_buf[1] = HI_BYTE(read_offset);
	write_buf[2] = LOW_BYTE(read_length);
	write_buf[3] = HI_BYTE(read_length);
	write_buf[4] = test_id;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS)
		goto set_status;

	read_test_id = cd->response_buf[6];
	if (read_test_id != test_id)
		return -EPROTO;

	read_len = get_unaligned_le16(&cd->response_buf[7]);
	if (read_len && data) {
		memcpy(&data[off_buf], &cd->response_buf[10], read_len);

		total_read_len += read_len;

		if (repeat && read_len < read_length) {
			read_offset += read_len;
			off_buf += read_len;
			read_length -= read_len;
			goto again;
		}
	}

	if (actual_read_len)
		*actual_read_len = total_read_len;
set_status:
	if (status)
		*status = cd->response_buf[5];

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_pip_get_selftest_result
 *
 * SUMMARY: Protected call to pt_hid_output_get_selftest_result by exclusive
 *	access to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd              - pointer to core data
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 test_id         - enumerated test ID to read selftest results from
 *	*status          - pointer to store the read response status
 *	*actual_read_len - pointer to store data length actually read
 *	*status          - pointer to where the cmd response statas is stored
 ******************************************************************************/
static int pt_pip_get_selftest_result(
		struct pt_core_data *cd, u16 read_offset, u16 read_length,
		u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_get_selftest_result_(cd, read_offset,
		read_length, test_id, status, actual_read_len, data);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_get_selftest_result
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_get_selftest_result
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to call protected or non-protected
 *	 read_offset     - read pointer offset
 *	 read_length     - length of data to read
 *	 test_id         - enumerated test ID to read selftest results from
 *	*status          - pointer to store the read response status
 *	*actual_read_len - pointer to store data length actually read
 *	*data            - pointer to where the data read is stored
 ******************************************************************************/
static int _pt_request_pip_get_selftest_result(struct device *dev,
		int protect, u16 read_offset, u16 read_length, u8 test_id,
		u8 *status, u16 *actual_read_len, u8 *data)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_get_selftest_result(cd, read_offset,
				read_length, test_id, status, actual_read_len,
				data);

	return pt_pip_get_selftest_result_(cd, read_offset,
			read_length, test_id, status, actual_read_len,
			data);
}

/*******************************************************************************
 * FUNCTION: _pt_pip_load_self_test_param
 *
 * SUMMARY: Sends the PIP "Load Self Test Parameters" (0x25) command to the DUT
 *	to load parameters needed by a self test
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd           - pointer to core data
 *	 self_test_id - enumerated test ID for which the parmeters belong
 *	 load_offset  - mem offset to where to load parameters
 *	 load_length  - length of parameter data to load
 *	*parameters   - pointer to list of parameter data
 *	*status       - pointer to store the response status
 *	*ret_test_id  - pointer to returned test id the parameters were stored
 *	*act_load_len - pointer to store the actual load length that was writen
 ******************************************************************************/
static int pt_pip_load_self_test_param_(struct pt_core_data *cd,
		u8 self_test_id, u16 load_offset, u16 load_length,
		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
	int rc = 0;
	int i;
	u8 write_buf[PT_MAX_PIP1_MSG_SIZE];
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_LOAD_SELF_TEST_PARAM),
		.write_length = 5 + load_length,
		.write_buf = write_buf,
		.timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT,
	};

	write_buf[0] = LOW_BYTE(load_offset);
	write_buf[1] = HI_BYTE(load_offset);
	write_buf[2] = LOW_BYTE(load_length);
	write_buf[3] = HI_BYTE(load_length);
	write_buf[4] = self_test_id;
	for (i = 0; i < load_length; i++)
		write_buf[i + 5] = parameters[i];

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (status)
		*status = cd->response_buf[5];
	if (ret_test_id)
		*ret_test_id = cd->response_buf[6];
	if (act_load_len)
		*act_load_len = get_unaligned_le16(&cd->response_buf[7]);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip_load_self_test_param
 *
 * SUMMARY: Protected call to pt_pip_load_self_test_param_ within an exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd           - pointer to core data
 *	 self_test_id - enumerated test ID for which the parmeters belong
 *	 load_offset  - mem offset to where to load parameters
 *	 load_length  - length of parameter data to load
 *	*parameters   - pointer to list of parameter data
 *	*status       - pointer to store the response status
 *	*ret_test_id  - pointer to returned test id the parameters were stored
 *	*act_load_len - pointer to store the actual load length that was writen
 ******************************************************************************/
static int pt_pip_load_self_test_param(struct pt_core_data *cd,
		u8 self_test_id, u16 load_offset, u16 load_length,
		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
		load_length, parameters, status, ret_test_id, act_load_len);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_load_self_test_param
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip_load_self_test_param
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev          - pointer to device structure
 *	 protect      - flag to call protected or non-protected
 *	 self_test_id - enumerated test ID for which the parmeters belong
 *	 load_offset  - mem offset to where to load parameters
 *	 load_length  - length of parameter data to load
 *	*parameters   - pointer to list of parameter data
 *	*status       - pointer to store the response status
 *	*ret_test_id  - pointer to returned test id the parameters were stored
 *	*act_load_len - pointer to store the actual load length that was writen
 ******************************************************************************/
static int _pt_request_pip_load_self_test_param(struct device *dev,
		int protect, u8 self_test_id, u16 load_offset, u16 load_length,
		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_load_self_test_param(cd, self_test_id,
		load_offset, load_length, parameters, status, ret_test_id,
		act_load_len);

	return pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
		load_length, parameters, status, ret_test_id, act_load_len);
}

/*******************************************************************************
 * FUNCTION: pt_pip_calibrate_ext_
 *
 * SUMMARY: Send the PIP1 Extended Calibrate command (0x30) to the DUT waiting
 *	for the response
 *
 * NOTE: This calibrate command requires the DUT to support PIP version >= 1.10
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	*cal_data - pointer to extended calibration data structure
 *	*status   - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_pip_calibrate_ext_(struct pt_core_data *cd,
		struct pt_cal_ext_data *cal_data, u8 *status)
{
	int rc = 0;
	int write_length = 4;
	u8 write_buf[4];
	u16 size = 0;
	unsigned short crc = 0;

	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED),
		.write_length = write_length,
		.write_buf = write_buf,
		.timeout_ms = PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT,
	};

	if (cal_data == NULL)
		return -EINVAL;

	memcpy(write_buf, cal_data, sizeof(struct pt_cal_ext_data));

	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	if (status)
		*status = cd->response_buf[5];

	/*
	 * When doing a calibration on a flashless DUT, save CAL data in
	 * the TTDL cache on any successful calibration
	 */
	if (cd->response_buf[5] == 0 && cd->cal_cache_in_host) {
		pt_debug(cd->dev, DL_INFO, "%s: Retrieve and Save CAL\n",
			__func__);
		rc = _pt_manage_local_cal_data(cd->dev, PT_CAL_DATA_SAVE,
			&size, &crc);
		if (rc)
			pt_debug(cd->dev, DL_ERROR,
				"%s: Error Saving CAL rc=%d\n", __func__, rc);
		else
			pt_debug(cd->dev, DL_INFO,
				"%s: Saved CAL: chip ID=0x%04X size=%d\n",
				__func__, crc, size);
	}

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_calibrate_ext
 *
 * SUMMARY: Protected call to pt_pip_calibrate_ext_ by exclusive access to the
 *	DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	*cal_data - pointer to extended calibration data structure
 *	*status   - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_pip_calibrate_ext(struct pt_core_data *cd,
		struct pt_cal_ext_data *cal_data, u8 *status)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_calibrate_ext_(cd, cal_data, status);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_calibrate_ext
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_calibrate_ext
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev     -  pointer to device structure
 *	 protect -  flag to call protected or non-protected
 *	*cal_data - pointer to extended calibration data structure
 *	*status   - pointer to where the command response status is stored
 ******************************************************************************/
static int _pt_request_pip_calibrate_ext(struct device *dev,
		int protect, struct pt_cal_ext_data *cal_data, u8 *status)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_calibrate_ext(cd, cal_data, status);

	return pt_pip_calibrate_ext_(cd, cal_data, status);
}

/*******************************************************************************
 * FUNCTION: pt_pip_calibrate_idacs_
 *
 * SUMMARY: Send the PIP Calibrate IDACs command (0x28) to the DUT waiting
 *	for the response
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd     - pointer to core data
 *	 mode   - sense mode to calibrate (0-5)
 *	*status - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_pip_calibrate_idacs_(struct pt_core_data *cd,
		u8 mode, u8 *status)
{
	int rc = 0;
	int write_length = 1;
	u8 write_buf[1];
	u8 cmd_offset = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_IDACS),
		.write_length = write_length,
		.write_buf = write_buf,
		.timeout_ms = PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT,
	};
	write_buf[cmd_offset++] = mode;
	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	*status = cd->response_buf[5];
	if (*status)
		return -EINVAL;

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip_calibrate_idacs
 *
 * SUMMARY: Protected call to pt_hid_output_calibrate_idacs_ by exclusive
 *	access to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd     - pointer to core data
 *	 mode   - sense mode to calibrate (0-5)
 *	*status - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_pip_calibrate_idacs(struct pt_core_data *cd,
		u8 mode, u8 *status)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip_calibrate_idacs_(cd, mode, status);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_calibrate_idacs
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to pt_pip_calibrate_idacs
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev     - pointer to device structure
 *	 protect - flag to call protected or non-protected
 *	 mode    - sense mode to calibrate (0-5)
 *	*status  - pointer to where the command response status is stored
 ******************************************************************************/
static int _pt_request_pip_calibrate_idacs(struct device *dev,
		int protect, u8 mode, u8 *status)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip_calibrate_idacs(cd, mode, status);

	return pt_pip_calibrate_idacs_(cd, mode, status);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_initialize_baselines_
 *
 * SUMMARY: Send the PIP "Initialize Baselines" command (0x29) to the DUT
 *  waiting for the response.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *   test_id  - bit type flag to allow initialize baseline MUT,BTN,SELG
 *              each or together with a single command.
 *  *status   - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_hid_output_initialize_baselines_(
		struct pt_core_data *cd, u8 test_id, u8 *status)
{
	int rc = 0;
	int write_length = 1;
	u8 write_buf[1];
	u8 cmd_offset = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_INITIALIZE_BASELINES),
		.write_length = write_length,
		.write_buf = write_buf,
	};
	write_buf[cmd_offset++] = test_id;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	*status = cd->response_buf[5];
	if (*status)
		return -EINVAL;

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_initialize_baselines
 *
 * SUMMARY: Protected call to pt_hid_output_initialize_baselines_ by exclusive
 *  access to the DUT
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *   test_id  - enumerated ID against which to initialize the baseline
 *  *status   - pointer to where the command response status is stored
 ******************************************************************************/
static int pt_hid_output_initialize_baselines(struct pt_core_data *cd,
		u8 test_id, u8 *status)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_initialize_baselines_(cd, test_id, status);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_initialize_baselines
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip_initialize_baselines
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev     - pointer to device structure
 *	 protect - flag to call protected or non-protected
 *	 test_id - enumerated ID against which to initialize the baseline
 *	*status  - pointer to where the command response status is stored
 ******************************************************************************/
static int _pt_request_pip_initialize_baselines(struct device *dev,
		int protect, u8 test_id, u8 *status)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_initialize_baselines(cd, test_id,
				status);

	return pt_hid_output_initialize_baselines_(cd, test_id, status);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_exec_panel_scan_
 *
 * SUMMARY: Sends the PIP "Execute Panel Scan" (0x2A) to the DUT and waits for
 *	the response
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd        - pointer to core data
 ******************************************************************************/
static int pt_hid_output_exec_panel_scan_(struct pt_core_data *cd)
{
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_EXEC_PANEL_SCAN),
	};

	return pt_pip1_send_output_and_wait_(cd, &hid_output);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_exec_panel_scan
 *
 * SUMMARY: Protected call to pt_hid_output_exec_panel_scan_ by exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd        - pointer to core data
 ******************************************************************************/
static int pt_hid_output_exec_panel_scan(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_exec_panel_scan_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_exec_panel_scan_
 *
 * SUMMARY: Send the PIP2 "Execute Panel Scan" (0x21) to the DUT and waits for
 *	the response
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd        - pointer to core data
 *	 scan_type - type of panel scan to perform (PIP2 only)
 ******************************************************************************/
static int pt_pip2_exec_panel_scan_(struct pt_core_data *cd, u8 scan_type)
{
	int rc = 0;
	u8  data[2];
	u8  read_buf[10];
	u16 actual_read_len;

	pt_debug(cd->dev, DL_DEBUG, "%s: PIP2 Execute Scan %d\n",
		__func__, scan_type);
	data[0] = scan_type;
	rc = _pt_request_pip2_send_cmd(cd->dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_EXECUTE_SCAN,
		data, 1, read_buf, &actual_read_len);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s EXECUTE_SCAN command for type %d failed. rc=%d\n",
			__func__, scan_type, rc);
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_exec_panel_scan
 *
 * SUMMARY: Protected call to pt_pip2_exec_panel_scan_ by exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd        - pointer to core data
 *	 scan_type - type of panel scan to perform (PIP2 only)
 ******************************************************************************/
static int pt_pip2_exec_panel_scan(struct pt_core_data *cd, u8 scan_type)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip2_exec_panel_scan_(cd, scan_type);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_exec_panel_scan
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip2_exec_panel_scan or pt_hid_output_exec_panel_scan
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev       - pointer to device structure
 *	 protect   - flag to call protected or non-protected
 *	 scan_type - type of panel scan to perform (PIP2 only)
 ******************************************************************************/
static int _pt_request_pip_exec_panel_scan(struct device *dev,
		int protect, u8 scan_type)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 12)) {
		if (protect)
			return pt_pip2_exec_panel_scan(cd, scan_type);

		return pt_pip2_exec_panel_scan_(cd, scan_type);
	}

	if (protect)
		return pt_hid_output_exec_panel_scan(cd);

	return pt_hid_output_exec_panel_scan_(cd);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_retrieve_panel_scan_
 *
 * SUMMARY: Sends the PIP "Retrieve Panel Scan" (0x2B) command to the DUT
 *  to retrieve the specified data type for a the last successful Execute
 *  Panel Scan command.
 *
 * RETURN:
 *    0 = success
 *   !0 = failure
 *
 * PARAMETERS:
 *  *dev             - pointer to device structure
 *   protect         - flag to call protected or non-protected
 *   read_offset     - read pointer offset
 *   read_count      - length of data to read
 *   data_id         - enumerated test ID to read selftest results from
 *  *response        - pointer to store the read response status
 *  *config          - pointer to store config data
 *  *actual_read_len - pointer to store data length actually read
 *  *read_buf        - pointer to the read buffer
 ******************************************************************************/
static int pt_hid_output_retrieve_panel_scan_(
		struct pt_core_data *cd, u16 read_offset, u16 read_count,
		u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
		u8 *read_buf)
{
	int status;
	u8 read_data_id;
	int rc = 0;
	int write_length = 5;
	u8 write_buf[5];
	u8 cmd_offset = 0;
	u8 data_elem_size;
	int size;
	int data_size;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RETRIEVE_PANEL_SCAN),
		.write_length = write_length,
		.write_buf = write_buf,
	};

	write_buf[cmd_offset++] = LOW_BYTE(read_offset);
	write_buf[cmd_offset++] = HI_BYTE(read_offset);
	write_buf[cmd_offset++] = LOW_BYTE(read_count);
	write_buf[cmd_offset++] = HI_BYTE(read_count);
	write_buf[cmd_offset++] = data_id;

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	status = cd->response_buf[5];
	if (status)
		return -EINVAL;

	read_data_id = cd->response_buf[6];
	if (read_data_id != data_id)
		return -EPROTO;

	size = get_unaligned_le16(&cd->response_buf[0]);
	*actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
	*config = cd->response_buf[9];

	data_elem_size = *config & 0x07;
	data_size = *actual_read_len * data_elem_size;

	if (read_buf)
		memcpy(read_buf, &cd->response_buf[10], data_size);
	if (response)
		memcpy(response, cd->response_buf, size);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_retrieve_panel_scan
 *
 * SUMMARY: Protected call to pt_hid_output_retrieve_panel_scan_ by exclusive
 *  access to the DUT.
 *
 * RETURN:
 *    0 = success
 *   !0 = failure
 *
 * PARAMETERS:
 *  *dev             - pointer to device structure
 *   protect         - flag to call protected or non-protected
 *   read_offset     - read pointer offset
 *   read_count      - length of data to read
 *   data_id         - enumerated test ID to read selftest results from
 *  *response        - pointer to store the read response status
 *  *config          - pointer to store config data
 *  *actual_read_len - pointer to store data length actually read
 *  *read_buf        - pointer to the read buffer
 ******************************************************************************/
static int pt_hid_output_retrieve_panel_scan(
		struct pt_core_data *cd, u16 read_offset, u16 read_count,
		u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
		u8 *read_buf)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_retrieve_panel_scan_(cd, read_offset,
			read_count, data_id, response, config,
			actual_read_len, read_buf);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_retrieve_panel_scan
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_hid_output_retrieve_panel_scan
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to call protected or non-protected
 *	 read_offset     - read pointer offset
 *	 read_count      - length of data to read
 *	 data_id         - enumerated test ID to read selftest results from
 *	*response        - pointer to store the read response status
 *	*config          - pointer to store config data
 *	*actual_read_len - pointer to store data length actually read
 *	*read_buf        - pointer to the read buffer
 ******************************************************************************/
static int _pt_request_pip_retrieve_panel_scan(struct device *dev,
		int protect, u16 read_offset, u16 read_count, u8 data_id,
		u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_retrieve_panel_scan(cd,
				read_offset, read_count, data_id, response,
				config, actual_read_len, read_buf);

	return pt_hid_output_retrieve_panel_scan_(cd,
			read_offset, read_count, data_id, response,
			config, actual_read_len, read_buf);
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_user_cmd
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_hid_output_user_cmd
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev             - pointer to device structure
 *	 protect         - flag to call protected or non-protected
 *	 read_len        - length of data to read
 *	*read_buf        - pointer to store read data
 *	 write_len       - length of data to write
 *	*write_buf       - pointer to buffer to write
 *	*actual_read_len - pointer to store data length actually read
 ******************************************************************************/
static int _pt_request_pip_user_cmd(struct device *dev,
		int protect, u16 read_len, u8 *read_buf, u16 write_len,
		u8 *write_buf, u16 *actual_read_len)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_user_cmd(cd, read_len, read_buf,
				write_len, write_buf, actual_read_len);

	return pt_hid_output_user_cmd_(cd, read_len, read_buf,
			write_len, write_buf, actual_read_len);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_get_information_
 *
 * SUMMARY: Sends the PIP "Get Bootloader Information" (0x38) command to the
 *  DUT to retrieve bootloader version and chip identification information.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd           - pointer to core data
 *  *return_data  - pointer to store the return data
 *****************************************************************************/
static int pt_hid_output_bl_get_information_(struct pt_core_data *cd,
		u8 *return_data)
{
	int rc;
	int data_len;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_INFO),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		return rc;

	data_len = get_unaligned_le16(&cd->input_buf[6]);
	if (!data_len)
		return -EPROTO;

	memcpy(return_data, &cd->response_buf[8], data_len);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_get_information
 *
 * SUMMARY: Protected call to pt_hid_output_bl_get_information_ by exclusive
 *  access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd           - pointer to core data
 *  *return_data  - pointer to store the return data
 ******************************************************************************/
static int pt_hid_output_bl_get_information(struct pt_core_data *cd,
		u8 *return_data)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_get_information_(cd, return_data);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_bl_get_information
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_hid_output_bl_get_information
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 protect     - flag to call protected or non-protected
 *	*return_data - pointer to store bl data
 ******************************************************************************/
static int _pt_request_pip_bl_get_information(struct device *dev,
		int protect, u8 *return_data)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_get_information(cd, return_data);

	return pt_hid_output_bl_get_information_(cd, return_data);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_initiate_bl_
 *
 * SUMMARY: Sends the PIP "Get Bootloader Information" (0x48) command to the
 *  DUT to erases the entire TrueTouch application, Configuration Data block,
 *  and Design Data block in flash and enables the host to execute the Program
 *  and Verify Row command to bootload the application image and data.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd               - pointer to core data
 *   protect          - flag to call protected or non-protected
 *   key_size         - size of key
 *  *key_buf          - pointer to key data to allow operation
 *   row_size         - size of the meta data row
 *  *metadata_row_buf - pointer to meta data to write
 ******************************************************************************/
static int pt_hid_output_bl_initiate_bl_(struct pt_core_data *cd,
		u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
{
	u16 write_length = key_size + row_size;
	u8 *write_buf;
	int rc = 0;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_INITIATE_BL),
		.write_length = write_length,
		.timeout_ms = PT_PIP1_CMD_INITIATE_BL_TIMEOUT,
	};

	write_buf = kzalloc(write_length, GFP_KERNEL);
	if (!write_buf)
		return -ENOMEM;

	hid_output.write_buf = write_buf;

	if (key_size)
		memcpy(write_buf, key_buf, key_size);

	if (row_size)
		memcpy(&write_buf[key_size], metadata_row_buf, row_size);

	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);

	kfree(write_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_initiate_bl
 *
 * SUMMARY: Protected call to pt_hid_output_bl_initiate_bl_ by exclusive
 *  access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd               - pointer to core data
 *   key_size         - size of key
 *  *key_buf          - pointer to key data to allow operation
 *   row_size         - size of the meta data row
 *  *metadata_row_buf - pointer to meta data to write
 ******************************************************************************/
static int pt_hid_output_bl_initiate_bl(struct pt_core_data *cd,
		u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
			row_size, metadata_row_buf);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_bl_initiate_bl
 *
 * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
 *	calls to the protected or unprotected call to
 *	pt_hid_output_bl_initiate_bl
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev              - pointer to device structure
 *	 protect          - flag to call protected or non-protected
 *	 key_size         - size of key
 *	*key_buf          - pointer to key data to allow operation
 *	 row_size         - size of the meta data row
 *	*metadata_row_buf - pointer to meta data to write
 ******************************************************************************/
static int _pt_request_pip_bl_initiate_bl(struct device *dev,
		int protect, u16 key_size, u8 *key_buf, u16 row_size,
		u8 *metadata_row_buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_initiate_bl(cd, key_size, key_buf,
				row_size, metadata_row_buf);

	return pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
			row_size, metadata_row_buf);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_program_and_verify_
 *
 * SUMMARY: Sends the PIP "Get Bootloader Information" (0x39) command to upload
 *  and program a 128-byte row into the flash, and then verifies written data.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *   data_len - length of data_buf
 *  *data_buf - firmware image to program
 ******************************************************************************/
static int pt_hid_output_bl_program_and_verify_(
		struct pt_core_data *cd, u16 data_len, u8 *data_buf)
{
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY),
		.write_length = data_len,
		.write_buf = data_buf,
		.timeout_ms = PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT,
	};

	return pt_pip1_send_output_and_wait_(cd, &hid_output);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_program_and_verify
 *
 * SUMMARY: Protected call to pt_hid_output_bl_program_and_verify_ by exclusive
 *  access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *   data_len - length of data_buf
 *  *data_buf - firmware image to program
 ******************************************************************************/
static int pt_hid_output_bl_program_and_verify(
		struct pt_core_data *cd, u16 data_len, u8 *data_buf)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_program_and_verify_(cd, data_len, data_buf);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_bl_program_and_verify
 *
 * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
 *	to request to have the BL program and verify a FW image
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev      - pointer to device structure
 *	 protect  - boolean to determine to call the protected function
 *	 data_len - length of data_buf
 *	*data_buf - firmware image to program
 ******************************************************************************/
static int _pt_request_pip_bl_program_and_verify(
		struct device *dev, int protect, u16 data_len, u8 *data_buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_program_and_verify(cd, data_len,
				data_buf);

	return pt_hid_output_bl_program_and_verify_(cd, data_len,
			data_buf);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_verify_app_integrity_
 *
 * SUMMARY: Sends the PIP "Get Bootloader Information" (0x31) command to
 *  perform a full verification of the application integrity by calculating the
 *  CRC of the image in flash and compare it to the expected CRC stored in the
 *  Metadata row.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd      - pointer to core data
 *  *result  - pointer to store result
 ******************************************************************************/
static int pt_hid_output_bl_verify_app_integrity_(
		struct pt_core_data *cd, u8 *result)
{
	int rc;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc) {
		*result = 0;
		return rc;
	}

	*result = cd->response_buf[8];
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_verify_app_integrity
 *
 * SUMMARY: Protected call to pt_hid_output_bl_verify_app_integrity_ by
 *  exclusive access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd      - pointer to core data
 *  *result  - pointer to store result
 ******************************************************************************/
static int pt_hid_output_bl_verify_app_integrity(
		struct pt_core_data *cd, u8 *result)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_verify_app_integrity_(cd, result);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_bl_verify_app_integrity
 *
 * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
 *	to request to have the BL verify the application integrity (PIP1.x only)
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev     - pointer to device structure
 *	 protect - boolean to determine to call the protected function
 *	*result  - pointer to store result
 ******************************************************************************/
static int _pt_request_pip_bl_verify_app_integrity(
		struct device *dev, int protect, u8 *result)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_verify_app_integrity(cd, result);

	return pt_hid_output_bl_verify_app_integrity_(cd, result);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_launch_app_
 *
 * SUMMARY: Sends the PIP "Launch Application" (0x3B) command to launch the
 *  application from bootloader (PIP1.x only).
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_hid_output_bl_launch_app_(struct pt_core_data *cd)
{
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_LAUNCH_APP),
		.reset_expected = 1,
	};

	return pt_pip1_send_output_and_wait_(cd, &hid_output);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_launch_app
 *
 * SUMMARY: Protected call to pt_hid_output_bl_launch_app_ by exclusive access
 *  to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_hid_output_bl_launch_app(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_launch_app_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_launch_app
 *
 * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
 *	to request to have the BL launch the application. (PIP1.x only)
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev      - pointer to device structure
 *	 protect  - boolean to determine to call the protected function
 ******************************************************************************/
static int _pt_request_pip_launch_app(struct device *dev,
		int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_launch_app(cd);

	return pt_hid_output_bl_launch_app_(cd);
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_get_panel_id_
 *
 * SUMMARY: Sends the PIP "Get Panel ID" (0x3E) command to return the Panel ID
 *  value store in the System Information.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *  *panel_id - pointer to where the panel ID will be stored
 ******************************************************************************/
static int pt_hid_output_bl_get_panel_id_(
		struct pt_core_data *cd, u8 *panel_id)
{
	int rc;
	struct pt_hid_output hid_output = {
		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_PANEL_ID),
	};

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Get Panel ID command not supported\n",
			__func__);
		*panel_id = PANEL_ID_NOT_ENABLED;
		return 0;
	} else if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Error on Get Panel ID command\n", __func__);
		return rc;
	}

	*panel_id = cd->response_buf[8];
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hid_output_bl_get_panel_id
 *
 * SUMMARY: Protected call to pt_hid_output_bl_get_panel_id_ by exclusive access
 *  to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd       - pointer to core data
 *  *panel_id - pointer to where the panel ID will be stored
 ******************************************************************************/
static int pt_hid_output_bl_get_panel_id(
		struct pt_core_data *cd, u8 *panel_id)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_hid_output_bl_get_panel_id_(cd, panel_id);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip_bl_get_panel_id
 *
 * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
 *	to have the BL retrieve the panel ID
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev      - pointer to device structure
 *	 protect  - flag to run in protected mode
 *	*panel_id - pointer to where the panel ID will be stored
 ******************************************************************************/
static int _pt_request_pip_bl_get_panel_id(
		struct device *dev, int protect, u8 *panel_id)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_hid_output_bl_get_panel_id(cd, panel_id);

	return pt_hid_output_bl_get_panel_id_(cd, panel_id);
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_mode_sysmode_
 *
 * SUMMARY: Determine the current mode and system mode of the DUT by use of the
 *	PIP2 STATUS command.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data structure
 *	*mode     - pointer to store the retrieved mode
 *	*sys_mode - pointer to store the FW system mode
 ******************************************************************************/
static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd,
		u8 *mode, u8 *sys_mode)
{
	u16 actual_read_len;
	u8 read_buf[12];
	u8 status, boot;
	int rc = 0;

	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
		PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len);

	pt_debug(cd->dev, DL_INFO, "%s: PIP2 STATUS command rc = %d\n",
		__func__, rc);

	if (!rc) {
		pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len,
			"PIP2 STATUS");
		status = read_buf[PIP2_RESP_STATUS_OFFSET];
		boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
		if (sys_mode) {
			if (status == PIP2_RSP_ERR_NONE &&
			    boot == PIP2_STATUS_APP_EXEC)
				*sys_mode = read_buf[PIP2_RESP_BODY_OFFSET + 1];
			else
				*sys_mode = FW_SYS_MODE_UNDEFINED;
		}
		if (mode) {
			if (status == PIP2_RSP_ERR_NONE &&
			    boot == PIP2_STATUS_BOOT_EXEC)
				*mode = PT_MODE_BOOTLOADER;
			else if (status == PIP2_RSP_ERR_NONE &&
				 boot == PIP2_STATUS_APP_EXEC)
				*mode = PT_MODE_OPERATIONAL;
			else
				*mode = PT_MODE_UNKNOWN;
		}
	} else {
		if (mode)
			*mode = PT_MODE_UNKNOWN;
		if (sys_mode)
			*sys_mode = FW_SYS_MODE_UNDEFINED;
		pt_debug(cd->dev, DL_WARN,
			"%s: Mode and sys_mode could not be determined\n",
			__func__);
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_mode_sysmode
 *
 * SUMMARY: Protected call to pt_pip2_get_mode_sysmode_ by exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data structure
 *	*mode     - pointer to store the retrieved mode
 *	*sys_mode - pointer to store the FW system mode
 ******************************************************************************/
static int pt_pip2_get_mode_sysmode(struct pt_core_data *cd,
			u8 *mode, u8 *sys_mode)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip2_get_mode_sysmode
 *
 * SUMMARY: Function pointer included in core_commands struct for external
 *	calls to the protected or unprotected call to
 *	pt_pip2_get_mode_sysmode
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev       - pointer to device structure
 *	*mode     - pointer to store the retrieved mode
 *	*sys_mode - pointer to store the FW system mode
 ******************************************************************************/
static int _pt_request_pip2_get_mode_sysmode(struct device *dev,
	int protect, u8 *mode, u8 *sys_mode)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_pip2_get_mode_sysmode(cd, mode, sys_mode);

	return pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);
}

/*******************************************************************************
 * FUNCTION: _pt_poll_for_fw_exit_boot_mode
 *
 * SUMMARY: Verify and or poll for the FW to exit BOOT mode. During the FW BOOT
 *	mode only the following PIP commands will be serviced, any other PIP
 *	command the FW will respond with an "Invalid PIP Command" response.
 *		- Get HID Descriptor (Register 0x0001, no command ID)
 *		- Reset (Register 0x0005, RESET HID request)
 *		- Ping (Register 0x0004, Command ID 0x00
 *		- Get System Information (Register 0x0004, Command ID 0x02)
 *		- PIP2 Status (Register 0x0101, Command ID 0x01)
 *		- PIP2 Version (Register 0x0101, Command ID 0x07)
 *	This function will loop on the results of the STATUS command until
 *	the FW reports it is out of BOOT mode.
 *
 *	NOTE:
 *	- This function will update cd->fw_system_mode
 *	- The STATUS cmd only supports this functionality for PIP 1.11+
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	timeout     - max time (ms) to wait for FW to exit BOOT mode
 *	actual_wait - pointer to actual time waited for FW to exit BOOT mode
 ******************************************************************************/
static int _pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
	int *actual_wait)
{
	int loop = 0;
	u8 sys_mode = cd->fw_system_mode;
	u8 pause = 10; /* in ms */
	int rc = 0;
	int max_loop = (timeout / pause) + 1; /* Add 1 due to int math */

	if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
		/*
		 * For PIP <1.11, no support for polling wait so do a hard
		 * coded wait and assume the FW is out of BOOT. Added 1 to
		 * timeout to make it clear in kmsg if non polling was done.
		 */
		*actual_wait = PT_FW_EXIT_BOOT_MODE_TIMEOUT + 1;
		pt_debug(cd->dev, DL_ERROR,
			"%s: PIP %d.%d no support for ext STATUS, sleep %d\n",
			__func__,
			cd->sysinfo.ttdata.pip_ver_major,
			cd->sysinfo.ttdata.pip_ver_minor, *actual_wait);
		msleep(*actual_wait);
		sys_mode = FW_SYS_MODE_SCANNING;
	}

	if (sys_mode == FW_SYS_MODE_BOOT) {
		while (!rc && loop <= max_loop &&
		      (sys_mode == FW_SYS_MODE_BOOT)) {
			loop++;
			usleep_range(9000, pause * 1000);
			rc = pt_pip2_get_mode_sysmode_(cd, NULL, &sys_mode);
			pt_debug(cd->dev, DL_DEBUG,
				"%s: FW in BOOT mode-sleep %dms, sys_mode=%d\n",
				__func__, loop * pause, sys_mode);
		}
		*actual_wait = (int)(loop * pause);
		pt_debug(cd->dev, DL_WARN,
			"%s: FW exited BOOT mode in %dms, sys_mode=%d\n",
			__func__, *actual_wait, sys_mode);

		if (rc)
			sys_mode = FW_SYS_MODE_UNDEFINED;
		else if (sys_mode == FW_SYS_MODE_BOOT ||
			 sys_mode == FW_SYS_MODE_UNDEFINED)
			rc = -EBUSY;
	}

	mutex_lock(&cd->system_lock);
	cd->fw_system_mode = sys_mode;
	mutex_unlock(&cd->system_lock);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_poll_for_fw_exit_boot_mode
 *
 * SUMMARY: Protected call to _pt_poll_for_fw_exit_boot_mode by exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	timeout     - max time (ms) to wait for FW to exit BOOT mode
 *	actual_wait - pointer to actual time waited for FW to exit BOOT mode
 ******************************************************************************/
static int pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
	int *actual_wait)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = _pt_poll_for_fw_exit_boot_mode(cd, timeout, actual_wait);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_get_fw_sys_mode
 *
 * SUMMARY: Determine the FW system mode. For PIP 1.11+ the
 * PIP2 STATUS command is used to directly query the FW system mode. For older
 * PIP versions, there is no direct PIP commamnd that will directly provide this
 * information but any PIP command above 0x1F requires scanning to be disabled
 * before it will be operational. If scanning was not disabled before sending
 * these PIP commands the FW will respond with a 6 byte error response. So to
 * safely determine the scanning state, a PIP message that will not affect the
 * operation of the FW was chosen:
 * "Verify Data Block CRC (ID 0x20)" is sent and if a 6 byte error code is
 * received scanning is active.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd        - pointer to core data
 *      *sys_mode  - pointer to FW System mode
 *      *mode      - pointer to mode (BL/FW)
 ******************************************************************************/
static int _pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
{
	int write_length = 1;
	int report_length;
	int rc = 0;
	u8 tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
	u8 tmp_mode = PT_MODE_UNKNOWN;
	u8 param[1] = { PT_TCH_PARM_EBID };
	struct pt_hid_output hid_output = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
		.write_length = write_length,
		.write_buf = param,
		.novalidate = true,
	};

	/* AFter PIP1.11 the preferred method is using STATUS cmd */
	if (IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
		rc = pt_pip2_get_mode_sysmode_(cd, &tmp_mode, &tmp_sys_mode);
		pt_debug(cd->dev, DL_DEBUG, "%s: tmp_sys_mode=%d tmp_mode=%d\n",
			__func__, tmp_sys_mode, tmp_mode);
		if (!rc) {
			if (tmp_mode != PT_MODE_OPERATIONAL)
				tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
		}
		goto exit;
	}

	/* Older systems use PIP1 CONFIG_BLOCK_CRC to best determine sys_mode */
	if (cd->mode != PT_MODE_OPERATIONAL) {
		tmp_mode = cd->mode;
		goto exit;
	}

	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
	if (rc)
		goto exit;

	report_length = (cd->response_buf[1] << 8) | (cd->response_buf[0]);
	if ((report_length == 0x06) &&
	    ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == 0x00) &&
	    (cd->response_buf[5] == PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
		tmp_mode = PIP2_STATUS_APP_EXEC;
		tmp_sys_mode = FW_SYS_MODE_SCANNING;
	} else if ((report_length == 0x0A) &&
		   ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) ==
		    PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
		tmp_mode = PIP2_STATUS_APP_EXEC;
		tmp_sys_mode = FW_SYS_MODE_TEST;
	}

exit:
	if (mode)
		*mode = tmp_mode;
	if (sys_mode)
		*sys_mode = tmp_sys_mode;
	pt_debug(cd->dev, DL_INFO, "%s: Return Mode=%d sys_mode=%d\n",
		__func__, tmp_mode, tmp_sys_mode);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_get_fw_sys_mode
 *
 * SUMMARY: Protected call to _pt_get_fw_sys_mode() to determine if FW scanning
 * is active or not.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data
 *	*sys_mode - pointer to fw system mode
 *	*mode     - pointer to mode
 ******************************************************************************/
static int pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = _pt_get_fw_sys_mode(cd, sys_mode, mode);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_get_fw_sys_mode
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request to get scan state
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev      - pointer to device structure
 *	 protect  - flag to call protected or non-protected
 *	*sys_mode - pointer to FW system mode
 *	*mode     - pointer to mode
 ******************************************************************************/
static int _pt_request_get_fw_sys_mode(struct device *dev, int protect,
		u8 *sys_mode, u8 *mode)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	if (protect)
		return pt_get_fw_sys_mode(cd, sys_mode, mode);

	return _pt_get_fw_sys_mode(cd, sys_mode, mode);
}

/* Default hid descriptor to provide basic register map */
const struct pt_hid_desc hid_desc_default = {
	230, /* hid_desc_len */
	HID_APP_REPORT_ID, /* packet_id */
	0x00, /* reserved_byte */
	0x0100, /* bcd_version */
	0x00EC, /* report_desc_len */
	0x0002, /* report_desc_register */
	0x0003, /* input_register */
	0x00FE, /* max_input_len */
	0x0004, /* output_register */
	0x00FE, /* max_output_len */
	0x0005, /* command_register */
	0x0006, /* data_register */
	0x04B4, /* vendor_id */
	0xC101, /* product_id */
	0x0100, /* version_id */
	{0x00, 0x00, 0x00, 0x00} /* reserved[4] */
};

/*******************************************************************************
 * FUNCTION: pt_init_hid_descriptor
 *
 * SUMMARY: Setup default values for HID descriptor structure
 *
 *
 * PARAMETERS:
 *	*desc - pointer to the HID descriptor data read back from DUT
 ******************************************************************************/
static inline void pt_init_hid_descriptor(struct pt_hid_desc *desc)
{
	memcpy(desc, &hid_desc_default, sizeof(hid_desc_default));
}

/*******************************************************************************
 * FUNCTION: pt_get_hid_descriptor_
 *
 * SUMMARY: Send the get HID descriptor command to the DUT and load the response
 *	into the HID descriptor structure
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	*desc - pointer to the HID descriptor data read back from DUT
 ******************************************************************************/
static int pt_get_hid_descriptor_(struct pt_core_data *cd,
		struct pt_hid_desc *desc)
{
	struct device *dev = cd->dev;
	int rc = 0;
	int t;
	u8 cmd[2];

	/*
	 * During startup the HID descriptor is required for all future
	 * processing. If IRQ is already asserted due to an early touch report
	 * the report must be cleared before sending command.
	 */
	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);

	/* Read HID descriptor length and version */
	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = 1;
	mutex_unlock(&cd->system_lock);

	/* Set HID descriptor register */
	memcpy(cmd, &cd->hid_core.hid_desc_register,
		sizeof(cd->hid_core.hid_desc_register));

	pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer [%zu]",
		__func__, sizeof(cmd));
	pt_pr_buf(cd->dev, DL_DEBUG, cmd, sizeof(cmd), ">>> Get HID Desc");
	rc = pt_adap_write_read_specific(cd, 2, cmd, NULL);
	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: failed to get HID descriptor, rc=%d\n",
			__func__, rc);
		goto error;
	}

	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
		msecs_to_jiffies(PT_GET_HID_DESCRIPTOR_TIMEOUT));

	if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
		cd->bus_transmit_error_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: HID get descriptor timed out\n", __func__);
		rc = -ETIME;
		goto error;
	} else {
		cd->hw_detected = true;
	}

	/* Load the HID descriptor including all registers */
	memcpy((u8 *)desc, cd->response_buf, sizeof(struct pt_hid_desc));

	/* Check HID descriptor length and version */
	pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__,
		le16_to_cpu(desc->hid_desc_len),
		le16_to_cpu(desc->bcd_version));

	if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) ||
		le16_to_cpu(desc->bcd_version) != HID_VERSION) {
		pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n",
			__func__);
		return -ENODEV;
	}

	goto exit;

error:
	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = 0;
	mutex_unlock(&cd->system_lock);
exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_get_hid_descriptor
 *
 * SUMMARY: Protected call to pt_get_hid_descriptor_()
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	*desc - pointer to the HID descriptor data read back from DUT
 ******************************************************************************/
static int pt_get_hid_descriptor(struct pt_core_data *cd,
		struct pt_hid_desc *desc)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_get_hid_descriptor_(cd, desc);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_version_
 *
 * SUMMARY: Sends a PIP2 VERSION command to the DUT and stores the data in
 *	cd-ttdata
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static int pt_pip2_get_version_(struct pt_core_data *cd)
{
	int rc = 0;
	int status;
	u8 read_buf[64];
	u16 actual_read_len;

	rc = _pt_request_pip2_send_cmd(cd->dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
		NULL, 0, read_buf, &actual_read_len);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Error Sending PIP2 VERSION Cmd rc=%d\n",
			__func__, rc);
		return rc;
	}

	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	if (status == 0) {
		/* Parse the PIP2 VERSION response into ttdata */
		pt_pip2_ver_load_ttdata(cd, actual_read_len);
	} else {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Error in PIP2 VERSION Cmd status=%d\n",
			__func__, status);
		return status;
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_get_version
 *
 * SUMMARY: Protected call to pt_pip2_get_version_ by exclusive
 *	access to the DUT.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd       - pointer to core data structure
 ******************************************************************************/
static int pt_pip2_get_version(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip2_get_version_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);

	return rc;
}


/*******************************************************************************
 * FUNCTION: _pt_request_active_pip_protocol
 *
 * SUMMARY: Get active PIP protocol version using the PIP2 version command.
 *	Function will return PIP version of BL or application based on
 *	when it's called.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev               - pointer to device structure
 *	 protect           - flag to run in protected mode
 *	*pip_version_major - pointer to store PIP major version
 *	*pip_version_minor - pointer to store PIP minor version
 ******************************************************************************/
int _pt_request_active_pip_protocol(struct device *dev, int protect,
	u8 *pip_version_major, u8 *pip_version_minor)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
	int rc = 0;
	struct pt_hid_output sys_info = {
		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
		.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
	};

	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);

	/* Skip PIP2 command if DUT generation is confirmed */
	if (cd->active_dut_generation == DUT_PIP1_ONLY)
		goto skip_pip2_command;

	rc = pt_pip2_get_version_(cd);
	if (!rc) {
		*pip_version_major = ttdata->pip_ver_major;
		*pip_version_minor = ttdata->pip_ver_minor;
		pt_debug(dev, DL_INFO,
			"%s: pip_version = %d.%d\n", __func__,
			*pip_version_major, *pip_version_minor);
	} else {
		/*
		 * Legacy products do not support the pip2 protocol to get
		 * pip version. However, they do support the "get sysinfo"
		 * command to get pip version from FW, but the bootloader
		 * does not support it. This function will try "get sysinfo"
		 * command if the pip2 command failed but this cmd could also
		 * fail if DUT is stuck in bootloader mode.
		 */
		pt_debug(dev, DL_INFO,
			"%s: PIP2 no response rc = %d, try legacy cmd\n",
			__func__, rc);

skip_pip2_command:
		rc = pt_pip1_send_output_and_wait_(cd, &sys_info);
		if (!rc) {
			*pip_version_minor =
			   cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET + 1];
			*pip_version_major =
			   cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];

			pt_debug(dev, DL_INFO,
				"%s: pip_version = %d.%d\n", __func__,
				*pip_version_major, *pip_version_minor);
		} else {
			*pip_version_major = 0;
			*pip_version_minor = 0;
			pt_debug(dev, DL_ERROR,
				"%s: pip_version Not Detected\n", __func__);
		}
	}

	return rc;
}
EXPORT_SYMBOL_GPL(_pt_request_active_pip_protocol);

/*******************************************************************************
 * FUNCTION: _pt_detect_dut_generation
 *
 * SUMMARY: Determine the generation of device that we are communicating with:
 *		DUT_PIP1_ONLY (Gen5 or Gen6)
 *		DUT_PIP2_CAPABLE (TC33xx or TT7xxx)
 *	The HID_DESC command is supported in Gen5/6 BL and FW as well as
 *	TT/TC FW. The packet ID in the descriptor, however, is unique when
 *	coming form the BL or the FW:
 *		Packet_ID in BL = HID_BL_REPORT_ID (0xFF)
 *		Packet_ID in FW = HID_APP_REPORT_ID (0xF7)
 *	This function will return a modified status if it detects the DUT
 *	is in the BL. In the case of a Gen5/6 BL, which also sends out a FW
 *	reset sentinel, the status is "corrected" from a FW to BL sentinel.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev     - pointer to device structure
 *      *status  - pointer to status bitmask
 *      *dut_gen - pointer to store the dut_generation
 *      *mode    - pointer to store the PT_MODE
 ******************************************************************************/
static int _pt_detect_dut_generation(struct device *dev,
	u32 *status, u8 *dut_gen, enum pt_mode *mode)
{
	int rc = 0;
	u8 dut_gen_tmp = DUT_UNKNOWN;
	u8 mode_tmp = PT_MODE_UNKNOWN;
	u8 attempt = 1;
	u32 status_tmp = STARTUP_STATUS_START;
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_hid_desc hid_desc;

	memset(&hid_desc, 0, sizeof(hid_desc));
	rc = pt_get_hid_descriptor_(cd, &hid_desc);

	while (rc && attempt < 3) {
		attempt++;
		usleep_range(2000, 5000);
		rc = pt_get_hid_descriptor_(cd, &hid_desc);
	}

	if (!rc && hid_desc.packet_id == HID_BL_REPORT_ID) {
		dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 BL */
		mode_tmp = PT_MODE_BOOTLOADER;
		status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL;
	} else if (!rc && hid_desc.packet_id == HID_APP_REPORT_ID) {
		rc = pt_pip2_get_version_(cd);
		if (!rc)
			dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC FW */
		else
			dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 FW */
		mode_tmp = PT_MODE_OPERATIONAL;
		status_tmp = STARTUP_STATUS_FW_RESET_SENTINEL;
		rc = 0; /* To return success instead of error code */
	} else if (rc) {
		rc = pt_pip2_get_version_(cd);
		if (!rc) {
			dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC BL */
			mode_tmp = PT_MODE_BOOTLOADER;
			status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL;
		}
	}

	mutex_lock(&cd->system_lock);
	if (dut_gen)
		*dut_gen = dut_gen_tmp;
	if (mode)
		*mode = mode_tmp;
	if (status)
		*status = status_tmp;
	mutex_unlock(&cd->system_lock);

#ifdef TTDL_DIAGNOSTICS
	pt_debug(cd->dev, DL_INFO, "%s: Generation=%d Mode=%d\n",
		__func__, dut_gen_tmp, mode_tmp);
#endif /* TTDL_DIAGNOSTICS */

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_dut_generation
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to get current dut generation.
 *
 * NOTE: This function WILL NOT try to determine dut generation.
 *
 * RETURN:
 *	The current dut generation.
 *
 * PARAMETERS:
 *      *dev            - pointer to device structure
 ******************************************************************************/
static int _pt_request_dut_generation(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return cd->active_dut_generation;
}

#define HW_VERSION_LEN_MAX 13
/*******************************************************************************
 * FUNCTION: _legacy_generate_hw_version
 *
 * SUMMARY: Format chip information from struct ttdata (maintained by PIP1
 *   SYSINFO command) or struct bl_info (maintained by PIP1 BL INFORMATION
 *   command) to the hw_version.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hw_version - pointer to store the hardware version
 ******************************************************************************/
static int _legacy_generate_hw_version(struct pt_core_data *cd,
				       char *hw_version)
{
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;

	if (cd->sysinfo.ready) {
		scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
			 ttdata->jtag_id_h, cd->pid_for_loader);
		return 0;
	} else if (cd->bl_info.ready) {
		scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
			 cd->bl_info.chip_id, cd->pid_for_loader);
		return 0;
	} else {
		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
		pt_debug(cd->dev, DL_ERROR,
			 "%s: SYSINFO and BL_INFO are not ready\n", __func__);
		return -ENODATA;
	}
}

/*******************************************************************************
 * FUNCTION: _pip2_generate_hw_version
 *
 * SUMMARY: Format chip information from struct ttdata (maintained by PIP2
 *   VERSION command) to the hw_version.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hw_version - pointer to store the hardware version
 ******************************************************************************/
static int _pip2_generate_hw_version(struct pt_core_data *cd, char *hw_version)
{
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;

	if (cd->app_pip_ver_ready | cd->bl_pip_ver_ready) {
		snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.%04X.%02X",
			 ttdata->chip_id, ttdata->chip_rev, cd->pid_for_loader);
		return 0;
	} else {
		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
		pt_debug(cd->dev, DL_ERROR,
			 "%s: PIP Version are not ready\n", __func__);
		return -ENODATA;
	}
}

/*******************************************************************************
 * FUNCTION: pt_generate_hw_version
 *
 * SUMMARY: Wraaper function for both legacy and TT/TC products generate the
 *   hw_version from static data.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer to core data
 *	*hw_version - pointer to store the hardware version
 ******************************************************************************/
static int pt_generate_hw_version(struct pt_core_data *cd, char *hw_version)
{
	int rc = 0;

	if (!hw_version)
		return -ENOMEM;

	if (cd->active_dut_generation == DUT_PIP1_ONLY)
		rc = _legacy_generate_hw_version(cd, hw_version);
	else if (cd->active_dut_generation == DUT_PIP2_CAPABLE)
		rc = _pip2_generate_hw_version(cd, hw_version);
	else {
		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
		rc = -ENODATA;
	}

	return rc;
}
/*******************************************************************************
 * SUMMARY: Attempt to retrieve the HW version of the connected DUT
 *
 * NOTE: The calling function must ensure to free *hw_version
 *
 * RETURN:
 *	0  = success
 *	!0 = Failure
 *
 * PARAMETERS:
 *      *dev        - pointer to device structure
 *	*hw_version - pointer to where the hw_version string will be stored
 ******************************************************************************/
static int _pt_request_hw_version(struct device *dev, char *hw_version)
{
	int rc = 0;
	u16 actual_read_len;
	u16 pip_ver;
	u8 rd_buf[256];
	u8 status;
	u8 index = PIP2_RESP_STATUS_OFFSET;
	u8 return_data[8];
	u8 panel_id;
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;

	if (!hw_version)
		return -ENOMEM;

	if (!cd->hw_detected) {
		/* No HW detected */
		rc = -ENODEV;
		pt_debug(dev, DL_ERROR, "%s: no hardware is detected!\n",
			 __func__);
		goto exit_error;
	}

	memset(return_data, 0, ARRAY_SIZE(return_data));
	/* For Parade TC or TT parts */
	if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
		rc = _pt_request_pip2_send_cmd(dev,
			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
			NULL, 0, rd_buf, &actual_read_len);

		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s: Failed to send PIP2 VERSION cmd\n",
				__func__);
			goto exit_error;
		}

		status = rd_buf[index];
		if (status == 0) {
			pip_ver = 256 * rd_buf[index + 2] + rd_buf[index + 1];

			/*
			 * BL PIP 2.02 and greater the version fields are
			 * swapped
			 */
			if (pip_ver >= 0x0202) {
				snprintf(hw_version, HW_VERSION_LEN_MAX,
					"%02X%02X.%02X%02X.FF",
					rd_buf[index + 10], rd_buf[index + 9],
					rd_buf[index + 8], rd_buf[index + 7]);
			} else {
				snprintf(hw_version, HW_VERSION_LEN_MAX,
					"%02X%02X.%02X%02X.FF",
					rd_buf[index + 8], rd_buf[index + 7],
					rd_buf[index + 10], rd_buf[index + 9]);
			}
			return STATUS_SUCCESS;
		} else {
			rc = status;
			pt_debug(dev, DL_WARN,
				"%s: PIP2 VERSION cmd response error\n",
				__func__);
		}
	} else if (cd->active_dut_generation == DUT_PIP1_ONLY) {
		/*
		 * For Parade/Cypress legacy parts the RevID and FamilyID are
		 * hard coded to FFFF
		 */
		if (cd->mode == PT_MODE_OPERATIONAL) {
			rc = pt_hid_output_get_sysinfo(cd);
			if (!rc) {
				panel_id =
					cd->sysinfo.sensing_conf_data.panel_id;
			} else {
				panel_id = PANEL_ID_NOT_ENABLED;
			}
			/* In FW - simply retrieve from ttdata struct */
			snprintf(hw_version, HW_VERSION_LEN_MAX,
				"%04X.FFFF.%02X",
				ttdata->jtag_id_h,
				panel_id);
			return STATUS_SUCCESS;
		} else {
			/*
			 * Return the stored value if PT_PANEL_ID_BY_BL
			 * is not supported while other feature is.
			 */
			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
				rc = pt_hid_output_bl_get_information(
				    cd, return_data);
				if (!rc) {
					rc = pt_hid_output_bl_get_panel_id(
					    cd, &panel_id);
				}
			} else
				panel_id = cd->pid_for_loader;
			if (!rc) {
				snprintf(hw_version,
					HW_VERSION_LEN_MAX,
					"%02X%02X.FFFF.%02X",
					return_data[3], return_data[2],
					panel_id);
				return STATUS_SUCCESS;
			}
		}
	} else {
		/* Unknown generation */
		rc = -ENODEV;
		pt_debug(dev, DL_ERROR, "%s: generation is unknown!\n",
			 __func__);
	}

exit_error:
	snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_start_wd_timer
 *
 * SUMMARY: Starts the TTDL watchdog timer if the timer interval is > 0
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static void pt_start_wd_timer(struct pt_core_data *cd)
{
	if (cd->watchdog_interval < 100) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: WARNING: Invalid watchdog interval: %d\n",
			__func__, cd->watchdog_interval);
		return;
	}

	if (cd->watchdog_force_stop) {
		pt_debug(cd->dev, DL_INFO,
			"%s: TTDL WD Forced stop\n", __func__);
		return;
	}

	mod_timer(&cd->watchdog_timer, jiffies +
		msecs_to_jiffies(cd->watchdog_interval));
	cd->watchdog_enabled = 1;
	pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Started\n", __func__);
}

/*******************************************************************************
 * FUNCTION: pt_stop_wd_timer
 *
 * SUMMARY: Stops the TTDL watchdog timer if the timer interval is > 0
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static void pt_stop_wd_timer(struct pt_core_data *cd)
{
	if (!cd->watchdog_interval)
		return;

	/*
	 * Ensure we wait until the watchdog timer
	 * running on a different CPU finishes
	 */
	del_timer_sync(&cd->watchdog_timer);
	cancel_work_sync(&cd->watchdog_work);
	del_timer_sync(&cd->watchdog_timer);
	cd->watchdog_enabled = 0;
	pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Stopped\n", __func__);
}

/*******************************************************************************
 * FUNCTION: pt_hw_soft_reset
 *
 * SUMMARY: Sends a PIP reset command to the DUT. Disable/re-enable the
 *	TTDL watchdog around the reset to ensure the WD doesn't happen to
 *	schedule an enum if it fires when the DUT is being reset.
 *	This can cause a double reset.
 *
 *  NOTE: The WD MUST be stopped/restarted by the calling Function. Having
 *       the WD active could cause this function to fail!
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data struct
 *	 protect - flag to call protected or non-protected
 ******************************************************************************/
static int pt_hw_soft_reset(struct pt_core_data *cd, int protect)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);
	cd->startup_status = STARTUP_STATUS_START;
	pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
	mutex_unlock(&cd->system_lock);
	if (protect)
		rc = pt_hid_cmd_reset(cd);
	else
		rc = pt_hid_cmd_reset_(cd);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: FAILED to execute SOFT reset\n", __func__);
		return rc;
	}
	pt_debug(cd->dev, DL_INFO, "%s: SOFT reset successful\n",
		__func__);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_hw_hard_reset
 *
 * SUMMARY: Calls the platform xres function if it exists to perform a hard
 *	reset on the DUT by toggling the XRES gpio. Disable/re-enable the
 *	TTDL watchdog around the reset to ensure the WD doesn't happen to
 *	schedule an enum if it fires when the DUT is being reset.
 *	This can cause a double reset.
 *
 * NOTE: The WD MUST be stopped/restarted by the calling Function. Having
 *       the WD active could cause this function to fail!
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data struct
 ******************************************************************************/
static int pt_hw_hard_reset(struct pt_core_data *cd)
{
	if (cd->cpdata->xres) {
		cd->startup_status = STARTUP_STATUS_START;
		pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n",
			__func__);
		cd->cpdata->xres(cd->cpdata, cd->dev);
		pt_debug(cd->dev, DL_WARN, "%s: executed HARD reset\n",
			__func__);
		return 0;
	}
	pt_debug(cd->dev, DL_ERROR,
		"%s: FAILED to execute HARD reset\n", __func__);

	return -ENODEV;
}

/*******************************************************************************
 * FUNCTION: pt_dut_reset
 *
 * SUMMARY: Attempts to reset the DUT by a hard reset and if that fails a
 *	soft reset.
 *
 * NOTE: The WD MUST be stopped/restarted by the calling Function. Having
 *       the WD active could cause this function to fail!
 * NOTE: "protect" flag is only used for soft reset.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd      - pointer to core data structure
 *	 protect - flag to call protected or non-protected
 ******************************************************************************/
static int pt_dut_reset(struct pt_core_data *cd, int protect)
{
	int rc = 0;

	pt_debug(cd->dev, DL_INFO, "%s: reset hw...\n", __func__);
	mutex_lock(&cd->system_lock);
	cd->hid_reset_cmd_state = 1;
	rc = pt_hw_hard_reset(cd);
	mutex_unlock(&cd->system_lock);

	if (rc == -ENODEV) {
		mutex_lock(&cd->system_lock);
		cd->hid_reset_cmd_state = 0;
		mutex_unlock(&cd->system_lock);
		pt_debug(cd->dev, DL_ERROR,
			"%s: Hard reset failed, try soft reset\n", __func__);
		rc = pt_hw_soft_reset(cd, protect);
	}

	if (rc)
		pt_debug(cd->dev, DL_ERROR, "%s: %s dev='%s' r=%d\n",
			__func__, "Fail hw reset", dev_name(cd->dev), rc);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_dut_reset_and_wait
 *
 * SUMMARY: Wrapper function for pt_dut_reset that waits for the reset to
 *	complete
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int pt_dut_reset_and_wait(struct pt_core_data *cd)
{
	int rc = 0;
	int t;

	rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
	if (rc < 0)
		goto exit;

	t = wait_event_timeout(cd->wait_q,
		(cd->hid_reset_cmd_state == 0),
		msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT));
	if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
		cd->bus_transmit_error_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR, "%s: reset timed out\n",
			__func__);
		rc = -ETIME;
		goto exit;
	}

exit:
	return rc;
}

/*
 * touch default parameters (from report descriptor) to resolve protocol for
 * touch report
 */
const struct pt_tch_abs_params tch_hdr_default[PT_TCH_NUM_HDR] = {
	/* byte offset, size, min, max, bit offset, report */
	{0x00, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* SCAN TIME */
	{0x02, 0x01, 0x00, 0x20,    0x00, 0x01},	/* NUMBER OF RECORDS */
	{0x02, 0x01, 0x00, 0x02,    0x05, 0x01},	/* LARGE OBJECT */
	{0x03, 0x01, 0x00, 0x08,    0x00, 0x01},	/* NOISE EFFECT */
	{0x03, 0x01, 0x00, 0x04,    0x06, 0x01},	/* REPORT_COUNTER */
};

/*
 * button default parameters (from report descriptor) to resolve protocol for
 * button report
 */
const struct pt_tch_abs_params tch_abs_default[PT_TCH_NUM_ABS] = {
	/* byte offset, size, min, max, bit offset, report */
	{0x02, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* X */
	{0x04, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* Y */
	{0x06, 0x01, 0x00, 0x100,   0x00, 0x01},	/* P (Z) */
	{0x01, 0x01, 0x00, 0x20,    0x00, 0x01},	/* TOUCH ID */
	{0x01, 0x01, 0x00, 0x04,    0x05, 0x01},	/* EVENT ID */
	{0x00, 0x01, 0x00, 0x08,    0x00, 0x01},	/* OBJECT ID */
	{0x01, 0x01, 0x00, 0x02,    0x07, 0x01},	/* LIFTOFF */
	{0x07, 0x01, 0x00, 0x100,   0x00, 0x01},	/* TOUCH_MAJOR */
	{0x08, 0x01, 0x00, 0x100,   0x00, 0x01},	/* TOUCH_MINOR */
	{0x09, 0x01, 0x00, 0x100,   0x00, 0x01},	/* ORIENTATION */
};

/*******************************************************************************
 * FUNCTION: pt_init_pip_report_fields
 *
 * SUMMARY: Setup default values for touch/button report parsing.
 *
 * PARAMETERS:
 *  *cd  - pointer to core data structure
 ******************************************************************************/
static void pt_init_pip_report_fields(struct pt_core_data *cd)
{
	struct pt_sysinfo *si = &cd->sysinfo;

	memcpy(si->tch_hdr, tch_hdr_default, sizeof(tch_hdr_default));
	memcpy(si->tch_abs, tch_abs_default, sizeof(tch_abs_default));

	si->desc.tch_report_id = PT_PIP_TOUCH_REPORT_ID;
	si->desc.tch_record_size = TOUCH_REPORT_SIZE;
	si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
	si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID;

	cd->features.easywake = 1;
	cd->features.noise_metric = 1;
	cd->features.tracking_heatmap = 1;
	cd->features.sensor_data = 1;
}

/*******************************************************************************
 * FUNCTION: pt_get_mode
 *
 * SUMMARY: Determine the current mode from the contents of a HID descriptor
 *	message
 *
 * RETURN: Enum of the current mode
 *
 * PARAMETERS:
 *      *cd      - pointer to the Core Data structure
 *       protect - run command in protected mode
 *      *mode    - pointer to store the retrieved mode
 ******************************************************************************/
static int pt_get_mode(struct pt_core_data *cd, struct pt_hid_desc *desc)
{
	if (desc->packet_id == HID_APP_REPORT_ID)
		return PT_MODE_OPERATIONAL;
	else if (desc->packet_id == HID_BL_REPORT_ID)
		return PT_MODE_BOOTLOADER;

	return PT_MODE_UNKNOWN;
}

/*******************************************************************************
 * FUNCTION: _pt_request_get_mode
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to determine the current mode of the DUT by use of the Get HID
 *	Descriptor command.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev      - pointer to device structure
 *       protect  - run command in protected mode
 *      *mode     - pointer to store the retrieved mode
 ******************************************************************************/
static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_hid_desc hid_desc;
	int rc = 0;

	memset(&hid_desc, 0, sizeof(hid_desc));

	if (protect)
		rc = pt_get_hid_descriptor(cd, &hid_desc);
	else
		rc = pt_get_hid_descriptor_(cd, &hid_desc);

	if (rc)
		*mode = PT_MODE_UNKNOWN;
	else
		*mode = pt_get_mode(cd, &hid_desc);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_queue_enum_
 *
 * SUMMARY: Queues a TTDL enum by scheduling work with the pt_enum_with_dut()
 *	function. It won't try to add/delete sysfs node or modules.
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static void pt_queue_enum_(struct pt_core_data *cd)
{
	if (cd->startup_state == STARTUP_NONE) {
		cd->startup_state = STARTUP_QUEUED;

#ifdef TTDL_DIAGNOSTICS
		if (!cd->bridge_mode)
			schedule_work(&cd->enum_work);
		else
			cd->startup_state = STARTUP_NONE;
#else
		schedule_work(&cd->enum_work);
#endif
		pt_debug(cd->dev, DL_INFO,
			"%s: enum_work queued\n", __func__);
	} else {
		pt_debug(cd->dev, DL_WARN,
			"%s: Enum not queued - startup_state = %d\n",
			__func__, cd->startup_state);
	}
}

/*******************************************************************************
 * FUNCTION: pt_queue_enum
 *
 * SUMMARY: Queues a TTDL enum within a mutex lock
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static void pt_queue_enum(struct pt_core_data *cd)
{
	mutex_lock(&cd->system_lock);
	pt_queue_enum_(cd);
	mutex_unlock(&cd->system_lock);
}

static void remove_sysfs_and_modules(struct device *dev);
/*******************************************************************************
 * FUNCTION: pt_queue_restart
 *
 * SUMMARY: Queues a TTDL restart within a mutex lock
 *
 * RETURN: void
 *
 * PARAMETERS:
 *                 *cd - pointer to core data
 * remove_sysfs_module - True: remove all DUT relative sysfs nodes and modules
 *                      False: will not perform remove action
 ******************************************************************************/
static void pt_queue_restart(struct pt_core_data *cd)
{
	mutex_lock(&cd->system_lock);
	if (cd->startup_state == STARTUP_NONE) {
		cd->startup_state = STARTUP_QUEUED;

		schedule_work(&cd->ttdl_restart_work);
		pt_debug(cd->dev, DL_INFO,
			"%s: pt_ttdl_restart queued\n", __func__);
	} else {
		pt_debug(cd->dev, DL_INFO, "%s: startup_state = %d\n",
			__func__, cd->startup_state);
	}
	mutex_unlock(&cd->system_lock);
}

/*******************************************************************************
 * FUNCTION: call_atten_cb
 *
 * SUMMARY: Iterate over attention list call the function that registered.
 *
 * RETURN: void
 *
 * PARAMETERS:
 *  *cd    - pointer to core data
 *   type  - type of attention list
 *   mode  - condition for execution
 ******************************************************************************/
static void call_atten_cb(struct pt_core_data *cd,
		enum pt_atten_type type, int mode)
{
	struct atten_node *atten, *atten_n;

	pt_debug(cd->dev, DL_DEBUG, "%s: check list type=%d mode=%d\n",
		__func__, type, mode);
	spin_lock(&cd->spinlock);
	list_for_each_entry_safe(atten, atten_n,
			&cd->atten_list[type], node) {
		if (!mode || atten->mode & mode) {
			spin_unlock(&cd->spinlock);
			pt_debug(cd->dev, DL_DEBUG,
				"%s: attention for '%s'",
				__func__, dev_name(atten->dev));
			atten->func(atten->dev);
			spin_lock(&cd->spinlock);
		}
	}
	spin_unlock(&cd->spinlock);
}

/*******************************************************************************
 * FUNCTION: start_fw_upgrade
 *
 * SUMMARY: Calling "PT_ATTEN_LOADER" attention list that loader registered to
 *  start firmware upgrade.
 *
 * RETURN:
 *   0 = success
 *
 * PARAMETERS:
 *  *data  - pointer to core data
 ******************************************************************************/
static int start_fw_upgrade(void *data)
{
	struct pt_core_data *cd = (struct pt_core_data *)data;

	call_atten_cb(cd, PT_ATTEN_LOADER, 0);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_put_device_into_easy_wakeup_
 *
 * SUMMARY: Call the enter_easywake_state function and set the device into easy
 *  wake up state.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd)
{
	int rc = 0;
	u8 status = 0;


	rc = pt_hid_output_enter_easywake_state_(cd,
		cd->easy_wakeup_gesture, &status);
	if (rc || status == 0)
		return -EBUSY;

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_put_device_into_deep_sleep_
 *
 * SUMMARY: Call the set_power function and set the DUT to deep sleep
 *
 * RETURN:
 *	 0 = success
 *	!0 = error
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static int pt_put_device_into_deep_sleep_(struct pt_core_data *cd)
{
	int rc = 0;

	rc = pt_hid_cmd_set_power_(cd, HID_POWER_SLEEP);
	if (rc)
		rc = -EBUSY;
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_put_device_into_deep_standby_
 *
 * SUMMARY: Call the set_power function and set the DUT to Deep Standby
 *
 * RETURN:
 *	 0 = success
 *	!0 = error
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static int pt_put_device_into_deep_standby_(struct pt_core_data *cd)
{
	int rc = 0;

	rc = pt_hid_cmd_set_power_(cd, HID_POWER_STANDBY);
	if (rc)
		rc = -EBUSY;
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_poweroff_device_
 *
 * SUMMARY: Disable IRQ and HW power down the device.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_poweroff_device_(struct pt_core_data *cd)
{
	int rc;

	if (cd->irq_enabled) {
		cd->irq_enabled = false;
		disable_irq_nosync(cd->irq);
	}

	rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0);
	if (rc < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: HW Power down fails r=%d\n",
			__func__, rc);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_sleep_
 *
 * SUMMARY: Suspend the device with power off or deep sleep based on the
 *  configuration in the core platform data structure.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_sleep_(struct pt_core_data *cd)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);
	pt_debug(cd->dev, DL_INFO, "%s - sleep_state %d\n", __func__, cd->sleep_state);
	if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) {
		mutex_unlock(&cd->system_lock);
		pt_debug(cd->dev, DL_INFO,
			"%s - Skip slee[ state %d\n", __func__, cd->sleep_state);
			return 0;
	} else {
		cd->sleep_state = SS_SLEEPING;
	}
	mutex_unlock(&cd->system_lock);

	/* Ensure watchdog and startup works stopped */
	pt_stop_wd_timer(cd);
	cancel_work_sync(&cd->enum_work);
	pt_stop_wd_timer(cd);

	if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
		pt_debug(cd->dev, DL_INFO, "%s: Entering into power off mode:\n", __func__);
		rc = pt_core_poweroff_device_(cd);
		if (rc)
			pr_err("%s: Power off error detected :rc=%d\n", __func__, rc);
	} else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) {
		pt_debug(cd->dev, DL_INFO,
			"%s: Entering into deep standby mode:\n", __func__);
		rc = pt_put_device_into_deep_standby_(cd);
		if (rc)
			pr_err("%s: Deep standby error detected :rc=%d\n", __func__, rc);
	} else {
		pt_debug(cd->dev, DL_INFO,
			"%s: Entering into deep sleep mode:\n", __func__);
		rc = pt_put_device_into_deep_sleep_(cd);
		if (rc)
			pr_err("%s: Deep sleep error detected :rc=%d\n", __func__, rc);
	}

	mutex_lock(&cd->system_lock);
	cd->sleep_state = SS_SLEEP_ON;
	mutex_unlock(&cd->system_lock);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_easywake_on_
 *
 * SUMMARY: Suspend the device with easy wake on the
 *  configuration in the core platform data structure.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_easywake_on_(struct pt_core_data *cd)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);

	if (cd->sleep_state == SS_EASY_WAKING_ON) {
		mutex_unlock(&cd->system_lock);
		pt_debug(cd->dev, DL_INFO, "%s - Skip sleep state %d\n",
			__func__, cd->sleep_state);
		return 0;
	}
	mutex_unlock(&cd->system_lock);

	/* Ensure watchdog and startup works stopped */
	pt_stop_wd_timer(cd);
	cancel_work_sync(&cd->enum_work);
	pt_stop_wd_timer(cd);

	if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) {
		rc = pt_put_device_into_easy_wakeup_(cd);
		pt_debug(cd->dev, DL_INFO, "%s :Entering into easywakeup: rc=%d\n", __func__, rc);
		if (rc)
			pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc);
	}
	mutex_lock(&cd->system_lock);
	cd->sleep_state = SS_EASY_WAKING_ON;
	mutex_unlock(&cd->system_lock);

	return rc;
}


/*******************************************************************************
 * FUNCTION: pt_core_easywake_on
 *
 * SUMMARY: Protected call to pt_core_easywake_on_ by exclusive access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_easywake_on(struct pt_core_data *cd)
{
	int rc = 0;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_core_easywake_on_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);
	else
		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
			__func__);

	return rc;
}


/*******************************************************************************
 * FUNCTION: pt_core_sleep
 *
 * SUMMARY: Protected call to pt_core_sleep_ by exclusive access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_sleep(struct pt_core_data *cd)
{
	int rc = 0;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_core_sleep_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail to release exclusive\n", __func__);
	else
		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
			__func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_wakeup_host
 *
 * SUMMARY: Check wake up report and call the PT_ATTEN_WAKE attention list.
 *
 * NOTE: TSG5 EasyWake and TSG6 EasyWake use different protocol.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_wakeup_host(struct pt_core_data *cd)
{
#ifndef EASYWAKE_TSG6
	/* TSG5 EasyWake */
	int rc = 0;
	int event_id;
	int size = get_unaligned_le16(&cd->input_buf[0]);

	/* Validate report */
	if (size != 4 || cd->input_buf[2] != 4)
		rc = -EINVAL;

	event_id = cd->input_buf[3];

	pt_debug(cd->dev, DL_INFO, "%s: e=%d, rc=%d\n",
		__func__, event_id, rc);

	if (rc) {
		pt_core_sleep_(cd);
		goto exit;
	}

	/* attention WAKE */
	call_atten_cb(cd, PT_ATTEN_WAKE, 0);
exit:
	return rc;
#else
	/* TSG6 FW1.3 EasyWake */
	int rc = 0;
	int i = 0;
	int report_length;

	/* Validate report */
	if (cd->input_buf[2] != PT_PIP_WAKEUP_REPORT_ID) {
		rc = -EINVAL;
		pt_core_sleep_(cd);
		goto exit;
	}

	/* Get gesture id and gesture data length */
	cd->gesture_id = cd->input_buf[3];
	report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]);
	cd->gesture_data_length = report_length - 4;

	pt_debug(cd->dev, DL_INFO,
		"%s: gesture_id = %d, gesture_data_length = %d\n",
		__func__, cd->gesture_id, cd->gesture_data_length);

	for (i = 0; i < cd->gesture_data_length; i++)
		cd->gesture_data[i] = cd->input_buf[4 + i];

	/* attention WAKE */
	call_atten_cb(cd, PT_ATTEN_WAKE, 0);
exit:
	return rc;
#endif
}

/*******************************************************************************
 * FUNCTION: pt_get_touch_axis
 *
 * SUMMARY: Function to calculate touch axis
 *
 * PARAMETERS:
 *     *cd      - pointer to core data structure
 *     *axis    - pointer to axis calculation result
 *      size    - size in bytes
 *      max     - max value of result
 *     *xy_data - pointer to input data to be parsed
 *      bofs    - bit offset
 ******************************************************************************/
static void pt_get_touch_axis(struct pt_core_data *cd,
	int *axis, int size, int max, u8 *data, int bofs)
{
	int nbyte;
	int next;

	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
		*axis = *axis + ((data[next] >> bofs) << (nbyte * 8));
		next++;
	}

	*axis &= max - 1;
}

/*******************************************************************************
 * FUNCTION: move_tracking_heatmap_data
 *
 * SUMMARY: Move the valid tracking heatmap data from the input buffer into the
 *	system information structure, xy_mode and xy_data.
 *	- If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
 *	the tthe_tuner sysfs node under the label "THM"
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 *	*si - pointer to the system information structure
 ******************************************************************************/
static int move_tracking_heatmap_data(struct pt_core_data *cd,
	struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
	int size = get_unaligned_le16(&cd->input_buf[0]);

	if (size)
		tthe_print(cd, cd->input_buf, size, "THM=");
#endif
	memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
	return 0;
}

/*******************************************************************************
 * FUNCTION: move_sensor_data
 *
 * SUMMARY: Move the valid sensor data from the input buffer into the system
 *	information structure, xy_mode and xy_data.
 *	- If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
 *	the tthe_tuner sysfs node under the label "sensor_monitor"
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 *	*si - pointer to the system information structure
 ******************************************************************************/
static int move_sensor_data(struct pt_core_data *cd,
	struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
	int size = get_unaligned_le16(&cd->input_buf[0]);

	if (size)
		tthe_print(cd, cd->input_buf, size, "sensor_monitor=");
#endif
	memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
	return 0;
}

/*******************************************************************************
 * FUNCTION: move_button_data
 *
 * SUMMARY: Move the valid button data from the input buffer into the system
 *	information structure, xy_mode and xy_data.
 *	- If TTHE_TUNER_SUPPORT is defined print the raw button data into
 *	the tthe_tuner sysfs node under the label "OpModeData"
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 *	*si - pointer to the system information structure
 ******************************************************************************/
static int move_button_data(struct pt_core_data *cd,
	struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
	int size = get_unaligned_le16(&cd->input_buf[0]);

	if (size)
		tthe_print(cd, cd->input_buf, size, "OpModeData=");
#endif
	memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE);
	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE,
		"xy_mode");

	memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE],
		BTN_REPORT_SIZE);
	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, BTN_REPORT_SIZE,
		"xy_data");
	return 0;
}

/*******************************************************************************
 * FUNCTION: move_touch_data
 *
 * SUMMARY: Move the valid touch data from the input buffer into the system
 *	information structure, xy_mode and xy_data.
 *	- If TTHE_TUNER_SUPPORT is defined print the raw touch data into
 *	the tthe_tuner sysfs node under the label "OpModeData"
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 *	*si - pointer to the system information structure
 ******************************************************************************/
static int move_touch_data(struct pt_core_data *cd, struct pt_sysinfo *si)
{
	int max_tch = si->sensing_conf_data.max_tch;
	int num_cur_tch;
	int length;
	struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
#ifdef TTHE_TUNER_SUPPORT
	int size = get_unaligned_le16(&cd->input_buf[0]);

	if (size)
		tthe_print(cd, cd->input_buf, size, "OpModeData=");
#endif

	memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size);
	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode,
		si->desc.tch_header_size, "xy_mode");

	pt_get_touch_axis(cd, &num_cur_tch, tch->size,
			tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs);
	if (unlikely(num_cur_tch > max_tch))
		num_cur_tch = max_tch;

	length = num_cur_tch * si->desc.tch_record_size;

	memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length);
	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, length, "xy_data");
	return 0;
}

/*******************************************************************************
 * FUNCTION: move_hid_pen_data
 *
 * SUMMARY: TODO Move the valid pen data from the input buffer into the system
 *	information structure, xy_mode and xy_data.
 *	- If TTHE_TUNER_SUPPORT is defined print the raw pen data into
 *	the tthe_tuner sysfs node under the label "HID" starting with the
 *	report ID.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 *	*si - pointer to the system information structure
 ******************************************************************************/
static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
	int size = get_unaligned_le16(&cd->input_buf[0]);

	if (size) {
		/*
		 * HID over USB does not require the two byte length field, so
		 * this should print from input_buf[2] but to keep both finger
		 * and pen reports the same the length is included
		 */
		if (cd->tthe_hid_usb_format == PT_FEATURE_ENABLE)
			tthe_print(cd, &(cd->input_buf[2]), size - 2,
				"HID-USB=");
		else
			tthe_print(cd, &(cd->input_buf[0]), size,
				"HID-I2C=");
	}
#endif
	pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen");
	return 0;
}

/*******************************************************************************
 * FUNCTION: parse_touch_input
 *
 * SUMMARY: Parse the touch report and take action based on the touch
 *	report_id.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	 size - size of touch record
 ******************************************************************************/
static int parse_touch_input(struct pt_core_data *cd, int size)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	int report_id = cd->input_buf[2];
	int rc = -EINVAL;

	pt_debug(cd->dev, DL_DEBUG, "%s: Received touch report\n",
		__func__);
	if (!si->ready) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Need system information to parse touches\n",
			__func__);
		return 0;
	}

	if (!si->xy_mode || !si->xy_data)
		return rc;

	if (report_id == PT_PIP_TOUCH_REPORT_ID)
		rc = move_touch_data(cd, si);
	else if (report_id == PT_HID_PEN_REPORT_ID)
		rc = move_hid_pen_data(cd, si);
	else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID)
		rc = move_button_data(cd, si);
	else if (report_id == PT_PIP_SENSOR_DATA_REPORT_ID)
		rc = move_sensor_data(cd, si);
	else if (report_id == PT_PIP_TRACKING_HEATMAP_REPORT_ID)
		rc = move_tracking_heatmap_data(cd, si);

	if (rc)
		return rc;

	/* attention IRQ */
	call_atten_cb(cd, PT_ATTEN_IRQ, cd->mode);

	return 0;
}

/*******************************************************************************
 * FUNCTION: parse_command_input
 *
 * SUMMARY: Move the response data from the input buffer to the response buffer
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer to core data
 *	 size - size of response data
 ******************************************************************************/
static int parse_command_input(struct pt_core_data *cd, int size)
{
	pt_debug(cd->dev, DL_DEBUG, "%s: Received cmd interrupt\n",
		__func__);

	memcpy(cd->response_buf, cd->input_buf, size);
#if defined(TTHE_TUNER_SUPPORT) && defined(TTDL_DIAGNOSTICS)
	if (size && cd->show_tt_data) {
		if (cd->pip2_prot_active)
			tthe_print(cd, cd->input_buf, size, "TT_DATA_PIP2=");
		else
			tthe_print(cd, cd->input_buf, size, "TT_DATA=");
	}
#endif

	mutex_lock(&cd->system_lock);
	cd->hid_cmd_state = 0;
	mutex_unlock(&cd->system_lock);
	wake_up(&cd->wait_q);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_allow_enumeration
 *
 * SUMMARY: Determine if an enumeration or fully re-probe should perform when
 *  FW sentinel is seen.
 *
 * RETURN:
 *	true  = allow enumeration or fully re-probe
 *	false = skip enumeration and fully re-probe
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static inline bool pt_allow_enumeration(struct pt_core_data *cd)
{
	if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
		(!cd->hid_reset_cmd_state) &&
	    (cd->core_probe_complete) &&
	    (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
	    (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
	    (cd->mode == PT_MODE_OPERATIONAL)) {
		return true;
	}

	if ((!cd->hid_reset_cmd_state) &&
	    (cd->core_probe_complete) &&
	    (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
	    (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
	    (cd->active_dut_generation != DUT_PIP1_ONLY)) {
		return true;
	}

	pt_debug(cd->dev, DL_INFO,
		"%s: Dissallow - %s=%d %s=%d %s=0x%02X %s=%d\n",
		__func__,
		"hid_reset_cmd_state(0)", cd->hid_reset_cmd_state,
		"core_probe_complete(1)", cd->core_probe_complete,
		"hid_cmd_state(Not 0x02 or 0x39)", cd->hid_cmd_state,
		"active_dut_gen(0,2)", cd->active_dut_generation);
	return false;
}

/*******************************************************************************
 * FUNCTION: pt_is_touch_report
 *
 * SUMMARY: Determine if a report ID should be treated as a touch report
 *
 * RETURN:
 *	true  = report ID is a touch report
 *	false = report ID is not a touch report
 *
 * PARAMETERS:
 *	id - Report ID
 ******************************************************************************/
static bool pt_is_touch_report(int id)
{
	if (id == PT_PIP_TOUCH_REPORT_ID ||
	    id == PT_HID_PEN_REPORT_ID ||
	    id == PT_PIP_CAPSENSE_BTN_REPORT_ID ||
	    id == PT_PIP_SENSOR_DATA_REPORT_ID ||
	    id == PT_PIP_TRACKING_HEATMAP_REPORT_ID)
		return true;
	else
		return false;
}

/*******************************************************************************
 * FUNCTION: pt_parse_input
 *
 * SUMMARY: Parse the input data read from DUT due to IRQ. Handle data based
 *	on if its a response to a command or asynchronous touch data / reset
 *	sentinel. PIP2.x messages have additional error checking that is
 *	parsed (SEQ match from cmd to rsp, CRC valid).
 *	Look for special packets based on unique lengths:
 *		0 bytes  - APP(FW) reset sentinel or Gen5/6 BL sentinel
 *		2 bytes  - Empty buffer (PIP 1.7 and earlier)
 *		11 bytes - possible PIP2.x reset sentinel (TAG and SEQ must = 0)
 *		0xFFXX   - Empty buffer (PIP 1.7+)
 *	Queue a startup after any asynchronous FW reset sentinel is seen, unless
 *	the initial probe has not yet been done.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to core data
 ******************************************************************************/
static int pt_parse_input(struct pt_core_data *cd)
{
	int report_id;
	int cmd_id;
	int is_command = 0;
	int size;
	int print_size;
	bool touch_report = true;
	unsigned short calc_crc;
	unsigned short resp_crc;

	cd->fw_sys_mode_in_standby_state = false;
	size = get_unaligned_le16(&cd->input_buf[0]);
	print_size = size;
	pt_debug(cd->dev, DL_DEBUG, "<<< %s: IRQ Triggered, read len [%d]\n",
			__func__, print_size);
	if (print_size <= PT_MAX_INPUT)
		pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, print_size,
			"<<< Read buf");
	if (size == 0 ||
	   (size == 11 &&
	   (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] &
	    PIP2_RESP_SEQUENCE_MASK) == 0 &&
	   (cd->input_buf[PIP2_RESP_REPORT_ID_OFFSET] &
	    PIP2_CMD_COMMAND_ID_MASK) ==
	   PIP2_CMD_ID_STATUS)) {
		touch_report = false;
		cd->hw_detected = true;
		cd->bl_pip_ver_ready = false;
		cd->app_pip_ver_ready = false;
		if (size == 0) {
			mutex_lock(&cd->system_lock);
			cd->pip2_prot_active = false;
			if (cd->active_dut_generation == DUT_PIP1_ONLY) {
				/*
				 * For Gen5/6 this sentinel could be from
				 * the BL or FW. Attempt to set the correct
				 * mode based on the previous PIP command.
				 */
				if (cd->hid_cmd_state ==
				    PIP1_BL_CMD_ID_LAUNCH_APP + 1) {
					cd->mode = PT_MODE_OPERATIONAL;
					cd->startup_status =
					    STARTUP_STATUS_FW_RESET_SENTINEL;
				} else if (cd->hid_cmd_state ==
					PIP1_CMD_ID_START_BOOTLOADER + 1 ||
					cd->hid_reset_cmd_state) {
					cd->mode = PT_MODE_BOOTLOADER;
					cd->startup_status =
					    STARTUP_STATUS_BL_RESET_SENTINEL;
				} else {
					cd->mode = PT_MODE_UNKNOWN;
					cd->startup_status =
						STARTUP_STATUS_START;
				}
				cd->fw_system_mode = FW_SYS_MODE_UNDEFINED;
				pt_debug(cd->dev, DL_INFO,
					"%s: ATM Gen5/6 %s sentinel received\n",
					__func__,
					(cd->mode == PT_MODE_OPERATIONAL ?
					"FW" :
					(cd->mode == PT_MODE_BOOTLOADER ?
					"BL" : "Unknown")));
			} else {
				cd->mode = PT_MODE_OPERATIONAL;
				cd->fw_system_mode = FW_SYS_MODE_BOOT;
				cd->startup_status =
					STARTUP_STATUS_FW_RESET_SENTINEL;
				pt_debug(cd->dev, DL_INFO,
					"%s: ATM PT/TT FW sentinel received\n",
					__func__);
			}
			mutex_unlock(&cd->system_lock);
			if (pt_allow_enumeration(cd)) {
				if (cd->active_dut_generation == DUT_UNKNOWN) {
					pt_debug(cd->dev, DL_INFO,
					"%s: Queue Restart\n", __func__);
					pt_queue_restart(cd);
				} else {
					pt_debug(cd->dev, DL_INFO,
					"%s: Queue Enum\n", __func__);
					pt_queue_enum(cd);
				}
			} else {
				pt_debug(cd->dev, DL_INFO,
					"%s: Sentinel - No Queued Action\n",
					__func__);
			}

		} else {
			/* Sentinel must be from TT/TC BL */
			mutex_lock(&cd->system_lock);
			cd->pip2_prot_active = true;
			cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL;
			cd->mode = PT_MODE_BOOTLOADER;
			cd->sysinfo.ready = false;
			mutex_unlock(&cd->system_lock);
			pt_debug(cd->dev, DL_INFO,
				"%s: BL Reset sentinel received\n", __func__);
			if (cd->flashless_dut &&
			    cd->flashless_auto_bl == PT_ALLOW_AUTO_BL) {
				pt_debug(cd->dev, DL_INFO,
					"%s: BL to RAM for flashless DUT\n",
					__func__);
				kthread_run(start_fw_upgrade, cd, "pt_loader");
			}
		}
		mutex_lock(&cd->system_lock);
		memcpy(cd->response_buf, cd->input_buf, 2);
		if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) {
			mutex_unlock(&cd->system_lock);
			pt_debug(cd->dev, DL_WARN,
				"%s: Device Initiated Reset\n", __func__);
			wake_up(&cd->wait_q);
			return 0;
		}

		cd->hid_reset_cmd_state = 0;
		if (cd->hid_cmd_state == PIP1_CMD_ID_START_BOOTLOADER + 1 ||
		    cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 ||
		    cd->hid_cmd_state == PIP1_CMD_ID_USER_CMD + 1)
			cd->hid_cmd_state = 0;
		wake_up(&cd->wait_q);
		mutex_unlock(&cd->system_lock);
		return 0;
	} else if (size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) {
		/*
		 * This debug message below is used by PBATS to calculate the
		 * time from the last lift off IRQ to when FW exits LFT mode.
		 */
		touch_report = false;
		pt_debug(cd->dev, DL_WARN,
			"%s: DUT - Empty buffer detected\n", __func__);
		return 0;
	} else if (size > PT_MAX_INPUT || size < 0) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: DUT - Unexpected len field in active bus data!\n",
			__func__);
		return -EINVAL;
	}

	if (cd->pip2_prot_active) {
		pt_debug(cd->dev, DL_DEBUG,
			"%s: Decode PIP2.x Response\n", __func__);

		/* PIP2 does not have a report id, hard code it */
		report_id = 0x00;
		cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET];
		calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2);
		resp_crc  = cd->input_buf[size - 2] << 8;
		resp_crc |= cd->input_buf[size - 1];
		if ((cd->pip2_cmd_tag_seq !=
			(cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F)) &&
			(resp_crc != calc_crc) &&
			((cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]
				== PT_PIP_TOUCH_REPORT_ID) ||
			(cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]
				== PT_PIP_CAPSENSE_BTN_REPORT_ID))) {
			pt_debug(cd->dev, DL_WARN,
				"%s: %s %d %s\n",
				__func__,
				"Received PIP1 report id =",
				cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET],
				"when expecting a PIP2 report - IGNORE report");
			return 0;
		}
		is_command = 1;
		touch_report = false;
	} else {
		report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET];
		cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET];
	}

#ifdef TTDL_DIAGNOSTICS
	pt_debug(cd->dev, DL_DEBUG,
		"%s: pip2 = %d report_id: 0x%02X, cmd_code: 0x%02X\n",
		__func__, cd->pip2_prot_active, report_id,
		(cmd_id & PIP2_CMD_COMMAND_ID_MASK));
#endif /* TTDL_DIAGNOSTICS */

	if (report_id == PT_PIP_WAKEUP_REPORT_ID) {
		pt_wakeup_host(cd);
#ifdef TTHE_TUNER_SUPPORT
		tthe_print(cd, cd->input_buf, size, "TT_WAKEUP=");
#endif
		return 0;
	}
	mod_timer_pending(&cd->watchdog_timer, jiffies +
			msecs_to_jiffies(cd->watchdog_interval));

	/*
	 * If it is PIP2 response, the report_id has been set to 0,
	 * so it will not be parsed as a touch packet.
	 */
	if (!pt_is_touch_report(report_id)) {
		is_command = 1;
		touch_report = false;
	}
	if (unlikely(is_command)) {
		parse_command_input(cd, size);
		return 0;
	}
	if (touch_report)
		parse_touch_input(cd, size);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_read_input
 *
 * SUMMARY: Reads incoming data off of the active bus
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static int pt_read_input(struct pt_core_data *cd)
{
	struct device *dev = cd->dev;
	int rc = 0;
	int t;
	int retry = PT_BUS_READ_INPUT_RETRY_COUNT;

	/*
	 * Workaround for easywake failure
	 * Interrupt for easywake, wait for bus controller to wake
	 */
	mutex_lock(&cd->system_lock);

	if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) {
		if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_EASY_WAKING_ON) {
			mutex_unlock(&cd->system_lock);
			if (!dev->power.is_suspended)
				goto read;
			t = wait_event_timeout(cd->wait_q,
				(cd->wait_until_wake == 1),
				msecs_to_jiffies(2000));
#ifdef TTDL_DIAGNOSTICS
			if (IS_TMO(t)) {
				cd->bus_transmit_error_count++;
				pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
				pt_debug(dev, DL_ERROR,
					"%s: !!!I2C Transmission Error %d\n",
					__func__,
					cd->bus_transmit_error_count);
			}
#else
			if (IS_TMO(t))
				pt_queue_enum(cd);
#endif /* TTDL_DIAGNOSTICS */
			goto read;
		}
	}
	mutex_unlock(&cd->system_lock);

read:
	/* Try reading up to 'retry' times */
	while (retry-- != 0) {
		rc = pt_adap_read_default_nosize(cd, cd->input_buf,
			PT_MAX_INPUT);
		if (!rc) {
			pt_debug(dev, DL_DEBUG,
				"%s: Read input successfully\n", __func__);
			goto read_exit;
		}
		usleep_range(1000, 2000);
	}
	pt_debug(dev, DL_ERROR,
		"%s: Error getting report, rc=%d\n", __func__, rc);

read_exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_irq
 *
 * SUMMARY: Process all detected interrupts
 *
 * RETURN:
 *	IRQ_HANDLED - Finished processing the interrupt
 *
 * PARAMETERS:
 *       irq    - IRQ number
 *	*handle - pointer to core data struct
 ******************************************************************************/
irqreturn_t pt_irq(int irq, void *handle)
{
	struct pt_core_data *cd = handle;
	int rc = 0;

	if (!pt_check_irq_asserted(cd))
		return IRQ_HANDLED;

	rc = pt_read_input(cd);
#ifdef TTDL_DIAGNOSTICS
	cd->irq_count++;

	/* Used to calculate T-Refresh */
	if (cd->t_refresh_active) {
		if (cd->t_refresh_count == 0) {
			cd->t_refresh_time = jiffies;
			cd->t_refresh_count++;
		} else if (cd->t_refresh_count < cd->t_refresh_total) {
			cd->t_refresh_count++;
		} else {
			cd->t_refresh_active = 0;
			cd->t_refresh_time =
			jiffies_to_msecs(jiffies - cd->t_refresh_time);
		}
	}
#endif /* TTDL_DIAGNOSTICS */

	if (!rc)
		pt_parse_input(cd);

	return IRQ_HANDLED;
}


/*******************************************************************************
 * FUNCTION: _pt_subscribe_attention
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to subscribe themselves into the TTDL attention list
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to device structure
 *	 type - attention type enum
 *	*id   - ID of the module calling this function
 *	*func - callback function pointer to be called when notified
 *	 mode - attention mode
 ******************************************************************************/
int _pt_subscribe_attention(struct device *dev,
	enum pt_atten_type type, char *id, int (*func)(struct device *),
	int mode)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct atten_node *atten, *atten_new;

	atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL);
	if (!atten_new)
		return -ENOMEM;

	pt_debug(cd->dev, DL_INFO, "%s from '%s'\n", __func__,
		dev_name(cd->dev));

	spin_lock(&cd->spinlock);
	list_for_each_entry(atten, &cd->atten_list[type], node) {
		if (atten->id == id && atten->mode == mode) {
			spin_unlock(&cd->spinlock);
			kfree(atten_new);
			pt_debug(cd->dev, DL_INFO, "%s: %s=%p %s=%d\n",
				 __func__,
				 "already subscribed attention",
				 dev, "mode", mode);

			return 0;
		}
	}

	atten_new->id = id;
	atten_new->dev = dev;
	atten_new->mode = mode;
	atten_new->func = func;

	list_add(&atten_new->node, &cd->atten_list[type]);
	spin_unlock(&cd->spinlock);

	return 0;
}

/*******************************************************************************
 * FUNCTION: _pt_unsubscribe_attention
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to unsubscribe themselves from the TTDL attention list
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to device structure
 *	 type - attention type enum
 *	*id   - ID of the module calling this function
 *	*func - function pointer
 *	 mode - attention mode
 ******************************************************************************/
int _pt_unsubscribe_attention(struct device *dev,
	enum pt_atten_type type, char *id, int (*func)(struct device *),
	int mode)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct atten_node *atten, *atten_n;

	spin_lock(&cd->spinlock);
	list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) {
		if (atten->id == id && atten->mode == mode) {
			list_del(&atten->node);
			spin_unlock(&cd->spinlock);
			pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n",
				__func__,
				"unsub for atten->dev", atten->dev,
				"atten->mode", atten->mode);
			kfree(atten);
			return 0;
		}
	}
	spin_unlock(&cd->spinlock);

	return -ENODEV;
}

/*******************************************************************************
 * FUNCTION: _pt_request_exclusive
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request exclusive access
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev        - pointer to device structure
 *	 timeout_ms - timeout to wait for exclusive access
 ******************************************************************************/
static int _pt_request_exclusive(struct device *dev,
		int timeout_ms)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return request_exclusive(cd, (void *)dev, timeout_ms);
}

/*******************************************************************************
 * FUNCTION: _pt_release_exclusive
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to release exclusive access
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 ******************************************************************************/
static int _pt_release_exclusive(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return release_exclusive(cd, (void *)dev);
}

/*******************************************************************************
 * FUNCTION: _pt_request_reset
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request the DUT to be reset. Function returns err if refused or
 *	timeout occurs (Note: core uses fixed timeout period).
 *
 * NOTE: Function blocks until ISR occurs.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev     - pointer to device structure
 *	 protect - flag to call protected or non-protected
 ******************************************************************************/
static int _pt_request_reset(struct device *dev, int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int rc;

	rc = pt_dut_reset(cd, protect);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
			__func__, rc);
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_enum
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request TTDL to queue an enum. This function will return err
 *	if refused; if no error then enum has completed and system is in
 *	normal operation mode.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 *	 wait - boolean to determine if to wait for startup event
 ******************************************************************************/
static int _pt_request_enum(struct device *dev, bool wait)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_queue_enum(cd);

	if (wait)
		wait_event_timeout(cd->wait_q,
			cd->startup_state == STARTUP_NONE,
			msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT));

	return 0;
}

/*******************************************************************************
 * FUNCTION: _pt_request_sysinfo
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request the pointer to the system information structure. This
 *	function will return NULL if sysinfo has not been acquired from the
 *	DUT yet.
 *
 * RETURN: Pointer to the system information struct
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 ******************************************************************************/
struct pt_sysinfo *_pt_request_sysinfo(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int rc = 0;

	/* Attempt to get sysinfo if not ready and panel_id is from XY pin */
	if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
			!cd->sysinfo.ready) {
		rc = pt_hid_output_get_sysinfo_(cd);
		if (rc) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Error getting sysinfo rc=%d\n",
				__func__, rc);
		}
	}

	if (cd->sysinfo.ready)
		return &cd->sysinfo;

	return NULL;
}

/*******************************************************************************
 * FUNCTION: _pt_request_loader_pdata
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request the pointer to the loader platform data
 *
 * RETURN: Pointer to the loader platform data struct
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 ******************************************************************************/
static struct pt_loader_platform_data *_pt_request_loader_pdata(
		struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return cd->pdata->loader_pdata;
}

/*******************************************************************************
 * FUNCTION: _pt_request_start_wd
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request to start the TTDL watchdog
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 ******************************************************************************/
static int _pt_request_start_wd(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_start_wd_timer(cd);
	return 0;
}

/*******************************************************************************
 * FUNCTION: _pt_request_stop_wd
 *
 * SUMMARY: Function pointer included in core_cmds to allow other modules
 *	to request to stop the TTDL watchdog
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev - pointer to device structure
 ******************************************************************************/
static int _pt_request_stop_wd(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_stop_wd_timer(cd);
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_launch_app
 *
 * SUMMARY: Sends the PIP2 EXECUTE command to launch the APP and then wait for
 *	the FW reset sentinel to indicate the function succeeded.
 *
 * NOTE: Calling this function when the DUT is in Application mode WILL result
 *  in a timeout delay and with the DUT being reset with an XRES.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 *	 protect - flag to call protected or non-protected
 ******************************************************************************/
static int pt_pip2_launch_app(struct device *dev, int protect)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u16 actual_read_len;
	u16 tmp_startup_status = cd->startup_status;
	u8 read_buf[12];
	u8 status;
	int time = 0;
	int rc = 0;

	mutex_lock(&cd->system_lock);
	cd->startup_status = STARTUP_STATUS_START;
	pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
	mutex_unlock(&cd->system_lock);

	rc = _pt_request_pip2_send_cmd(dev, protect,
		PIP2_CMD_ID_EXECUTE, NULL, 0, read_buf,
		&actual_read_len);
	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: PIP2 EXECUTE cmd failed rc = %d\n",
			__func__, rc);
	} else {
		status = read_buf[PIP2_RESP_STATUS_OFFSET];

		/* Test for no or invalid image in FLASH, no point to reset */
		if (status == PIP2_RSP_ERR_INVALID_IMAGE) {
			rc = status;
			goto exit;
		}

		/* Any other boot failure */
		if (status != 0) {
			pt_debug(dev, DL_ERROR,
				"%s: FW did not EXECUTE, status = %d\n",
				__func__, status);
			rc = status;
		}
	}

	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: Failed to launch APP, XRES DUT rc = %d\n",
			__func__, rc);
		goto exit;
	}

	while ((cd->startup_status == STARTUP_STATUS_START) && time < 240) {
		msleep(20);
		pt_debug(cd->dev, DL_INFO, "%s: wait for %d for enum=0x%04X\n",
			__func__, time, cd->startup_status);
		time += 20;
	}
	if (cd->startup_status == STARTUP_STATUS_START) {
		pt_debug(cd->dev, DL_WARN,
			"%s: TMO waiting for FW reset sentinel\n", __func__);
		rc = -ETIME;
	}

exit:
	if (cd->startup_status == STARTUP_STATUS_START) {
		/* Reset to original state because we could be stuck in BL */
		mutex_lock(&cd->system_lock);
		cd->startup_status = tmp_startup_status;
		mutex_unlock(&cd->system_lock);
	}
	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_request_pip2_launch_app
 *
 * SUMMARY: Calls pt_pip2_launch_app() when configured to. A small delay is
 *	inserted to ensure the reset has allowed the BL reset sentinel to be
 *	consumed.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd - pointer to core data structure
 ******************************************************************************/
static int _pt_request_pip2_launch_app(struct device *dev, int protect)
{
	return pt_pip2_launch_app(dev, protect);
}

/*******************************************************************************
 * FUNCTION: _pt_request_wait_for_enum_state
 *
 * SUMMARY: Loops for up to timeout waiting for the startup_status to reach
 *	the state passed in or STARTUP_STATUS_COMPLETE whichever comes first
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *cd      - pointer to core data structure
 *	 timeout - timeout for how long to wait
 *	 state   - enum state to wait for
 ******************************************************************************/
static int _pt_request_wait_for_enum_state(struct device *dev, int timeout,
	int state)
{
	int rc = 0;
	int t;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	t = wait_event_timeout(cd->wait_q,
		(cd->startup_status & state) || (cd->startup_status & 0x0100),
		msecs_to_jiffies(timeout));
	if (IS_TMO(t)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: TMO waiting for enum state 0x%04X in %dms\n",
			__func__, state, timeout);
		pt_debug(cd->dev, DL_WARN,
			"%s: enum state reached 0x%04X\n",
			__func__, cd->startup_status);
		rc = -ETIME;
	} else if (cd->startup_status & state) {
		pt_debug(cd->dev, DL_INFO,
			 "%s: Enum state reached: enum=0x%04X in %dms\n",
			 __func__, cd->startup_status,
			 (t == 1) ? timeout : (timeout - jiffies_to_msecs(t)));
	} else {
		if (t == 1) {
			pt_debug(
			    cd->dev, DL_ERROR,
			    "%s: TMO waiting for enum state 0x%04X in %dms\n",
			    __func__, state, timeout);
			rc = -ETIME;
		} else {
			pt_debug(
			    cd->dev, DL_ERROR,
			    "%s: Enum state 0x%04X not reached in %dms\n",
			    __func__, state, timeout - jiffies_to_msecs(t));
			rc = -EINVAL;
		}
		pt_debug(cd->dev, DL_INFO, "%s: enum state reached 0x%04X\n",
			 __func__, cd->startup_status);
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_wake_device_from_deep_sleep_
 *
 * SUMMARY: Call the set_power function and set the DUT to wake up from
 *  deep sleep.
 *
 * RETURN:
 *	 0 = success
 *	!0 = error
 *
 * PARAMETERS:
 *      *cd - pointer to core data
 ******************************************************************************/
static int pt_core_wake_device_from_deep_sleep_(
		struct pt_core_data *cd)
{
	int rc;

	rc = pt_hid_cmd_set_power_(cd, HID_POWER_ON);
	if (rc)
		rc =  -EAGAIN;

	/* Prevent failure on sequential wake/sleep requests from OS */
	msleep(20);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_wake_device_from_easy_wake_
 *
 * SUMMARY: Wake up device from Easy-Wake state.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd)
{
	int rc = 0;


	rc = pt_core_wake_device_from_deep_sleep_(cd);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_restore_parameters_
 *
 * SUMMARY: This function sends all RAM parameters stored in the linked list
 *	back to the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer the core data structure
 ******************************************************************************/
static int pt_restore_parameters_(struct pt_core_data *cd)
{
	struct param_node *param;
	int rc = 0;

	if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
		goto exit;

	spin_lock(&cd->spinlock);
	list_for_each_entry(param, &cd->param_list, node) {
		spin_unlock(&cd->spinlock);
		pt_debug(cd->dev, DL_INFO, "%s: Parameter id:%d value:%d\n",
			 __func__, param->id, param->value);
		rc = pt_pip_set_param_(cd, param->id,
			param->value, param->size);
		if (rc)
			goto exit;
		spin_lock(&cd->spinlock);
	}
	spin_unlock(&cd->spinlock);
exit:
	return rc;
}


/*******************************************************************************
 * FUNCTION: pt_pip2_exit_bl_
 *
 * SUMMARY: Attempt to exit the BL and run the application, taking into account
 *	a DUT that may not have flash and will need FW to be loaded into RAM
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer the core data structure
 *	*status_str - pointer to optional status string buffer
 *	 buf_size   - size of status_str buffer
 ******************************************************************************/
int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size)
{
	int rc;
	int wait_time = 0;
	u8 mode = PT_MODE_UNKNOWN;
	bool load_status_str = false;

	/*
	 * Below function has protective call to ensure no enum is still on
	 * going, while this kind of protection should be applied widely in
	 * future (TODO).
	 */
	rc = pt_pip2_get_mode_sysmode(cd, &mode, NULL);

	if (status_str && buf_size <= 50)
		load_status_str = true;

	if (mode == PT_MODE_BOOTLOADER) {
		if (cd->flashless_dut == 1) {
			rc = pt_hw_hard_reset(cd);
		} else {
			rc = pt_pip2_launch_app(cd->dev,
				PT_CORE_CMD_UNPROTECTED);
			if (rc == PIP2_RSP_ERR_INVALID_IMAGE) {
				pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
				__func__, "Invalid image in FLASH rc", rc);
			} else if (rc) {
				pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
				__func__, "Failed to launch app rc", rc);
			}
		}

		if (!rc) {
			if (cd->flashless_dut == 1) {
				/* Wait for BL to complete before enum */
				rc = _pt_request_wait_for_enum_state(cd->dev,
					4000, STARTUP_STATUS_FW_RESET_SENTINEL);
				if (rc && load_status_str) {
					strlcpy(status_str, "No FW sentinel after BL",
						sizeof(*status_str)*PT_STATUS_STR_LEN);
					goto exit;
				}
			}

			/*
			 * If the host wants to interact with the FW or do a
			 * forced calibration, the FW must be out of BOOT mode
			 * and the system information must have been retrieved.
			 * Reaching the FW_OUT_OF_BOOT state guarantees both.
			 * If, however, the enumeration does not reach this
			 * point, the DUT may still be in APP mode so test
			 * for all conditions.
			 */
			rc = _pt_request_wait_for_enum_state(cd->dev, 4500,
				STARTUP_STATUS_FW_OUT_OF_BOOT);
			if (!rc || cd->startup_status >=
			    STARTUP_STATUS_FW_RESET_SENTINEL) {
				mutex_lock(&cd->system_lock);
				cd->mode = PT_MODE_OPERATIONAL;
				mutex_unlock(&cd->system_lock);
			}
			if (rc) {
				pt_debug(cd->dev, DL_WARN, "%s: %s: 0x%04X\n",
					__func__, "Failed to enum with DUT",
					cd->startup_status);
				if (load_status_str && !(cd->startup_status &
				    STARTUP_STATUS_FW_OUT_OF_BOOT)) {
					strlcpy(status_str, "FW Stuck in Boot mode",
						sizeof(*status_str)*PT_STATUS_STR_LEN);
					goto exit;
				}
			}

			/*
			 * The coming FW sentinel could wake up the event
			 * queue, which has chance to be taken by next command
			 * wrongly. Following delay is a workaround to avoid
			 * this issue for most situations.
			 */
			msleep(20);

			pt_start_wd_timer(cd);
		}
		if (load_status_str) {
			if (rc == PIP2_RSP_ERR_INVALID_IMAGE)
				strlcpy(status_str, "Failed - Invalid image in FLASH",
					sizeof(*status_str)*PT_STATUS_STR_LEN);
			else if (!rc)
				strlcpy(status_str, "Entered APP from BL mode",
					sizeof(*status_str)*PT_STATUS_STR_LEN);
			else
				strlcpy(status_str, "Failed to enter APP from BL mode",
					sizeof(*status_str)*PT_STATUS_STR_LEN);
		}
	} else if (mode == PT_MODE_OPERATIONAL) {
		mutex_lock(&cd->system_lock);
		cd->mode = mode;
		mutex_unlock(&cd->system_lock);
		rc = pt_poll_for_fw_exit_boot_mode(cd, 1500, &wait_time);
		if (load_status_str) {
			if (!rc)
				strlcpy(status_str, "Already in APP mode",
					sizeof(*status_str)*PT_STATUS_STR_LEN);
			else
				strlcpy(status_str, "Already in APP mode - FW stuck in Boot mode",
					sizeof(*status_str)*PT_STATUS_STR_LEN);
		}
	} else if (rc || mode == PT_MODE_UNKNOWN) {
		mutex_lock(&cd->system_lock);
		cd->mode = mode;
		mutex_unlock(&cd->system_lock);
		if (load_status_str)
			strlcpy(status_str, "Failed to determine active mode",
				sizeof(*status_str)*PT_STATUS_STR_LEN);
	}

exit:
	if (!rc)
		pt_start_wd_timer(cd);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_exit_bl
 *
 * SUMMARY: Wrapper function for _pt_pip2_exit_bl that guarantees exclusive
 *	access.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd         - pointer the core data structure
 *	*status_str - pointer to optional status string buffer
 *	 buf_size   - size of status_str buffer
 ******************************************************************************/
int pt_pip2_exit_bl(struct pt_core_data *cd, u8 *status_str, int buf_size)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
			 "%s: fail get exclusive ex=%p own=%p\n", __func__,
			 cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_pip2_exit_bl_(cd, status_str, buf_size);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
			 __func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: _fast_startup
 *
 * SUMMARY: Perform fast startup after resume device by power on/off stratergy.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd   - pointer the core data structure
 ******************************************************************************/
static int _fast_startup(struct pt_core_data *cd)
{
	int retry = PT_CORE_STARTUP_RETRY_COUNT;
	int rc = 0;
	u8 mode = PT_MODE_UNKNOWN;
	struct pt_hid_desc hid_desc;
	int wait_time = 0;

	memset(&hid_desc, 0, sizeof(hid_desc));
reset:
	if (retry != PT_CORE_STARTUP_RETRY_COUNT)
		pt_debug(cd->dev, DL_INFO, "%s: Retry %d\n", __func__,
			 PT_CORE_STARTUP_RETRY_COUNT - retry);

	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
		pt_debug(cd->dev, DL_INFO, "%s: PIP1 Enumeration start\n",
			 __func__);
		pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);

		rc = pt_get_hid_descriptor_(cd, &hid_desc);
		if (rc < 0) {
			pt_debug(cd->dev, DL_ERROR,
				 "%s: Error on getting HID descriptor r=%d\n",
				 __func__, rc);
			if (retry--)
				goto reset;
			goto exit;
		}
		cd->mode = pt_get_mode(cd, &hid_desc);

		if (cd->mode == PT_MODE_BOOTLOADER) {
			pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
				 __func__);
			rc = pt_hid_output_bl_launch_app_(cd);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					 "%s: Error on launch app r=%d\n",
					 __func__, rc);
				if (retry--)
					goto reset;
				goto exit;
			}
			rc = pt_get_hid_descriptor_(cd, &hid_desc);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					 "%s: Error on getting HID descriptor r=%d\n",
					 __func__, rc);
				if (retry--)
					goto reset;
				goto exit;
			}
			cd->mode = pt_get_mode(cd, &hid_desc);
			if (cd->mode == PT_MODE_BOOTLOADER) {
				if (retry--)
					goto reset;
				goto exit;
			}
		}

		cd->startup_status |= STARTUP_STATUS_GET_DESC;
		cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
	} else {
		pt_debug(cd->dev, DL_INFO, "%s: PIP2 Enumeration start\n",
			 __func__);

		if (retry == PT_CORE_STARTUP_RETRY_COUNT) {
			/* Wait for any sentinel before first try */
			rc = _pt_request_wait_for_enum_state(
				cd->dev, 150,
				STARTUP_STATUS_BL_RESET_SENTINEL |
				STARTUP_STATUS_FW_RESET_SENTINEL);
			if (rc)
				pt_debug(cd->dev, DL_ERROR,
					 "%s: No Sentinel detected rc = %d\n",
					 __func__, rc);
		} else
			pt_flush_bus_if_irq_asserted(cd,
						     PT_FLUSH_BUS_BASED_ON_LEN);

		rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
		if (rc) {
			pt_debug(cd->dev, DL_ERROR,
				 "%s: Get mode failed, mode unknown\n",
				 __func__);
			mode = PT_MODE_UNKNOWN;
		}
		cd->mode = mode;

		if (cd->mode == PT_MODE_BOOTLOADER) {
			pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
				 __func__);
			rc = pt_pip2_exit_bl_(cd, NULL, 0);
			if (rc) {
				pt_debug(cd->dev, DL_ERROR,
					 "%s Failed to exit bootloader\n",
					 __func__);
				msleep(50);
				rc = -ENODEV;
				if (retry--)
					goto reset;
				goto exit;
			} else {
				pt_debug(cd->dev, DL_INFO,
					 "%s: Exit bootloader successfully\n",
					 __func__);
			}

			if (cd->mode != PT_MODE_OPERATIONAL) {
				pt_debug(cd->dev, DL_WARN,
					 "%s: restore mode failure mode = %d\n",
					 __func__, cd->mode);
				if (retry--)
					goto reset;
				goto exit;
			}
		}
		cd->startup_status |= STARTUP_STATUS_GET_DESC;
	}

	/* FW cannot handle most PIP cmds when it is still in BOOT mode */
	rc = _pt_poll_for_fw_exit_boot_mode(cd, 500, &wait_time);
	if (!rc) {
		cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
		pt_debug(cd->dev, DL_WARN,
			"%s: Exit FW BOOT Mode after %dms\n",
			__func__, wait_time);
	} else {
		pt_debug(cd->dev, DL_WARN,
			"%s: FW stuck in BOOT Mode after %dms\n",
			__func__, wait_time);
		goto exit;
	}

	if (!cd->sysinfo.ready) {
		rc = pt_hid_output_get_sysinfo_(cd);
		if (rc) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Error on getting sysinfo r=%d\n",
				__func__, rc);
			if (retry--)
				goto reset;
			goto exit;
		}
	}
	cd->startup_status |= STARTUP_STATUS_GET_SYS_INFO;

	rc = pt_restore_parameters_(cd);
	if (rc)
		pt_debug(cd->dev, DL_ERROR,
			"%s: failed to restore parameters rc=%d\n",
			__func__, rc);
	else
		cd->startup_status |= STARTUP_STATUS_RESTORE_PARM;

exit:
	cd->startup_status |= STARTUP_STATUS_COMPLETE;
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_poweron_device_
 *
 * SUMMARY: Power on device, enable IRQ, and then perform a fast startup.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_poweron_device_(struct pt_core_data *cd)
{
	struct device *dev = cd->dev;
	int rc = 0;

	/*
	 * After power on action, the chip can general FW sentinel. It can
	 * trigger an enumeration without hid_reset_cmd_state flag. Since the
	 * _fast_startup() can perform a quick enumeration too, here doesn't
	 * need another enumeration.
	 */
	mutex_lock(&cd->system_lock);
	cd->startup_status = STARTUP_STATUS_START;
	cd->hid_reset_cmd_state = 1;
	mutex_unlock(&cd->system_lock);

	rc = cd->cpdata->power(cd->cpdata, 1, dev, 0);
	if (rc < 0) {
		pt_debug(dev, DL_ERROR, "%s: HW Power up fails r=%d\n",
			__func__, rc);
		goto exit;
	}

	if (!cd->irq_enabled) {
		cd->irq_enabled = true;
		enable_irq(cd->irq);
	}

	/* TBD: following function doesn't update startup_status */
	rc = _fast_startup(cd);
exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_wake_device_from_deep_standby_
 *
 * SUMMARY: Reset device, and then trigger a full enumeration.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_wake_device_from_deep_standby_(struct pt_core_data *cd)
{
	int rc;

	rc = pt_dut_reset_and_wait(cd);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
			__func__, rc);
		goto exit;
	}

	rc = _fast_startup(cd);
exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_easywake_off_
 *
 * SUMMARY: Resume the device with a power on or wake from deep sleep based on
 *  the configuration in the core platform data structure.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_easywake_off_(struct pt_core_data *cd)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);
	if (cd->sleep_state == SS_EASY_WAKING_OFF) {
		mutex_unlock(&cd->system_lock);
		pt_debug(cd->dev, DL_INFO,
			"%s - %d skip wakeoff\n", __func__, cd->sleep_state);
		return 0;
	}
	mutex_unlock(&cd->system_lock);

	if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) {
		if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
			rc = pt_core_wake_device_from_easy_wake_(cd);
		if (rc < 0)
			pt_debug(cd->dev, DL_ERROR,
				"%s - %d failed %d\n", __func__, rc);
	}

	mutex_lock(&cd->system_lock);
	cd->sleep_state = SS_EASY_WAKING_OFF;
	mutex_unlock(&cd->system_lock);
	pt_start_wd_timer(cd);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_easywake_off
 *
 * SUMMARY: Protected call to pt_core_easywake_off by exclusive access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_easywake_off(struct pt_core_data *cd)
{
	int rc;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_core_easywake_off_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
			__func__);
	else
		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
			__func__);

	return rc;
}


/*******************************************************************************
 * FUNCTION: pt_core_wake_
 *
 * SUMMARY: Resume the device with a power on or wake from deep sleep based on
 *  the configuration in the core platform data structure.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_wake_(struct pt_core_data *cd)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);

	if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING) {
		mutex_unlock(&cd->system_lock);
		pt_debug(cd->dev, DL_INFO,
			"%s - skip wake sleep state %d\n", __func__, cd->sleep_state);
		return 0;
	} else {
		cd->sleep_state = SS_WAKING;
	}
	mutex_unlock(&cd->system_lock);

	if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) {
		if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
			pt_debug(cd->dev, DL_INFO,
				"%s: Entering into poweron mode:\n", __func__);
			rc = pt_core_poweron_device_(cd);
			if (rc < 0)
				pr_err("%s: Poweron error detected: rc=%d\n",
					__func__, rc);
		}
		else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY)
			rc = pt_core_wake_device_from_deep_standby_(cd);
		else /* Default action to exit DeepSleep */
			rc = pt_core_wake_device_from_deep_sleep_(cd);
	}

	mutex_lock(&cd->system_lock);
	cd->sleep_state = SS_SLEEP_OFF;
	mutex_unlock(&cd->system_lock);

	pt_start_wd_timer(cd);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_wake_
 *
 * SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static int pt_core_wake(struct pt_core_data *cd)
{
	int rc = 0;

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		return rc;
	}

	rc = pt_core_wake_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
			__func__);
	else
		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
			__func__);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_get_ic_crc_
 *
 * SUMMARY: This function retrieves the config block CRC
 *
 * NOTE: The post condition of calling this function will be that the DUT will
 *  be in SCANNINING mode if no failures occur
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd   - pointer the core data structure
 *	 ebid - enumerated block ID
 ******************************************************************************/
static int pt_get_ic_crc_(struct pt_core_data *cd, u8 ebid)
{
	struct pt_sysinfo *si = &cd->sysinfo;
	int rc = 0;
	u8 status;
	u16 calculated_crc = 0;
	u16 stored_crc = 0;

	rc = pt_pip_suspend_scanning_(cd);
	if (rc)
		goto error;

	rc = pt_pip_verify_config_block_crc_(cd, ebid, &status,
			&calculated_crc, &stored_crc);
	if (rc)
		goto exit;

	if (status) {
		rc = -EINVAL;
		goto exit;
	}

	si->ttconfig.crc = stored_crc;

exit:
	pt_pip_resume_scanning_(cd);
error:
	pt_debug(cd->dev, DL_INFO,
		"%s: CRC: ebid:%d, calc:0x%04X, stored:0x%04X, rc=%d\n",
		__func__, ebid, calculated_crc, stored_crc, rc);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_read_gpio
 *
 * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and stores the 32 gpio
 *	bits into the passed in variable
 *
 * NOTE: PIP2 READ_GPIO command is only supported in bootloader
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev    - pointer to device structure
 *	*status - pointer to where the command response status is stored
 *	*gpio   - pointer to device attributes structure
 ******************************************************************************/
static int pt_pip2_read_gpio(struct device *dev, u8 *status, u32 *gpio)
{
	int rc = 0;
	u16 actual_read_len;
	u8 read_buf[12];
	u8 tmp_status = 0;
	u8 index = PIP2_RESP_STATUS_OFFSET;

	memset(read_buf, 0, ARRAY_SIZE(read_buf));
	rc = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_READ_GPIO,
		NULL, 0, read_buf, &actual_read_len);

	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: Failed to send PIP2 READ_GPIO cmd\n", __func__);
		rc = -ECOMM;
	} else {
		tmp_status = read_buf[index];
	}

	if (status)
		*status = tmp_status;

	if (!rc && gpio && (tmp_status == 0)) {
		*gpio = ((read_buf[index + 4] << 24) |
			 (read_buf[index + 3] << 16) |
			 (read_buf[index + 2] << 8) |
			 (read_buf[index + 1]));
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_get_panel_id_by_gpio
 *
 * SUMMARY: Wrapper function to call pt_pip2_read_gpio() to get panel ID
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd    - pointer the core data structure
 *	*pid   - pointer to store panel ID
 ******************************************************************************/
static int _pt_pip2_get_panel_id_by_gpio(struct pt_core_data *cd, u8 *pid)
{
	u32 gpio_value = 0;
	u8 status = 0;
	u8 panel_id = PANEL_ID_NOT_ENABLED;
	int rc = 0;

	if (!pid)
		return -ENOMEM;

	rc = pt_pip2_read_gpio(cd->dev, &status, &gpio_value);
	if (!rc) {
		if (status == 0) {
			panel_id = (gpio_value & PT_PANEL_ID_BITMASK) >>
			       PT_PANEL_ID_SHIFT;
			pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X %s=0x%08X\n",
				 __func__,
				 "BL mode PID", panel_id, "gpio", gpio_value);
			*pid = panel_id;
		} else {
			pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
				 __func__,
				 "BL read gpio failed status", status);
		}
	} else {
		pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
			 __func__,
			 "BL read gpio failed status", status);
	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_enum_with_dut_
 *
 * SUMMARY: This function does the full enumeration of the DUT with TTDL.
 *	The core data (cd) startup_status will store, as a bitmask, each
 *	state of the enumeration process. The startup will be attempted
 *	PT_CORE_STARTUP_RETRY_COUNT times before giving up.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd    - pointer the core data structure
 *	 reset - Flag to reset the DUT before attempting to enumerate
 *	*status - poionter to store the enum status bitmask flags
 ******************************************************************************/
static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset,
	u32 *enum_status)
{
	int try = 1;
	int rc = 0;
	int wait_time = 0;
	bool detected = false;
	u8 return_data[8];
	u8 mode = PT_MODE_UNKNOWN;
	u8 pid = PANEL_ID_NOT_ENABLED;
	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
	struct pt_hid_desc hid_desc;

	memset(&hid_desc, 0, sizeof(hid_desc));
#ifdef TTHE_TUNER_SUPPORT
	tthe_print(cd, NULL, 0, "enter startup");
#endif
	pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n",
		__func__, cd->startup_status, reset);
	pt_stop_wd_timer(cd);
reset:
	if (try > 1)
		pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n",
			__func__, try);
	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
		pt_debug(cd->dev, DL_INFO,
			"%s: PIP1 Enumeration start\n", __func__);

		/* Only reset the DUT after the first try */
		if (reset || (try > 1)) {
			/*
			 * Reset hardware only for Legacy parts. Skip for TT/TC
			 * parts because if the FW image was loaded directly
			 * to SRAM issueing a reset ill wipe out what was just
			 * loaded.
			 */
			rc = pt_dut_reset_and_wait(cd);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Error on h/w reset r=%d\n",
					__func__, rc);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				goto exit;
			}
			/* sleep to allow FW to be launched if available */
			msleep(120);
		}
		rc = pt_get_hid_descriptor_(cd, &hid_desc);
		if (rc < 0) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Error getting HID Descriptor r=%d\n",
				__func__, rc);
			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
				goto reset;
			rc = -EIO;
			goto exit;
		}
		detected = true;
		cd->mode = pt_get_mode(cd, &hid_desc);
		/*
		 * Most systems do not use an XY pin as the panel_id and so
		 * the BL is used to retrieve the panel_id, however for
		 * systems that do use an XY pin, the panel_id MUST be
		 * retrieved from the system information when running FW
		 * (done below) and so this section of code is skipped.
		 * Entering the BL here is only needed on XY_PIN systems.
		 */
		if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
			if (cd->mode == PT_MODE_OPERATIONAL) {
				rc = pt_pip_start_bootloader_(cd);
				if (rc < 0) {
					pt_debug(cd->dev, DL_ERROR,
						"%s: Error on start bootloader r=%d\n",
						__func__, rc);
					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
						goto reset;
					goto exit;
				}
				cd->mode = PT_MODE_BOOTLOADER;
				pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
					__func__);
			}
			rc = pt_hid_output_bl_get_information_(cd, return_data);
			if (!rc) {
				cd->bl_info.ready = true;
				cd->bl_info.chip_id =
				    get_unaligned_le16(&return_data[2]);
				pt_debug(cd->dev, DL_INFO, "%s: chip ID %04X\n",
					 __func__, cd->bl_info.chip_id);
			} else {
				pt_debug(cd->dev, DL_ERROR,
					 "%s: failed to get chip ID, r=%d\n",
					 __func__, rc);
			}
			rc = pt_hid_output_bl_get_panel_id_(cd, &pid);
			mutex_lock(&cd->system_lock);
			if (!rc) {
				cd->pid_for_loader = pid;
				pt_debug(cd->dev, DL_INFO, "%s: Panel ID: 0x%02X\n",
					__func__, cd->pid_for_loader);
			} else {
				cd->pid_for_loader = PANEL_ID_NOT_ENABLED;
				pt_debug(cd->dev, DL_WARN,
					"%s: Read Failed, disable Panel ID: 0x%02X\n",
					__func__, cd->pid_for_loader);
			}
			mutex_unlock(&cd->system_lock);
		}

		/* Exit BL due to XY_PIN case or any other cause to be in BL */
		if (cd->mode == PT_MODE_BOOTLOADER) {
			rc = pt_hid_output_bl_launch_app_(cd);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Error on launch app r=%d\n",
					__func__, rc);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				rc = -ENODEV;
				goto exit;
			}

			/* sleep to allow FW to be launched if available */
			msleep(120);

			/* Ensure the DUT is now in Application mode */
			rc = pt_get_hid_descriptor_(cd, &hid_desc);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Error getting HID Desc r=%d\n",
					__func__, rc);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				rc = -EIO;
				goto exit;
			}

			cd->mode = pt_get_mode(cd, &hid_desc);
			if (cd->mode == PT_MODE_BOOTLOADER) {
				pt_debug(cd->dev, DL_WARN,
					"%s: Error confiming exit BL\n",
					__func__);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				rc = -ENODEV;
				goto exit;
			}
		}
		pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__);
		cd->mode = PT_MODE_OPERATIONAL;
		*enum_status |= STARTUP_STATUS_GET_DESC;
		*enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
	} else {
		/* Generation is PIP2 Capable */
		pt_debug(cd->dev, DL_INFO,
			"%s: PIP2 Enumeration start\n", __func__);

		rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
		if (rc) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Get mode failed, mode unknown\n",
				__func__);
			mode = PT_MODE_UNKNOWN;
		} else
			detected = true;

		cd->mode = mode;
		switch (cd->mode) {
		case PT_MODE_OPERATIONAL:
			pt_debug(cd->dev, DL_INFO,
				"%s: Operational mode\n", __func__);
			if (cd->app_pip_ver_ready == false) {
				rc = pt_pip2_get_version_(cd);
				if (!rc)
					cd->app_pip_ver_ready = true;
				else {
					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
						goto reset;
					goto exit;
				}
			}
			break;
		case PT_MODE_BOOTLOADER:
			pt_debug(cd->dev, DL_INFO,
				"%s: Bootloader mode\n", __func__);
			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
				rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid);
				mutex_lock(&cd->system_lock);
				if (!rc) {
					cd->pid_for_loader = pid;
					pt_debug(cd->dev, DL_INFO,
						 "%s: Panel ID: 0x%02X\n",
						 __func__, cd->pid_for_loader);
				} else {
					cd->pid_for_loader =
					    PANEL_ID_NOT_ENABLED;
					pt_debug(cd->dev, DL_WARN,
						 "%s: Read Failed, disable Panel ID: 0x%02X\n",
						 __func__, cd->pid_for_loader);
				}
				mutex_unlock(&cd->system_lock);
			}

			if (cd->bl_pip_ver_ready == false) {
				rc = pt_pip2_get_version_(cd);
				if (!rc)
					cd->bl_pip_ver_ready = true;
				else {
					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
						goto reset;
					goto exit;
				}
			}

			/*
			 * Launch app command will fail in flashless mode.
			 * Skip launch app command here to save time for
			 * enumeration flow.
			 */
			if (cd->flashless_dut)
				goto exit;

			/*
			 * pt_pip2_launch_app() is needed here instead of
			 * pt_pip2_exit_bl() because exit_bl will cause several
			 * hw_resets to occur and the auto BL on a flashless
			 * DUT will fail.
			 */
			rc = pt_pip2_launch_app(cd->dev,
				PT_CORE_CMD_UNPROTECTED);
			if (rc) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Error on launch app r=%d\n",
					__func__, rc);
				msleep(50);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				goto exit;
			}

			/*
			 * IRQ thread can be delayed if the serial log print is
			 * enabled. It causes next command to get wrong response
			 * Here the delay is to ensure pt_parse_input() to be
			 * finished.
			 */
			msleep(60);
			/* Check and update the mode */
			rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
			if (rc) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Get mode failed, mode unknown\n",
					__func__);
				mode = PT_MODE_UNKNOWN;
			}

			cd->mode = mode;
			if (cd->mode == PT_MODE_OPERATIONAL) {
				pt_debug(cd->dev, DL_INFO,
					 "%s: Launched to Operational mode\n",
					 __func__);
			} else if (cd->mode == PT_MODE_BOOTLOADER) {
				pt_debug(cd->dev, DL_ERROR,
					 "%s: Launch failed, Bootloader mode\n",
					 __func__);
				goto exit;
			} else if (cd->mode == PT_MODE_UNKNOWN) {
				pt_debug(cd->dev, DL_ERROR,
					 "%s: Launch failed, Unknown mode\n",
					 __func__);
				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
					goto reset;
				goto exit;
			}
			if (cd->app_pip_ver_ready == false) {
				rc = pt_pip2_get_version_(cd);
				if (!rc)
					cd->app_pip_ver_ready = true;
				else {
					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
						goto reset;
					goto exit;
				}
			}
			break;
		default:
			pt_debug(cd->dev, DL_ERROR,
				"%s: Unknown mode\n", __func__);
			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
				goto reset;
			goto exit;
		}
		*enum_status |= STARTUP_STATUS_GET_DESC;
	}

	pt_init_pip_report_fields(cd);
	*enum_status |= STARTUP_STATUS_GET_RPT_DESC;
	if (!cd->features.easywake)
		cd->easy_wakeup_gesture = PT_CORE_EWG_NONE;

	pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__);
	rc = pt_hid_output_get_sysinfo_(cd);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Error on getting sysinfo r=%d\n", __func__, rc);
		if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
			goto reset;
		goto exit;
	}
	*enum_status |= STARTUP_STATUS_GET_SYS_INFO;
	/* FW cannot handle most PIP cmds when it is still in BOOT mode */
	rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time);
	if (!rc) {
		*enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
		pt_debug(cd->dev, DL_WARN,
			"%s: Exit FW BOOT Mode after %dms\n",
			__func__, wait_time);
	} else {
		pt_debug(cd->dev, DL_WARN,
			"%s: FW stuck in BOOT Mode after %dms\n",
			__func__, wait_time);
		goto exit;
	}

	pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n",
			__func__,
			cd->sysinfo.ttdata.pip_ver_major,
			cd->sysinfo.ttdata.pip_ver_minor);
	rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: DUT Config block CRC failure rc=%d\n",
			__func__, rc);
		if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
			goto reset;
	} else {
		_pt_get_fw_sys_mode(cd, &sys_mode, NULL);
		if (sys_mode != FW_SYS_MODE_SCANNING) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: scan state: %d, retry: %d\n",
				__func__, sys_mode, try);
			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
				goto reset;
		} else
			*enum_status |= STARTUP_STATUS_GET_CFG_CRC;
	}
	rc = pt_restore_parameters_(cd);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Failed to restore parameters rc=%d\n",
			__func__, rc);
	} else
		*enum_status |= STARTUP_STATUS_RESTORE_PARM;
	call_atten_cb(cd, PT_ATTEN_STARTUP, 0);
	cd->watchdog_interval = PT_WATCHDOG_TIMEOUT;
	cd->startup_retry_count = 0;

exit:
	/* Generate the HW Version of the connected DUT and store in cd */
	pt_generate_hw_version(cd, cd->hw_version);
	pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__,
		cd->hw_version);

	pt_start_wd_timer(cd);

	if (!detected)
		rc = -ENODEV;

#ifdef TTHE_TUNER_SUPPORT
	tthe_print(cd, NULL, 0, "exit startup");
#endif

	pt_debug(cd->dev, DL_WARN,
		"%s: Completed Enumeration rc=%d On Attempt %d\n",
		__func__, rc, try);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_enum_with_dut
 *
 * SUMMARY: This is the safe function wrapper for pt_enum_with_dut_() by
 *	requesting exclusive access around the call.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd    - pointer the core data structure
 *	 reset - Flag to reset the DUT before attempting to enumerate
 *	*status - pointer to store the enum status bitmask flags
 ******************************************************************************/
static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status)
{
	int rc = 0;

	mutex_lock(&cd->system_lock);
	cd->startup_state = STARTUP_RUNNING;
	mutex_unlock(&cd->system_lock);

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: fail get exclusive ex=%p own=%p\n",
			__func__, cd->exclusive_dev, cd->dev);
		goto exit;
	}

	rc = pt_enum_with_dut_(cd, reset, status);

	if (release_exclusive(cd, cd->dev) < 0)
		/* Don't return fail code, mode is already changed. */
		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
			__func__);
	else
		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
			__func__);
exit:
	mutex_lock(&cd->system_lock);
	/* Clear startup state for any tasks waiting for startup completion */
	cd->startup_state = STARTUP_NONE;
	mutex_unlock(&cd->system_lock);

	/* Set STATUS_COMPLETE bit to indicate the status is ready to be read */
	*status |= STARTUP_STATUS_COMPLETE;

	/* Wake the waiters for end of startup */
	wake_up(&cd->wait_q);

	return rc;
}

static int add_sysfs_interfaces(struct device *dev);
static void remove_sysfs_interfaces(struct device *dev);
static void remove_sysfs_and_modules(struct device *dev);
static void pt_release_modules(struct pt_core_data *cd);
static void pt_probe_modules(struct pt_core_data *cd);
/*******************************************************************************
 * FUNCTION: _pt_ttdl_restart
 *
 * SUMMARY: Restarts TTDL enumeration with the DUT and re-probes all modules
 *
 * NOTE: The function DOSE NOT remove sysfs and modules. Trying to create
 * existing sysfs nodes will produce an error.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int _pt_ttdl_restart(struct device *dev)
{
	int rc = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);
#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
	struct i2c_client *client =
		(struct i2c_client *)container_of(dev, struct i2c_client, dev);
#endif
	/*
	 * Make sure the device is awake, pt_mt_release function will
	 * cause pm sleep function and lead to deadlock.
	 */
	pm_runtime_get_sync(dev);
	/* Use ttdl_restart_lock to avoid reentry */
	mutex_lock(&cd->ttdl_restart_lock);

	remove_sysfs_and_modules(cd->dev);

#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		pt_debug(dev, DL_ERROR,
			"%s I2C functionality not Supported\n", __func__);
		rc = -EIO;
		goto ttdl_no_error;
	}
#endif

	if (cd->active_dut_generation == DUT_UNKNOWN) {
		rc = _pt_detect_dut_generation(cd->dev,
			&cd->startup_status, &cd->active_dut_generation,
			&cd->mode);
		if ((cd->active_dut_generation == DUT_UNKNOWN) || (rc)) {
			pt_debug(dev, DL_ERROR,
				"%s: Error, Unknown DUT Generation rc=%d\n",
				__func__, rc);
		}
	}

	rc = add_sysfs_interfaces(cd->dev);
	if (rc < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Error, failed sysfs nodes rc=%d\n",
			__func__, rc);

	if (!(cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL)) {
		pt_debug(dev, DL_INFO, "%s: Call pt_enum_with_dut\n", __func__);
		rc = pt_enum_with_dut(cd, true, &cd->startup_status);
		if (rc < 0)
			pt_debug(dev, DL_ERROR,
				"%s: Error, Failed to Enumerate\n", __func__);
	}

	rc = pt_mt_probe(dev);
	if (rc < 0) {
		pt_debug(dev, DL_ERROR,
			"%s: Error, fail mt probe\n", __func__);
	}

	rc = pt_btn_probe(dev);
	if (rc < 0) {
		pt_debug(dev, DL_ERROR,
			"%s: Error, fail btn probe\n", __func__);
	}

	pt_probe_modules(cd);

	pt_debug(cd->dev, DL_WARN,
		"%s: Well Done! TTDL Restart Completed\n", __func__);
	rc = 0;

#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
ttdl_no_error:
#endif
	mutex_unlock(&cd->ttdl_restart_lock);

	mutex_lock(&cd->system_lock);
	cd->startup_status |= STARTUP_STATUS_COMPLETE;
	cd->startup_state = STARTUP_NONE;
	mutex_unlock(&cd->system_lock);

	pm_runtime_put(dev);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_restart_work_function
 *
 * SUMMARY: This is the wrapper function placed in a work queue to call
 *	_pt_ttdl_restart()
 *
 * RETURN: none
 *
 * PARAMETERS:
 *	*work - pointer to the work_struct
 ******************************************************************************/
static void pt_restart_work_function(struct work_struct *work)
{
	struct pt_core_data *cd =  container_of(work,
		struct pt_core_data, ttdl_restart_work);
	int rc = 0;

	rc = _pt_ttdl_restart(cd->dev);
	if (rc < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
			__func__, rc);
}

/*******************************************************************************
 * FUNCTION: pt_enum_work_function
 *
 * SUMMARY: This is the wrapper function placed in a work queue to call
 *	pt_enum_with_dut()
 *
 * RETURN: none
 *
 * PARAMETERS:
 *	*work - pointer to the work_struct
 ******************************************************************************/
static void pt_enum_work_function(struct work_struct *work)
{
	struct pt_core_data *cd =  container_of(work,
		struct pt_core_data, enum_work);
	int rc;

	rc = pt_enum_with_dut(cd, false, &cd->startup_status);
	if (rc < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
			__func__, rc);
}

static int pt_get_regulator(struct pt_core_data *cd, bool get)
{
	int rc;

	if (!get) {
		rc = 0;
		goto regulator_put;
	}

	cd->vdd = regulator_get(cd->dev, "vdd");
	if (IS_ERR(cd->vdd)) {
		rc = PTR_ERR(cd->vdd);
		dev_err(cd->dev,
			"Regulator get failed vdd rc=%d\n", rc);
		goto regulator_put;
	}

	cd->vcc_i2c = regulator_get(cd->dev, "vcc_i2c");
	if (IS_ERR(cd->vcc_i2c)) {
		rc = PTR_ERR(cd->vcc_i2c);
		dev_err(cd->dev,
			"Regulator get failed vcc_i2c rc=%d\n", rc);
		goto regulator_put;
	}

	return 0;

regulator_put:
	if (cd->vdd) {
		regulator_put(cd->vdd);
		cd->vdd = NULL;
	}

	if (cd->vcc_i2c) {
		regulator_put(cd->vcc_i2c);
		cd->vcc_i2c = NULL;
	}

	return rc;
}

#ifdef ENABLE_I2C_REG_ONLY
static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en)
{
	int rc = 0;

	pt_debug(cd->dev, DL_INFO, "%s: Enter flag = %d\n", __func__, en);
	if (!en) {
		rc = 0;
		goto disable_vcc_i2c_reg_only;
	}

	if (cd->vcc_i2c) {
		rc = regulator_set_load(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA);
		if (rc < 0)
			pt_debug(cd->dev, DL_INFO,
				"%s: I2c unable to set active current rc = %d\n", __func__, rc);

		pt_debug(cd->dev, DL_INFO, "%s: i2c set I2C_ACTIVE_LOAD_MA rc = %d\n",
			__func__, rc);
	}

	return 0;

disable_vcc_i2c_reg_only:
	if (cd->vcc_i2c) {
		rc = regulator_set_load(cd->vcc_i2c, I2C_SUSPEND_LOAD_UA);
		if (rc < 0)
			pt_debug(cd->dev, DL_INFO, "%s: i2c unable to set 0 uAm rc = %d\n",
				__func__, rc);

	}
	pt_debug(cd->dev, DL_INFO, "%s: Exit rc = %d I2C_SUSPEND_LOAD_UA\n", __func__, rc);

	return rc;
}

#endif

static int pt_enable_regulator(struct pt_core_data *cd, bool en)
{
	int rc;

	if (!en) {
		rc = 0;
		goto disable_vcc_i2c_reg;
	}

	if (cd->vdd) {
		if (regulator_count_voltages(cd->vdd) > 0) {
			rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV,
						FT_VTG_MAX_UV);
			if (rc) {
				dev_err(cd->dev,
					"Regulator set_vtg failed vdd rc=%d\n", rc);
				goto exit;
			}
		}

		rc = regulator_enable(cd->vdd);
		if (rc) {
			dev_err(cd->dev,
				"Regulator vdd enable failed rc=%d\n", rc);
			goto exit;
		}
		dev_info(cd->dev, "%s: VDD regulator enabled:\n", __func__);
	}

	if (cd->vcc_i2c) {
		if (regulator_count_voltages(cd->vcc_i2c) > 0) {
			rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV,
							FT_I2C_VTG_MAX_UV);
			if (rc) {
				dev_err(cd->dev,
					"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
				goto disable_vdd_reg;
			}
		}

		rc = regulator_enable(cd->vcc_i2c);
		if (rc) {
			dev_err(cd->dev,
				"Regulator vcc_i2c enable failed rc=%d\n", rc);
			goto disable_vdd_reg;
		}
		dev_info(cd->dev, "%s: VCC I2C regulator enabled:\n", __func__);
	}

	return 0;

disable_vcc_i2c_reg:
	if (cd->vcc_i2c) {
		if (regulator_count_voltages(cd->vcc_i2c) > 0)
			regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV,
						FT_I2C_VTG_MAX_UV);

		regulator_disable(cd->vcc_i2c);
		dev_info(cd->dev, "%s: VCC I2C regulator disabled:\n", __func__);

	}

disable_vdd_reg:
	if (cd->vdd) {
		if (regulator_count_voltages(cd->vdd) > 0)
			regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV,
						FT_VTG_MAX_UV);

		regulator_disable(cd->vdd);
		dev_info(cd->dev, "%s: VDD regulator disabled:\n", __func__);
	}

exit:
	return rc;
}


#if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE)
#define KERNEL_VER_GT_3_19
#endif

#if defined(CONFIG_PM_RUNTIME) || defined(KERNEL_VER_GT_3_19)
/* CONFIG_PM_RUNTIME option is removed in 3.19.0 */
#if defined(CONFIG_PM_SLEEP)
/*******************************************************************************
 * FUNCTION: pt_core_rt_suspend
 *
 * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_sleep.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_rt_suspend(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n",
		__func__, cd->core_probe_complete);

	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_core_rt_resume
 *
 * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_wake.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_rt_resume(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n",
		__func__, cd->core_probe_complete);

	return 0;
}
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM_RUNTIME || LINUX_VERSION_CODE */

#if defined(CONFIG_PM_SLEEP)
/*******************************************************************************
 * FUNCTION: pt_core_suspend_
 *
 * SUMMARY: Wrapper function with device suspend/resume stratergy to call
 *  pt_core_sleep. This function may disable IRQ during sleep state.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_suspend_(struct device *dev)
{
	int rc;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_debug(dev, DL_INFO, "%s: Enter\n", __func__);
	rc = pt_core_sleep(cd);
	if (rc) {
		pt_debug(dev, DL_ERROR, "%s: Error on sleep rc =%d\n",
			__func__, rc);
		return -EAGAIN;
	}

	rc = pt_enable_regulator(cd, false);
	if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
		return 0;

	/* Required to prevent interrupts before bus awake */
	disable_irq(cd->irq);
	cd->irq_disabled = 1;

	if (device_may_wakeup(dev)) {
		pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n",
			__func__);
		if (!enable_irq_wake(cd->irq))
			cd->irq_wake = 1;
	} else {
		pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n",
			__func__);
	}

	pt_debug(dev, DL_INFO, "%s: Exit :\n", __func__);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_suspend
 *
 * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being
 *  woke up or put to sleep based on Kernel power state even when the display
 *  is off based on the check of TTDL core platform flag.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_suspend(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int rc = 0;

	if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP))
		return 0;

	if (pt_core_state == STATE_SUSPEND)
	{
		pt_debug(cd->dev, DL_INFO, "%s Already in Suspend state\n", __func__);
		return 0;
	}

	pt_debug(cd->dev, DL_INFO, "%s Suspend start\n", __func__);
	cancel_work_sync(&cd->resume_work);
	cancel_work_sync(&cd->suspend_work);

	mutex_lock(&cd->system_lock);
	cd->wait_until_wake = 0;
	mutex_unlock(&cd->system_lock);

	if (pm_suspend_via_firmware() || cd->touch_offload) {
		rc = pt_core_suspend_(cd->dev);
		cd->quick_boot = true;
	} else {
		rc = pt_enable_i2c_regulator(cd, false);
		if (rc < 0)
			pt_debug(cd->dev, DL_ERROR,
				"%s: Error on disabling i2c regulator\n", __func__);
	}
	pt_debug(cd->dev, DL_INFO, "%s Suspend exit - rc = %d\n", __func__, rc);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_core_resume_
 *
 * SUMMARY: Wrapper function with device suspend/resume stratergy to call
 *  pt_core_wake. This function may enable IRQ before wake up.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_resume_(struct device *dev)
{
	int rc = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	dev_info(dev, "%s: Entering into resume mode:\n",
		__func__);
	rc = pt_enable_regulator(cd, true);
	dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n",
		__func__, rc);

	if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
		goto exit;

	/*
	 * Bus pm does not call suspend if device runtime suspended
	 * This flag is covers that case
	 */
	if (cd->irq_disabled) {
		enable_irq(cd->irq);
		cd->irq_disabled = 0;
	}

	if (device_may_wakeup(dev)) {
		pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n",
			__func__);
		if (cd->irq_wake) {
			disable_irq_wake(cd->irq);
			cd->irq_wake = 0;
		}
	} else {
		pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n",
			__func__);
	}

exit:
	rc = pt_core_wake(cd);
	if (rc) {
		dev_err(dev, "%s: Failed to wake up: rc=%d\n",
			__func__, rc);
		return -EAGAIN;
	}

	return rc;
}
/*******************************************************************************
 * FUNCTION: pt_core_restore
 *
 * SUMMARY: Wrapper function with device suspend/resume stratergy to call
 *  pt_core_wake. This function may enable IRQ before wake up.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_restore(struct device *dev)
{
	int rc = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	dev_info(dev, "%s: Entering into resume mode:\n",
		__func__);

	queue_work(cd->pt_workqueue, &cd->resume_offload_work);
	return rc;
}

/*******************************************************************************
 * FUNCTION: suspend_offload_work
 *
 * SUMMARY: Wrapper function of pt_core_suspend() to avoid TP to be waken/slept
 *  along with kernel power state even the display is off based on the check of
 *  TTDL core platform flag.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static void pt_suspend_offload_work(struct work_struct *work)

{
	int rc = 0;
	struct pt_core_data *cd = container_of(work, struct pt_core_data,
				suspend_offload_work);

	pt_debug(cd->dev, DL_INFO, "%s start\n", __func__);
	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
		return;

	rc = pt_core_suspend_(cd->dev);
	pt_debug(cd->dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc);
}

/*******************************************************************************
 * FUNCTION: resume_offload_work
 *
 * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept
 *  along with kernel power state even the display is off based on the check of
 *  TTDL core platform flag.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static void pt_resume_offload_work(struct work_struct *work)

{
	int rc = 0;
	int retry_count = 1000;
	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
					resume_offload_work);

	pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__);
	do {
		retry_count--;
	rc = pt_core_resume_(pt_data->dev);
	if (rc < 0)
		pt_debug(pt_data->dev, DL_ERROR,
			"%s: Error on wake\n", __func__);
	} while (retry_count && rc < 0);

	if (rc < 0){
		pt_debug(pt_data->dev, DL_ERROR, "%s: Error on wake\n", __func__);
		return;
	}

#ifdef TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND
	rc = pt_core_easywake_on(pt_data);
	if (rc < 0) {
		pt_debug(pt_data->dev, DL_ERROR,
			"%s: Error on enable touch to wake key\n",
			__func__);
		return;
	}
	pt_data->fb_state = FB_OFF;
	pt_debug(pt_data->dev, DL_INFO, "%s End\n", __func__);
#endif
	pt_data->quick_boot = false;
	pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__);
}


/*******************************************************************************
 * FUNCTION: pt_core_resume
 *
 * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept
 *  along with kernel power state even the display is off based on the check of
 *  TTDL core platform flag.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
static int pt_core_resume(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int rc = 0;

	if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP))
		return 0;


	if (pm_suspend_via_firmware() || cd->touch_offload) {
			rc = pt_core_restore(cd->dev);
	} else {
		pt_debug(cd->dev, DL_INFO, "%s start\n", __func__);
		rc = pt_enable_i2c_regulator(cd, true);
		pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, rc);
	}

	mutex_lock(&cd->system_lock);
	cd->wait_until_wake = 1;
	mutex_unlock(&cd->system_lock);
	wake_up(&cd->wait_q);

	pt_debug(cd->dev, DL_INFO, "%s End rc = %d\n", __func__, rc);
	return rc;
}
#endif

#ifdef NEED_SUSPEND_NOTIFIER
/*******************************************************************************
 * FUNCTION: pt_pm_notifier
 *
 * SUMMARY: This function is registered to notifier chain and will perform
 *  suspend operation if match event PM_SUSPEND_PREPARE.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *  *nb     - pointer to notifier_block structure
 *   action - notifier event type
 *  *data   - void pointer
 ******************************************************************************/
static int pt_pm_notifier(struct notifier_block *nb,
		unsigned long action, void *data)
{
	struct pt_core_data *cd = container_of(nb,
			struct pt_core_data, pm_notifier);
	if (action == PM_SUSPEND_PREPARE) {
		pt_debug(cd->dev, DL_INFO, "%s: Suspend prepare\n",
			__func__);

		/*
		 * If PM runtime is not suspended, either call runtime
		 * PM suspend callback or wait until it finishes
		 */
		if (!pm_runtime_suspended(cd->dev))
			pm_runtime_suspend(cd->dev);

		(void) pt_core_suspend(cd->dev);
	}

	return NOTIFY_DONE;
}
#endif

const struct dev_pm_ops pt_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(pt_core_suspend, pt_core_resume)
	.freeze  = pt_core_suspend,
	.restore = pt_core_restore,
	SET_RUNTIME_PM_OPS(pt_core_rt_suspend, pt_core_rt_resume,
			NULL)
};
EXPORT_SYMBOL_GPL(pt_pm_ops);

/*******************************************************************************
 * FUNCTION: _pt_request_pip2_enter_bl
 *
 * SUMMARY: Force the DUT to enter the BL by resetting the DUT by use of the
 *	XRES pin or a soft reset.
 *
 *	NOTE: The WD MUST be stopped/restarted by the calling Function. Having
 *        the WD active could cause this function to fail!
 *	NOTE: If start_mode is passed in as PT_MODE_IGNORE, this function
 *	      will not try to determine the current mode but will proceed with
 *	      resetting the DUT and entering the BL.
 *
 *	NOTE: The definition of result code:
 *	      PT_ENTER_BL_PASS                (0)
 *	      PT_ENTER_BL_ERROR               (1)
 *	      PT_ENTER_BL_RESET_FAIL          (2)
 *	      PT_ENTER_BL_HID_START_BL_FAIL   (3)
 *	      PT_ENTER_BL_CONFIRM_FAIL        (4)
 *	      PT_ENTER_BL_GET_FLASH_INFO_FAIL (5)
 *
 * RETURNS:
 *	 0 = success
 *	!0 = failure
 *
 *
 * PARAMETERS:
 *	*dev        - pointer to device structure
 *	*start_mode - pointer to the mode the DUT was in when this function
 *	              starts
 *	*result     - pointer to store the result when to enter BL
 ******************************************************************************/
int _pt_request_pip2_enter_bl(struct device *dev, u8 *start_mode, int *result)
{
	int rc = 0;
	int t;
	int tmp_result = PT_ENTER_BL_ERROR;
	int flash_info_retry = 2;
	u8 mode = PT_MODE_UNKNOWN;
	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
	u8 read_buf[32];
	u16 actual_read_len;
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 host_mode_cmd[4] = {0xA5, 0xA5, 0xA5, 0xA5};
	u8 time = 0;
	u8 saved_flashless_auto_bl_mode = cd->flashless_auto_bl;

	if (cd->watchdog_enabled) {
		pt_debug(dev, DL_WARN,
			"%s: Watchdog must be stopped before entering BL\n",
			__func__);
		goto exit;
	}

	cancel_work_sync(&cd->enum_work);
	cancel_work_sync(&cd->watchdog_work);

	/* if undefined assume operational/test to bypass all checks */
	if (*start_mode == PT_MODE_IGNORE) {
		mode = PT_MODE_OPERATIONAL;
		sys_mode = FW_SYS_MODE_TEST;
		pt_debug(dev, DL_INFO, "%s: Assume Mode = %d", __func__, mode);
	} else if (*start_mode == PT_MODE_UNKNOWN) {
		rc = pt_pip2_get_mode_sysmode_(cd, &mode, &sys_mode);
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s: Get mode failed, mode unknown\n",
				__func__);
		}
		*start_mode = mode;
		pt_debug(dev, DL_INFO, "%s: Get Mode = %d", __func__, mode);
	} else if (*start_mode == PT_MODE_OPERATIONAL) {
		/* Assume SCANNIING mode to avoid doing an extra get_mode */
		sys_mode = FW_SYS_MODE_SCANNING;
	}

_retry:
	/* For Flashless DUTs - Suppress auto BL on next BL sentinel */
	pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - SUPPRESS\n", __func__);
	cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;

	switch (mode) {
	case PT_MODE_UNKNOWN:
		/*
		 * When the mode could not be determined the DUT could be
		 * in App mode running corrupted FW or FW that is not
		 * responding to the mode request, assume no communication
		 * and do a hard reset
		 */
		mutex_lock(&cd->system_lock);
		cd->startup_status = STARTUP_STATUS_START;
		pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
		mutex_unlock(&cd->system_lock);

		rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
		if (rc) {
			tmp_result = PT_ENTER_BL_RESET_FAIL;
			goto exit;
		}
		break;

	case PT_MODE_OPERATIONAL:
		if (sys_mode == FW_SYS_MODE_SCANNING) {
			pt_debug(dev, DL_INFO, "%s: Suspend Scanning\n",
				__func__);
			rc = pt_pip_suspend_scanning_(cd);
			if (rc) {
				/*
				 * Print to log but don't exit, the FW could be
				 * running but be hung or fail to respond to
				 * this request
				 */
				pt_debug(dev, DL_ERROR,
					"%s Suspend Scan Failed\n", __func__);
			}
			/* sleep to allow the suspend scan to be processed */
			usleep_range(1000, 2000);
		}

		mutex_lock(&cd->system_lock);
		cd->startup_status = STARTUP_STATUS_START;
		pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
		mutex_unlock(&cd->system_lock);

		/* Reset device to enter the BL */
		rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
		if (rc) {
			tmp_result = PT_ENTER_BL_RESET_FAIL;
			goto exit;
		}
		break;

	case PT_MODE_BOOTLOADER:
		/* Do nothing as we are already in the BL */
		tmp_result = PT_ENTER_BL_PASS;
		goto exit;

	default:
		/* Should NEVER get here */
		tmp_result = PT_ENTER_BL_ERROR;
		pt_debug(dev, DL_ERROR, "%s: Unknown mode code\n", __func__);
		goto exit;
	}

	if (!cd->flashless_dut &&
	    (mode == PT_MODE_UNKNOWN || mode == PT_MODE_OPERATIONAL)) {
		/*
		 * Sending the special "Host Mode" command will instruct the
		 * BL to not execute the FW it has loaded into RAM.
		 * The command must be sent within a 40ms window from releasing
		 * the XRES pin. If the messages is sent too early it will NAK,
		 * so keep sending it every 2ms until it is accepted by the BL.
		 * A no-flash DUT does not require this command as there is no
		 * FW for the BL to load and execute.
		 */
		usleep_range(4000, 6000);
		pt_debug(cd->dev, DL_INFO,
			">>> %s: Write Buffer Size[%d] Stay in BL\n",
			__func__, (int)sizeof(host_mode_cmd));

		pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd,
			(int)sizeof(host_mode_cmd), ">>> User CMD");
		rc = 1;
		while (rc && time < 34) {
			rc = pt_adap_write_read_specific(cd, 4,
				host_mode_cmd, NULL);
			usleep_range(1800, 2000);
			time += 2;
		}

		/* Sleep to allow the BL to come up */
		usleep_range(1000, 2000);
	}

	/*
	 * To avoid the case that next PIP command can be confused by BL/FW
	 * sentinel's "wakeup" event, chekcing hid_reset_cmd_state  which is
	 * followed by "wakeup event" function can lower the failure rate.
	 */
	t = wait_event_timeout(cd->wait_q,
		((cd->startup_status != STARTUP_STATUS_START)
		 && (cd->hid_reset_cmd_state == 0)),
		msecs_to_jiffies(300));
	if (IS_TMO(t)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: TMO waiting for BL sentinel\n", __func__);
	}

	/* Check if device is now in BL mode */
	rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
	pt_debug(dev, DL_INFO, "%s: Mode=%d, Status=0x%04X\n", __func__, mode,
		cd->startup_status);
	if (!rc && mode == PT_MODE_BOOTLOADER) {
		pt_debug(dev, DL_INFO, "%s In bootloader mode now\n", __func__);
		mutex_lock(&cd->system_lock);
		cd->pip2_prot_active = true;
		cd->mode = PT_MODE_BOOTLOADER;
		mutex_unlock(&cd->system_lock);
		tmp_result = PT_ENTER_BL_PASS;
	} else {
		/*
		 * If the device doesn't enter BL mode as expected and rc is
		 * tested pass by above function pt_pip2_get_mode_sysmode_(),
		 * the function should return an error code to indicate this
		 * failure PT_ENTER_BL_CONFIRM_FAIL.
		 */
		if (!rc)
			rc = -EINVAL;
		tmp_result = PT_ENTER_BL_CONFIRM_FAIL;
		mutex_lock(&cd->system_lock);
		cd->mode = mode;
		mutex_unlock(&cd->system_lock);
		pt_debug(dev, DL_ERROR,
			"%s ERROR: Not in BL as expected", __func__);
	}

exit:
	if (!cd->flashless_dut && (tmp_result == PT_ENTER_BL_PASS)) {
		/* Check to get (buffered) flash information */
		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
					       PIP2_CMD_ID_FLASH_INFO, NULL, 0,
					       read_buf, &actual_read_len);
		if (!rc) {
			if (read_buf[PIP2_RESP_BODY_OFFSET] == 0) {
				pt_debug(
				    dev, DL_WARN,
				    "%s Unavailable Manufacturer ID: 0x%02x\n",
				    __func__,
				    read_buf[PIP2_RESP_BODY_OFFSET]);
				/*
				 * If the BL was unable to cache the correct
				 * values when entering the first time due to
				 * the Flash part not having been powered up
				 * long enough, re-enter the BL to trigger the
				 * BL to re-attempt to cache the values.
				 */
				if (flash_info_retry-- > 0) {
					mode = PT_MODE_UNKNOWN;
					pt_debug(dev, DL_WARN,
						 "%s Assume mode to UNKNOWN to enter BL again, retry=%d\n",
						 __func__, flash_info_retry);
					goto _retry;
				} else {
					pt_debug(dev, DL_WARN,
						"%s Manufacturer ID Unknown\n",
						__func__);
					tmp_result = PT_ENTER_BL_PASS;
				}
			}
		} else {
			tmp_result = PT_ENTER_BL_GET_FLASH_INFO_FAIL;
			pt_debug(
			    dev, DL_ERROR,
			    "%s: Failed to send PIP2 READ_FLASH_INFO cmd\n",
			    __func__);
		}
	}

	pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - %s\n", __func__,
		saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" :
		"SUPPRESS");
	cd->flashless_auto_bl = saved_flashless_auto_bl_mode;
	if (result)
		*result = tmp_result;
	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_open
 *
 * SUMMARY: Using the BL PIP2 commands open a file and return the file handle
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *
 * RETURNS:
 *	<0 = Error
 *	>0 = file handle opened
 *
 * PARAMETERS:
 *	*dev     - pointer to device structure
 *	 file_no - PIP2 file number to open
 ******************************************************************************/
int _pt_pip2_file_open(struct device *dev, u8 file_no)
{
	int ret = 0;
	u16 status;
	u16 actual_read_len;
	u8  file_handle;
	u8  data[2];
	u8  read_buf[10];

	pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no);
	data[0] = file_no;
	ret = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN,
		data, 1, read_buf, &actual_read_len);
	if (ret) {
		pt_debug(dev, DL_ERROR,
			"%s ROM BL FILE_OPEN timeout for file = %d\n",
			__func__, file_no);
		return -PIP2_RSP_ERR_NOT_OPEN;
	}

	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	file_handle = read_buf[PIP2_RESP_BODY_OFFSET];
	if (ret || ((status != 0x00) && (status != 0x03)) ||
	    (file_handle != file_no)) {
		pt_debug(dev, DL_ERROR,
			"%s ROM BL FILE_OPEN failure: 0x%02X for file = %d\n",
			__func__, status, file_handle);
		return -status;
	}
	return file_handle;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_close
 *
 * SUMMARY: Using the BL PIP2 commands close a file
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *
 * RETURNS:
 *	<0 = Error
 *	>0 = file handle closed
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 file_handle - handle to the file to be closed
 ******************************************************************************/
int _pt_pip2_file_close(struct device *dev, u8 file_handle)
{
	int ret = 0;
	u16 status;
	u16 actual_read_len;
	u8  data[2];
	u8 read_buf[32];

	pt_debug(dev, DL_DEBUG, "%s: CLOSE file %d\n", __func__, file_handle);
	data[0] = file_handle;
	ret = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_CLOSE,
		data, 1, read_buf, &actual_read_len);
	if (ret) {
		pt_debug(dev, DL_ERROR,
			"%s ROM BL FILE_CLOSE timeout for file = %d\n",
			__func__, file_handle);
		return -ETIME;
	}

	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	if (status != 0x00) {
		pt_debug(dev, DL_ERROR,
			"%s ROM BL FILE_CLOSE failure: 0x%02X for file = %d\n",
			__func__, status, file_handle);
		return -status;
	}
	return file_handle;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_erase
 *
 * SUMMARY: Using the BL PIP2 commands erase a file
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *	NOTE: Some FLASH parts can time out while erasing one or more sectors,
 *		one retry is attempted for each sector in a file.
 *
 * RETURNS:
 *	<0 = Error
 *	>0 = file handle closed
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 file_handle - handle to the file to be erased
 *	 *status     - PIP2 erase status code
 ******************************************************************************/
static int _pt_pip2_file_erase(struct device *dev, u8 file_handle, int *status)
{
	int ret = 0;
	int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE;
	int retry = 1;
	u16 actual_read_len;
	u8  data[2];
	u8  read_buf[10];
	struct pt_core_data *cd = dev_get_drvdata(dev);

	pt_debug(dev, DL_DEBUG, "%s: ERASE file %d\n", __func__, file_handle);
	data[0] = file_handle;
	data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE;
	*status = PIP2_RSP_ERR_TIMEOUT;

	/* Increase waiting time for large file erase */
	mutex_lock(&cd->system_lock);
	cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_ERASE_TIMEOUT;
	mutex_unlock(&cd->system_lock);
	while (*status == PIP2_RSP_ERR_TIMEOUT && retry < max_retry) {
		ret = _pt_request_pip2_send_cmd(dev,
			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
			data, 2, read_buf, &actual_read_len);
		if (ret)
			break;
		*status = read_buf[PIP2_RESP_STATUS_OFFSET];
		if (*status == PIP2_RSP_ERR_TIMEOUT) {
#ifdef TTDL_DIAGNOSTICS
			cd->file_erase_timeout_count++;
#endif
			pt_debug(dev, DL_WARN,
				"%s: ERASE timeout %d for file = %d\n",
				__func__, retry, file_handle);
		}
		retry++;
	}
	mutex_lock(&cd->system_lock);
	cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;
	mutex_unlock(&cd->system_lock);
	if (ret) {
		pt_debug(dev, DL_ERROR,
			"%s ROM FILE_ERASE cmd failure: %d for file = %d\n",
			__func__, ret, file_handle);
		return -EIO;
	}

	if (*status != 0x00) {
		pt_debug(dev, DL_ERROR,
			"%s ROM FILE_ERASE failure: 0x%02X for file = %d\n",
			__func__, *status, file_handle);
		return -EIO;
	}
	return file_handle;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_read
 *
 * SUMMARY: Using the BL PIP2 commands read n bytes from a already opened file
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *
 * RETURNS:
 *	<0 = Error
 *	>0 = number of bytes read
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 file_handle - File handle to read from
 *	 num_bytes   - number of bytes to read
 ******************************************************************************/
int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes,
	u8 *read_buf)
{
	int ret = 0;
	u16 status;
	u16 actual_read_len;
	u8  data[3];

	data[0] = file_handle;
	data[1] = (num_bytes & 0x00FF);
	data[2] = (num_bytes & 0xFF00) >> 8;

	ret = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ,
		data, 3, read_buf, &actual_read_len);
	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	if (ret || ((status != 0x00) && (status != 0x03))) {
		pt_debug(dev, DL_ERROR,
			"%s File open failure with error code = %d\n",
			__func__, status);
		return -EPERM;
	}
	ret = num_bytes;

	return ret;
}

/*******************************************************************************
 * FUNCTION: _pt_read_us_file
 *
 * SUMMARY: Open a user space file and read 'size' bytes into buf. If size = 0
 *	then read the entire file.
 *      NOTE: The file size must be less than PT_PIP2_MAX_FILE_SIZE
 *
 * RETURN:
 *       0 = success
 *      !0 = failure
 *
 * PARAMETERS:
 *      *file_path - pointer to the file path
 *      *buf       - pointer to the buffer to store the file contents
 *      *size      - pointer to the size of the file
 ******************************************************************************/
int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size)
{
	struct file *filp = NULL;
	struct inode *inode = NULL;
	unsigned int file_len = 0;
	unsigned int read_len = 0;
	mm_segment_t oldfs;
	int rc = 0;

	if (file_path == NULL || buf == NULL) {
		pt_debug(dev, DL_ERROR, "%s: path || buf is NULL.\n", __func__);
		return -EINVAL;
	}
	pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path);

	oldfs = force_uaccess_begin();
	filp = filp_open_block(file_path, O_RDONLY, 0400);

	if (IS_ERR(filp)) {
		pt_debug(dev, DL_ERROR, "%s: Failed to open %s\n", __func__,
			file_path);
		rc = -ENOENT;
		goto err;
	}

	if (filp->f_op == NULL) {
		pt_debug(dev, DL_ERROR, "%s: File Operation Method Error\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}
	inode = filp->f_path.dentry->d_inode;
	if (inode == NULL) {
		pt_debug(dev, DL_ERROR, "%s: Get inode from filp failed\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}
	file_len = i_size_read(inode->i_mapping->host);
	if (file_len == 0) {
		pt_debug(dev, DL_ERROR, "%s: file size error,file_len = %d\n",
			__func__, file_len);
		rc = -EINVAL;
		goto exit;
	}

	if (*size == 0)
		read_len = file_len;
	else
		read_len = *size;

	if (read_len > PT_PIP2_MAX_FILE_SIZE) {
		pt_debug(dev, DL_ERROR, "%s: file size ( %d ) exception.\n",
			__func__, read_len);
		rc = -EINVAL;
		goto exit;
	}
	filp->private_data = inode->i_private;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
	if (filp->f_op->read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
#else
	if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
#endif
		pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__);
		rc = -EINVAL;
		goto exit;
	}
	*size = read_len;

exit:
	if (filp_close(filp, NULL) != 0)
		pt_debug(dev, DL_ERROR, "%s: file close error.\n", __func__);
err:
	force_uaccess_end(oldfs);
	return rc;

}

/*******************************************************************************
 * FUNCTION: _pt_request_pip2_bin_hdr
 *
 * SUMMARY: Read the stored bin file header from Flash or the User Space file
 *	in the case of a flashless DUT, and parse the contents
 *
 * RETURNS:
 *	 0 = Success
 *	!0 = Error condition
 *
 * PARAMETERS:
 *	*dev - pointer to device structure
 ******************************************************************************/
int _pt_request_pip2_bin_hdr(struct device *dev, struct pt_bin_file_hdr *hdr)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8  file_handle;
	u8  read_buf[255];
	u8  hdr_len = 0;
	u8  i;
	int bytes_read;
	int read_size;
	int ret = 0;
	int rc = 0;
	bool load_hdr_struct = false;

	if (cd->flashless_dut) {
		read_size = sizeof(read_buf);
		rc = _pt_read_us_file(dev, cd->pip2_us_file_path,
			read_buf, &read_size);
		if (rc) {
			ret = rc;
			pt_debug(dev, DL_ERROR,
				"%s Failed to read fw image from US, rc=%d\n",
				__func__, rc);
			goto exit;
		}
		load_hdr_struct = true;
		hdr_len = read_buf[0];
		i = 0;
	} else {
		if (cd->mode != PT_MODE_BOOTLOADER) {
			ret = -EPERM;
			goto exit;
		}

		/* Open the bin file in Flash */
		pt_debug(dev, DL_INFO, "%s Open File 1\n", __func__);
		file_handle = _pt_pip2_file_open(dev, PIP2_FW_FILE);
		if (file_handle != PIP2_FW_FILE) {
			ret = -ENOENT;
			pt_debug(dev, DL_ERROR,
				"%s Failed to open bin file\n", __func__);
			goto exit;
		}

		/* Read the header length from the file */
		pt_debug(dev, DL_INFO, "%s Read length of header\n", __func__);
		read_size = 1;
		bytes_read = _pt_pip2_file_read(dev, file_handle, read_size,
				read_buf);
		if (bytes_read != read_size) {
			ret = -EX_ERR_FREAD;
			pt_debug(dev, DL_ERROR,
				"%s Failed to bin file header len\n", __func__);
			goto exit_close_file;
		}
		hdr_len = read_buf[PIP2_RESP_BODY_OFFSET];
		if (hdr_len == 0xFF) {
			ret = -EX_ERR_FLEN;
			pt_debug(dev, DL_ERROR,
				"%s Bin header len is invalid\n", __func__);
			goto exit_close_file;
		}

		/* Read the rest of the header from the bin file */
		pt_debug(dev, DL_INFO, "%s Read bin file header\n", __func__);
		memset(read_buf, 0, sizeof(read_buf));
		bytes_read = _pt_pip2_file_read(dev, file_handle, hdr_len,
				read_buf);
		if (bytes_read != hdr_len) {
			ret = -EX_ERR_FREAD;
			pt_debug(dev, DL_ERROR,
				"%s Failed to read bin file\n", __func__);
			goto exit_close_file;
		}
		load_hdr_struct = true;

exit_close_file:
		/* Close the file */
		if (file_handle != _pt_pip2_file_close(dev, file_handle)) {
			ret = -EX_ERR_FCLOSE;
			pt_debug(dev, DL_ERROR,
				"%s Failed to close bin file\n", __func__);
		}

		/*
		 * The length was already read so subtract 1 to make the rest of
		 * the offsets match the spec
		 */
		i = PIP2_RESP_BODY_OFFSET - 1;
	}

	if (load_hdr_struct) {
		hdr->length      = hdr_len;
		hdr->ttpid       = (read_buf[i+1] << 8) | read_buf[i+2];
		hdr->fw_major    = (read_buf[i+3]);
		hdr->fw_minor    = (read_buf[i+4]);
		hdr->fw_crc      = (read_buf[i+5] << 24) |
				   (read_buf[i+6] << 16) |
				   (read_buf[i+7] << 8)  |
				   (read_buf[i+8]);
		hdr->fw_rev_ctrl = (read_buf[i+9]  << 24) |
				   (read_buf[i+10] << 16) |
				   (read_buf[i+11] << 8)  |
				   (read_buf[i+12]);
		hdr->si_rev      = (read_buf[i+14] << 8)  | (read_buf[i+13]);
		hdr->si_id       = (read_buf[i+16] << 8)  | (read_buf[i+15]);
		hdr->config_ver  = (read_buf[i+17] << 8)  | (read_buf[i+18]);
		if (hdr_len >= 22) {
			hdr->hex_file_size = (read_buf[i+19] << 24) |
					     (read_buf[i+20] << 16) |
					     (read_buf[i+21] << 8) |
					     (read_buf[i+22]);
		} else {
			hdr->hex_file_size = 0;
		}
	}

exit:
	return ret;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_get_stats
 *
 * SUMMARY: Using the BL PIP2 commands get file information from an already
 *  opened file
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *
 * RETURNS:
 *	!0 = Error
 *	 0 = Success
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	 file_handle - File handle to read from
 *	*address     - pointer to store address of file
 *	*file_size   _ pointer to store size of file
 ******************************************************************************/
int _pt_pip2_file_get_stats(struct device *dev, u8 file_handle, u32 *address,
	u32 *file_size)
{
	int ret = 1;
	u16 status;
	u16 actual_read_len;
	u8  data[2];
	u8  read_buf[16];

	data[0] = file_handle;
	data[1] = PIP2_FILE_IOCTL_CODE_FILE_STATS;

	ret = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
		data, 2, read_buf, &actual_read_len);

	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	if (ret || (status != 0x00)) {
		pt_debug(dev, DL_ERROR,
			"%s ROM FILE_STATS failure: 0x%02X for file = %d, ret = %d\n",
			__func__, status, file_handle, ret);
		ret = -EIO;
		goto exit;
	}

	pt_debug(dev, DL_DEBUG,
		"%s --- FILE %d Information ---\n", __func__, file_handle);

	if (address) {
		*address = read_buf[PIP2_RESP_BODY_OFFSET] +
			(read_buf[PIP2_RESP_BODY_OFFSET + 1] << 8) +
			(read_buf[PIP2_RESP_BODY_OFFSET + 2] << 16) +
			(read_buf[PIP2_RESP_BODY_OFFSET + 3] << 24);
		pt_debug(dev, DL_DEBUG, "%s Address: 0x%08x\n",
			__func__, *address);
	}

	if (file_size) {
		*file_size = read_buf[PIP2_RESP_BODY_OFFSET + 4] +
			(read_buf[PIP2_RESP_BODY_OFFSET + 5] << 8) +
			(read_buf[PIP2_RESP_BODY_OFFSET + 6] << 16) +
			(read_buf[PIP2_RESP_BODY_OFFSET + 7] << 24);
		pt_debug(dev, DL_DEBUG, "%s Size   : 0x%08x\n",
			__func__, *file_size);
	}

exit:
	return ret;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_seek_offset
 *
 * SUMMARY: Using the BL PIP2 commands seek read/write offset for an already
 *  opened file
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *	NOTE: File open/erase command can reset the offset
 *
 * RETURNS:
 *	!0 = Error
 *	 0 = Success
 *
 * PARAMETERS:
 *	*dev          - pointer to device structure
 *	 file_handle  - File handle to read from
 *	 read_offset  - read offset of file
 *	 write_offset - write offset of file
 ******************************************************************************/
int _pt_pip2_file_seek_offset(struct device *dev, u8 file_handle,
	u32 read_offset, u32 write_offset)
{
	int ret = 1;
	u16 status;
	u16 actual_read_len;
	u8  data[10];
	u8  read_buf[16];

	data[0] = file_handle;
	data[1] = PIP2_FILE_IOCTL_CODE_SEEK_POINTER;
	data[2] = 0xff & read_offset;
	data[3] = 0xff & (read_offset >> 8);
	data[4] = 0xff & (read_offset >> 16);
	data[5] = 0xff & (read_offset >> 24);
	data[6] = 0xff & write_offset;
	data[7] = 0xff & (write_offset >> 8);
	data[8] = 0xff & (write_offset >> 16);
	data[9] = 0xff & (write_offset >> 24);

	ret = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
		data, 10, read_buf, &actual_read_len);

	status = read_buf[PIP2_RESP_STATUS_OFFSET];
	if (ret || (status != 0x00)) {
		pt_debug(dev, DL_ERROR,
			"%s ROM FILE_SEEK failure: 0x%02X for file = %d, ret = %d\n",
			__func__, status, ret, file_handle);
		ret = -EIO;
	}
	return ret;
}

/*******************************************************************************
 * FUNCTION: _pt_pip2_file_crc
 *
 * SUMMARY: Using the BL PIP2 commands to calculate CRC for a file or portion of
 *   the file.
 *
 *	NOTE: The DUT must be in BL mode for this command to work
 *	NOTE: This command only can be used for BL version 1.8 or greater.
 *        BL version 1.8 added this change according to PGV-173.
 *
 * RETURNS:
 *	!0 = Error
 *	 0 = Success
 *
 * PARAMETERS:
 *	*dev          - pointer to device structure
 *	 file_handle  - File handle to read from
 *	 offset       - start offset for CRC calculation
 *	 length       - number of bytes to calculate CRC over
 *   read_buf     - pointer to the read buffer
 ******************************************************************************/
int _pt_pip2_file_crc(struct device *dev, u8 file_handle,
	u32 offset, u32 length, u8 *read_buf)
{
	int rc = 1;
	u16 actual_read_len;
	u8  data[10];

	data[0] = file_handle;
	data[1] = PIP2_FILE_IOCTL_CODE_FILE_CRC;
	data[2] = 0xff & offset;
	data[3] = 0xff & (offset >> 8);
	data[4] = 0xff & (offset >> 16);
	data[5] = 0xff & (offset >> 24);
	data[6] = 0xff & length;
	data[7] = 0xff & (length >> 8);
	data[8] = 0xff & (length >> 16);
	data[9] = 0xff & (length >> 24);

	rc = _pt_request_pip2_send_cmd(dev,
		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
		data, 10, read_buf, &actual_read_len);

	if (rc)
		pt_debug(dev, DL_ERROR,
			"%s Return FILE_CRC failure, rc = %d\n",
			__func__, rc);

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_ping_test
 *
 * SUMMARY: BIST type test that uses the PIP2 PING command and ramps up the
 *	optional payload from 0 bytes to max_bytes and ensures the PIP2
 *	response payload matches what was sent.
 *	The max payload size is 247:
 *		(255 - 2 byte reg address - 4 byte header - 2 byte CRC)
 *
 * RETURN:
 *       0 = success
 *      !0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to device structure
 *      *attr - pointer to device attributes
 *      *buf  - pointer to output buffer
 ******************************************************************************/
int pt_pip2_ping_test(struct device *dev, int max_bytes, int *last_packet_size)
{
	u16 actual_read_len;
	u8 *read_buf = NULL;
	u8 *data = NULL;
	int data_offset = PIP2_RESP_STATUS_OFFSET;
	int i = 1;
	int j = 0;
	int rc = 0;

	read_buf = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
	if (!read_buf)
		goto ping_test_exit;

	data = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
	if (!data)
		goto ping_test_exit;

	/* Load data payload with an array of walking 1's */
	for (i = 0; i < 255; i++)
		data[i] = 0x01 << (i % 8);

	/* Send 'max_bytes' PING cmds using 'i' bytes as payload for each */
	for (i = 0; i <= max_bytes; i++) {
		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
			PIP2_CMD_ID_PING, data, i, read_buf,
			&actual_read_len);
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s: PING failed with %d byte payload\n",
				 __func__, i);
			break;
		}
		/* Verify data returned matches data sent */
		for (j = 0; j < i; j++) {
			if (read_buf[data_offset + j] != data[j]) {
				pt_debug(dev, DL_DEBUG,
					"%s: PING packet size %d: sent[%d]=0x%02X recv[%d]=0x%02X\n",
					__func__, i, j, data[j], j,
					read_buf[data_offset + j]);
				goto ping_test_exit;
			}
		}
	}

ping_test_exit:
	*last_packet_size = i - 1;
	kfree(read_buf);
	kfree(data);
	return rc;
}

/*******************************************************************************
 * FUNCTION: _pt_ic_parse_input_hex
 *
 * SUMMARY: Parse a char data array as space delimited hex values into
 *	an int array.
 *
 * NOTE: _pt_ic_parse_input() function may have similar work while the type of
 *  buffer to store data is "u32". This function is still needed by the
 *  "command" sysfs node because the buffer type to store data is "u8".
 *
 * RETURN: Length of parsed data
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	*buf         - pointer to buffer that holds the input array to parse
 *	 buf_size    - size of buf
 *	*ic_buf      - pointer to array to store parsed data
 *	 ic_buf_size - max size of ic_buf
 ******************************************************************************/
static int _pt_ic_parse_input_hex(struct device *dev, const char *buf,
	size_t buf_size, u8 *ic_buf, size_t ic_buf_size)
{
	const char *pbuf = buf;
	unsigned long value;
	char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
	u32 i = 0;
	u32 j;
	int last = 0;
	int ret;

	pt_debug(dev, DL_DEBUG,
		"%s: pbuf=%p buf=%p size=%zu %s=%d buf=%s\n",
		__func__, pbuf, buf, buf_size, "scan buf size",
		PT_MAX_PIP2_MSG_SIZE, buf);

	while (pbuf <= (buf + buf_size)) {
		if (i >= PT_MAX_PIP2_MSG_SIZE) {
			pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
				__func__, "Max cmd size exceeded", i,
				PT_MAX_PIP2_MSG_SIZE);
			return -EINVAL;
		}
		if (i >= ic_buf_size) {
			pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
				__func__, "Buffer size exceeded", i,
				ic_buf_size);
			return -EINVAL;
		}
		while (((*pbuf == ' ') || (*pbuf == ','))
			&& (pbuf < (buf + buf_size))) {
			last = *pbuf;
			pbuf++;
		}

		if (pbuf >= (buf + buf_size))
			break;

		memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
		if ((last == ',') && (*pbuf == ',')) {
			pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
				__func__, "Invalid data format.");
			return -EINVAL;
		}
		for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
		     && (pbuf < (buf + buf_size))
		     && (*pbuf != ' ')
		     && (*pbuf != ','); j++) {
			last = *pbuf;
			scan_buf[j] = *pbuf++;
		}
		ret = kstrtoul(scan_buf, 16, &value);
		if (ret < 0) {
			pt_debug(dev, DL_ERROR,
				"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
				"Invalid data format. ", scan_buf,
				"Use \"0xHH,...,0xHH\"", " instead.",
				i, ret);
			return ret;
		}

		ic_buf[i] = value;
		pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx",
			__func__, i, value);
		i++;
	}

	return i;
}

/*******************************************************************************
 * FUNCTION: _pt_ic_parse_input
 *
 * SUMMARY: Parse user sysfs input data as a space or comma delimited list of
 * hex values or dec values into an int array with the following rules:
 *  1) Hex values must have a "0x" prefix for each element or the first element
 *     only
 *  2) Dec values do not have any prefix
 *  3) It is not allowed to have a mix of dec and hex values in the user input
 *     string
 *
 * RETURN: Number of values parsed
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	*buf         - pointer to buffer that holds the input array to parse
 *	 buf_size    - size of buf
 *	*out_buf      - pointer to array to store parsed data
 *	 out_buf_size - max size of buffer to be stored
 ******************************************************************************/
static int _pt_ic_parse_input(struct device *dev,
	const char *buf, size_t buf_size,
	u32 *out_buf, size_t out_buf_size)
{
	const char *pbuf = buf;
	unsigned long value;
	char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
	u32 i = 0;
	u32 j;
	int last = 0;
	int ret = 0;
	u8 str_base = 0;

	pt_debug(dev, DL_DEBUG,
		"%s: in_buf_size=%zu out_buf_size=%zu %s=%d buf=%s\n",
		__func__, buf_size, out_buf_size, "scan buf size",
		PT_MAX_PIP2_MSG_SIZE, buf);

	while (pbuf <= (buf + buf_size)) {
		if (i >= PT_MAX_PIP2_MSG_SIZE) {
			pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
				__func__, "Max cmd size exceeded", i,
				PT_MAX_PIP2_MSG_SIZE);
			ret = -EINVAL;
			goto error;
		}
		if (i >= out_buf_size) {
			pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
				__func__, "Buffer size exceeded", i,
				out_buf_size);
			ret = -EINVAL;
			goto error;
		}
		while (((*pbuf == ' ') || (*pbuf == ','))
			&& (pbuf < (buf + buf_size))) {
			last = *pbuf;
			pbuf++;
		}

		if (pbuf >= (buf + buf_size))
			break;

		memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
		if ((last == ',') && (*pbuf == ',')) {
			pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
				__func__, "Invalid data format.");
			ret = -EINVAL;
			goto error;
		}
		for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
		     && (pbuf < (buf + buf_size))
		     && (*pbuf != ' ')
		     && (*pbuf != ','); j++) {
			last = *pbuf;
			scan_buf[j] = *pbuf++;
		}

		if (i == 0) {
			if ((strncmp(scan_buf, "0x", 2) == 0) ||
					(strncmp(scan_buf, "0X", 2) == 0))
				str_base = 16;
			else
				str_base = 10;
		} else {
			if (((strncmp(scan_buf, "0x", 2) == 0) ||
				(strncmp(scan_buf, "0X", 2) == 0)) &&
				(str_base == 10)) {
				pt_debug(dev, DL_ERROR,
					"%s: Decimal and Heximal data mixed\n",
					__func__);
				ret = -EINVAL;
				goto error;
			}
		}
		ret = kstrtoul(scan_buf, str_base, &value);
		if (ret < 0) {
			pt_debug(dev, DL_ERROR,
				"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
				"Invalid data format. ", scan_buf,
				"Use \"0xHH,...,0xHH\" or \"DD DD DD ... DD\"",
				" instead.", i, ret);
			goto error;
		}

		out_buf[i] = value;
		pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx(%lu)",
			__func__, i, value, value);
		i++;
	}
	ret = i;
error:
	return ret;
}

#ifdef TTHE_TUNER_SUPPORT
/*******************************************************************************
 * FUNCTION: tthe_debugfs_open
 *
 * SUMMARY: Open method for tthe_tuner debugfs node. On some hosts the size of
 *	PT_MAX_PRBUF_SIZE (equal to PAGE_SIZE) is not large enough to handle
 *	printing a large number of fingers and sensor data without overflowing
 *	the buffer. tthe_tuner needs ~4K and so the buffer is sized to some
 *	even multiple of PAGE_SIZE
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *inode - file inode number
 *      *filp  - file pointer to debugfs file
 ******************************************************************************/
static int tthe_debugfs_open(struct inode *inode, struct file *filp)
{
	struct pt_core_data *cd = inode->i_private;
	u32 buf_size = PT_MAX_PRBUF_SIZE;

	filp->private_data = inode->i_private;
	if (cd->tthe_buf)
		return -EBUSY;

	while (buf_size < 4096)
		buf_size = buf_size << 1;

	pt_debug(cd->dev, DL_INFO, "%s:PT_MAX_BRBUF_SIZE=%d buf_size=%d\n",
		__func__, (int)PT_MAX_PRBUF_SIZE, (int)buf_size);

	cd->tthe_buf_size = buf_size;
	cd->tthe_buf = kzalloc(cd->tthe_buf_size, GFP_KERNEL);
	if (!cd->tthe_buf)
		return -ENOMEM;

	return 0;
}

/*******************************************************************************
 * FUNCTION: tthe_debugfs_close
 *
 * SUMMARY: Close method for tthe_tuner debugfs node.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *      *inode - file inode number
 *      *filp  - file pointer to debugfs file
 ******************************************************************************/
static int tthe_debugfs_close(struct inode *inode, struct file *filp)
{
	struct pt_core_data *cd = filp->private_data;

	filp->private_data = NULL;
	kfree(cd->tthe_buf);
	cd->tthe_buf = NULL;

	return 0;
}

/*******************************************************************************
 * FUNCTION: tthe_debugfs_read
 *
 * SUMMARY: Read method for tthe_tuner debugfs node. This function prints
 *	tthe_buf to user buffer.
 *
 * RETURN: Size of debugfs data print
 *
 * PARAMETERS:
 *      *filp   - file pointer to debugfs file
 *      *buf    - the user space buffer to read to
 *       count  - the maximum number of bytes to read
 *      *ppos   - the current position in the buffer
 ******************************************************************************/
static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf,
		size_t count, loff_t *ppos)
{
	struct pt_core_data *cd = filp->private_data;
	int size;
	int ret;
	static int partial_read;

	wait_event_interruptible(cd->wait_q,
			cd->tthe_buf_len != 0 || cd->tthe_exit);
	mutex_lock(&cd->tthe_lock);
	if (cd->tthe_exit) {
		mutex_unlock(&cd->tthe_lock);
		return 0;
	}
	if (count > cd->tthe_buf_len)
		size = cd->tthe_buf_len;
	else
		size = count;
	if (!size) {
		mutex_unlock(&cd->tthe_lock);
		return 0;
	}

	if (partial_read) {
		ret = copy_to_user(buf, cd->tthe_buf + partial_read, size);
		partial_read = 0;
	} else {
		ret = copy_to_user(buf, cd->tthe_buf, size);
	}
	if (size == count)
		partial_read = count;

	if (ret == size)
		return -EFAULT;
	size -= ret;
	cd->tthe_buf_len -= size;
	mutex_unlock(&cd->tthe_lock);
	*ppos += size;
	return size;
}

static const struct file_operations tthe_debugfs_fops = {
	.open = tthe_debugfs_open,
	.release = tthe_debugfs_close,
	.read = tthe_debugfs_read,
};
#endif

static struct pt_core_nonhid_cmd _pt_core_nonhid_cmd = {
	.start_bl              = _pt_request_pip_start_bl,
	.suspend_scanning      = _pt_request_pip_suspend_scanning,
	.resume_scanning       = _pt_request_pip_resume_scanning,
	.get_param             = _pt_request_pip_get_param,
	.set_param             = _pt_request_pip_set_param,
	.verify_cfg_block_crc  = _pt_request_pip_verify_config_block_crc,
	.get_config_row_size   = _pt_request_pip_get_config_row_size,
	.get_data_structure    = _pt_request_pip_get_data_structure,
	.run_selftest          = _pt_request_pip_run_selftest,
	.get_selftest_result   = _pt_request_pip_get_selftest_result,
	.load_self_test_param  = _pt_request_pip_load_self_test_param,
	.calibrate_idacs       = _pt_request_pip_calibrate_idacs,
	.calibrate_ext         = _pt_request_pip_calibrate_ext,
	.initialize_baselines  = _pt_request_pip_initialize_baselines,
	.exec_panel_scan       = _pt_request_pip_exec_panel_scan,
	.retrieve_panel_scan   = _pt_request_pip_retrieve_panel_scan,
	.read_data_block       = _pt_request_pip_read_data_block,
	.write_data_block      = _pt_request_pip_write_data_block,
	.user_cmd              = _pt_request_pip_user_cmd,
	.get_bl_info           = _pt_request_pip_bl_get_information,
	.initiate_bl           = _pt_request_pip_bl_initiate_bl,
	.launch_app            = _pt_request_pip_launch_app,
	.prog_and_verify       = _pt_request_pip_bl_program_and_verify,
	.verify_app_integrity  = _pt_request_pip_bl_verify_app_integrity,
	.get_panel_id          = _pt_request_pip_bl_get_panel_id,
	.pip2_send_cmd         = _pt_request_pip2_send_cmd,
	.pip2_send_cmd_no_int  = _pt_pip2_send_cmd_no_int,
	.pip2_file_open        = _pt_pip2_file_open,
	.pip2_file_close       = _pt_pip2_file_close,
	.pip2_file_erase       = _pt_pip2_file_erase,
	.read_us_file          = _pt_read_us_file,
	.manage_cal_data       = _pt_manage_local_cal_data,
	.calc_crc              = crc_ccitt_calculate,
#ifdef TTDL_DIAGNOSTICS
	.pip2_file_read        = _pt_pip2_file_read,
	.pip2_file_seek_offset = _pt_pip2_file_seek_offset,
	.pip2_file_get_stats   = _pt_pip2_file_get_stats,
	.pip2_file_crc         = _pt_pip2_file_crc,
#endif
};

static struct pt_core_commands _pt_core_commands = {
	.subscribe_attention           = _pt_subscribe_attention,
	.unsubscribe_attention         = _pt_unsubscribe_attention,
	.request_exclusive             = _pt_request_exclusive,
	.release_exclusive             = _pt_release_exclusive,
	.request_reset                 = _pt_request_reset,
	.request_pip2_launch_app       = _pt_request_pip2_launch_app,
	.request_enum                  = _pt_request_enum,
	.request_sysinfo               = _pt_request_sysinfo,
	.request_loader_pdata          = _pt_request_loader_pdata,
	.request_stop_wd               = _pt_request_stop_wd,
	.request_start_wd              = _pt_request_start_wd,
	.request_get_mode              = _pt_request_get_mode,
	.request_active_pip_prot       = _pt_request_active_pip_protocol,
	.request_pip2_get_mode_sysmode = _pt_request_pip2_get_mode_sysmode,
	.request_pip2_enter_bl         = _pt_request_pip2_enter_bl,
	.request_pip2_bin_hdr          = _pt_request_pip2_bin_hdr,
	.request_dut_generation        = _pt_request_dut_generation,
	.request_hw_version            = _pt_request_hw_version,
	.parse_sysfs_input             = _pt_ic_parse_input,
#ifdef TTHE_TUNER_SUPPORT
	.request_tthe_print            = _pt_request_tthe_print,
#endif
#ifdef TTDL_DIAGNOSTICS
	.request_toggle_err_gpio       = _pt_request_toggle_err_gpio,
#endif
	.nonhid_cmd                    = &_pt_core_nonhid_cmd,
	.request_get_fw_mode           = _pt_request_get_fw_sys_mode,
};

struct pt_core_commands *pt_get_commands(void)
{
	return &_pt_core_commands;
}
EXPORT_SYMBOL_GPL(pt_get_commands);

static DEFINE_MUTEX(core_list_lock);
static LIST_HEAD(core_list);
static DEFINE_MUTEX(module_list_lock);
static LIST_HEAD(module_list);
static int core_number;

/*******************************************************************************
 * FUNCTION: pt_probe_module
 *
 * SUMMARY: Add the module pointer to module_node and call the probe pointer.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *cd      - pointer to core data
 *  *module  - pointer to module structure
 ******************************************************************************/
static int pt_probe_module(struct pt_core_data *cd,
		struct pt_module *module)
{
	struct module_node *module_node;
	int rc = 0;

	module_node = kzalloc(sizeof(*module_node), GFP_KERNEL);
	if (!module_node)
		return -ENOMEM;

	module_node->module = module;

	mutex_lock(&cd->module_list_lock);
	list_add(&module_node->node, &cd->module_list);
	mutex_unlock(&cd->module_list_lock);

	rc = module->probe(cd->dev, &module_node->data);
	if (rc) {
		/*
		 * Remove from the list when probe fails
		 * in order not to call release
		 */
		mutex_lock(&cd->module_list_lock);
		list_del(&module_node->node);
		mutex_unlock(&cd->module_list_lock);
		kfree(module_node);
		goto exit;
	}

exit:
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_release_module
 *
 * SUMMARY: Call the release pointer and remove the module pointer from
 *  module_list.
 *
 * PARAMETERS:
 *  *cd      - pointer to core data
 *  *module  - pointer to module structure
 ******************************************************************************/
static void pt_release_module(struct pt_core_data *cd,
		struct pt_module *module)
{
	struct module_node *m, *m_n;

	mutex_lock(&cd->module_list_lock);
	list_for_each_entry_safe(m, m_n, &cd->module_list, node)
		if (m->module == module) {
			module->release(cd->dev, m->data);
			list_del(&m->node);
			kfree(m);
			break;
		}
	mutex_unlock(&cd->module_list_lock);
}

/*******************************************************************************
 * FUNCTION: pt_probe_modules
 *
 * SUMMARY: Iterate module_list and probe each module.
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static void pt_probe_modules(struct pt_core_data *cd)
{
	struct pt_module *m;
	int rc = 0;

	mutex_lock(&module_list_lock);
	list_for_each_entry(m, &module_list, node) {
		pt_debug(cd->dev, DL_ERROR, "%s: Probe module %s\n",
			__func__, m->name);
		rc = pt_probe_module(cd, m);
		if (rc)
			pt_debug(cd->dev, DL_ERROR,
				"%s: Probe fails for module %s\n",
				__func__, m->name);
	}
	mutex_unlock(&module_list_lock);
}

/*******************************************************************************
 * FUNCTION: pt_release_modules
 *
 * SUMMARY: Iterate module_list and remove each module.
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static void pt_release_modules(struct pt_core_data *cd)
{
	struct pt_module *m;

	mutex_lock(&module_list_lock);
	list_for_each_entry(m, &module_list, node)
		pt_release_module(cd, m);
	mutex_unlock(&module_list_lock);
}

/*******************************************************************************
 * FUNCTION: pt_get_core_data
 *
 * SUMMARY: Iterate core_list and get core data.
 *
 * RETURN:
 *   pointer to core data or null pointer if fail
 *
 * PARAMETERS:
 *  *id  - pointer to core id
 ******************************************************************************/
struct pt_core_data *pt_get_core_data(char *id)
{
	struct pt_core_data *d;

	list_for_each_entry(d, &core_list, node)
		if (!strncmp(d->core_id, id, 20))
			return d;
	return NULL;
}
EXPORT_SYMBOL_GPL(pt_get_core_data);

/*******************************************************************************
 * FUNCTION: pt_add_core
 *
 * SUMMARY: Add core data to the core_list.
 *
 * PARAMETERS:
 *  *dev  - pointer to device structure
 ******************************************************************************/
static void pt_add_core(struct device *dev)
{
	struct pt_core_data *d;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	mutex_lock(&core_list_lock);
	list_for_each_entry(d, &core_list, node)
		if (d->dev == dev)
			goto unlock;

	list_add(&cd->node, &core_list);
unlock:
	mutex_unlock(&core_list_lock);
}

/*******************************************************************************
 * FUNCTION: pt_del_core
 *
 * SUMMARY: Remove core data from the core_list.
 *
 * PARAMETERS:
 *  *dev  - pointer to device structure
 ******************************************************************************/
static void pt_del_core(struct device *dev)
{
	struct pt_core_data *d, *d_n;

	mutex_lock(&core_list_lock);
	list_for_each_entry_safe(d, d_n, &core_list, node)
		if (d->dev == dev) {
			list_del(&d->node);
			goto unlock;
		}
unlock:
	mutex_unlock(&core_list_lock);
}

/*******************************************************************************
 * FUNCTION: pt_register_module
 *
 * SUMMARY: Register the module to module_list and probe the module for each
 *  core.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *module  - pointer to module structure
 ******************************************************************************/
int pt_register_module(struct pt_module *module)
{
	struct pt_module *m;
	struct pt_core_data *cd;

	int rc = 0;

	if (!module || !module->probe || !module->release)
		return -EINVAL;

	mutex_lock(&module_list_lock);
	list_for_each_entry(m, &module_list, node)
		if (m == module) {
			rc = -EEXIST;
			goto unlock;
		}

	list_add(&module->node, &module_list);

	/* Probe the module for each core */
	mutex_lock(&core_list_lock);
	list_for_each_entry(cd, &core_list, node)
		pt_probe_module(cd, module);
	mutex_unlock(&core_list_lock);

unlock:
	mutex_unlock(&module_list_lock);
	return rc;
}
EXPORT_SYMBOL_GPL(pt_register_module);

/*******************************************************************************
 * FUNCTION: pt_unregister_module
 *
 * SUMMARY: Release the module for each core and remove the module from
 *  module_list.
 *
 * RETURN:
 *   0 = success
 *  !0 = failure
 *
 * PARAMETERS:
 *  *module  - pointer to module structure
 ******************************************************************************/
void pt_unregister_module(struct pt_module *module)
{
	struct pt_module *m, *m_n;
	struct pt_core_data *cd;

	if (!module)
		return;

	mutex_lock(&module_list_lock);

	/* Release the module for each core */
	mutex_lock(&core_list_lock);
	list_for_each_entry(cd, &core_list, node)
		pt_release_module(cd, module);
	mutex_unlock(&core_list_lock);

	list_for_each_entry_safe(m, m_n, &module_list, node)
		if (m == module) {
			list_del(&m->node);
			break;
		}

	mutex_unlock(&module_list_lock);
}
EXPORT_SYMBOL_GPL(pt_unregister_module);

/*******************************************************************************
 * FUNCTION: pt_get_module_data
 *
 * SUMMARY: Get module data from module_node by module_list.
 *
 * RETURN:
 *   pointer to module data
 *
 * PARAMETERS:
 *  *dev     - pointer to device structure
 *  *module  - pointer to module structure
 ******************************************************************************/
void *pt_get_module_data(struct device *dev, struct pt_module *module)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct module_node *m;
	void *data = NULL;

	mutex_lock(&cd->module_list_lock);
	list_for_each_entry(m, &cd->module_list, node)
		if (m->module == module) {
			data = m->data;
			break;
		}
	mutex_unlock(&cd->module_list_lock);

	return data;
}
EXPORT_SYMBOL(pt_get_module_data);

#ifdef CONFIG_HAS_EARLYSUSPEND
/*******************************************************************************
 * FUNCTION: pt_early_suspend
 *
 * SUMMARY: Android PM architecture function that will call "PT_ATTEN_SUSPEND"
 *  attention list.
 *
 * PARAMETERS:
 *  *h  - pointer to early_suspend structure
 ******************************************************************************/
static void pt_early_suspend(struct early_suspend *h)
{
	struct pt_core_data *cd =
		container_of(h, struct pt_core_data, es);

	call_atten_cb(cd, PT_ATTEN_SUSPEND, 0);
}
/*******************************************************************************
 * FUNCTION: pt_late_resume
 *
 * SUMMARY: Android PM architecture function that will call "PT_ATTEN_RESUME"
 * attention list.
 *
 * PARAMETERS:
 *  *h  - pointer to early_suspend structure
 ******************************************************************************/
static void pt_late_resume(struct early_suspend *h)
{
	struct pt_core_data *cd =
		container_of(h, struct pt_core_data, es);

	call_atten_cb(cd, PT_ATTEN_RESUME, 0);
}

/*******************************************************************************
 * FUNCTION: pt_setup_early_suspend
 *
 * SUMMARY: Register early/suspend function to the system.
 *
 * PARAMETERS:
 *  *cd  - pointer to core data
 ******************************************************************************/
static void pt_setup_early_suspend(struct pt_core_data *cd)
{
	cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
	cd->es.suspend = pt_early_suspend;
	cd->es.resume = pt_late_resume;

	register_early_suspend(&cd->es);
}
#elif defined(CONFIG_DRM)

static void pt_resume_work(struct work_struct *work)
{
	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
					resume_work);
	int rc = 0;

	pt_debug(pt_data->dev, DL_INFO, "%s start ", __func__);
	if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
		return;

	rc = pt_core_easywake_off(pt_data);
	if (rc < 0) {
		pt_debug(pt_data->dev, DL_ERROR,
			"%s: Error on wake\n", __func__);
	}
	pt_debug(pt_data->dev, DL_INFO, "%s touch to wake disabled ", __func__);
	return;
}

static void pt_suspend_work(struct work_struct *work)
{
	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
					suspend_work);
	int rc = 0;

	pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__);

	if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
		return;

	rc = pt_core_easywake_on(pt_data);
	if (rc < 0) {
		pt_debug(pt_data->dev, DL_ERROR, "%s: Error on sleep\n", __func__);
		return;
	}
	pt_debug(pt_data->dev, DL_INFO, "%s Exit touch to wake enabled\n", __func__);
	return;
}

#if defined(CONFIG_PANEL_NOTIFIER)
/*******************************************************************************
 * FUNCTION: panel_event_notifier_callback
 *
 * SUMMARY: Call back function for Panel Event notifier to allow to call
 * resume/suspend attention list.
 *
 * PARAMETERS:
 *   tag           - type of input panel.
 *  *notification  - pointer to notification details.
 *  *client_data   - pointer to core data
 ******************************************************************************/
static void panel_event_notifier_callback(enum panel_event_notifier_tag tag,
		struct panel_event_notification *notification, void *client_data)
{
	struct pt_core_data *cd = client_data;

	if(!notification)
	{
		pt_debug(cd->dev,DL_INFO, "%s: Invalid notification\n", __func__);
		return;
	}

	pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__);
	if (cd->quick_boot || cd->drv_debug_suspend)
		goto exit;

	pt_debug(cd->dev, DL_INFO, "%s: DRM event:%d,fb_state %d",
		__func__, notification->notif_type, cd->fb_state);
	pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ",
		__func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", cd->fb_state);

	if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) {
		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
		if (notification->notif_data.early_trigger) {
			pr_err("%s: resume: event = %d, not care\n", __func__, notification->notif_type);
			pt_debug(cd->dev, DL_INFO, "%s: resume: event = %d, not care\n",
				__func__, notification->notif_type);
		} else {
				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
					__func__);
				cancel_work_sync(&cd->resume_offload_work);
				cancel_work_sync(&cd->suspend_offload_work);
				cancel_work_sync(&cd->resume_work);
				cancel_work_sync(&cd->suspend_work);

				queue_work(cd->pt_workqueue, &cd->resume_work);
#if defined(CONFIG_PM_SLEEP)
				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
					__func__);
				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
					pt_core_resume_(cd->dev);
#endif
				cd->fb_state = FB_ON;
				pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__);
		}
	} else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) {
		pt_debug(cd->dev, DL_INFO, "%s: BLANK!\n", __func__);
		if (notification->notif_data.early_trigger) {
#if defined(CONFIG_PM_SLEEP)
			pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n",
				__func__);
			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
				pt_core_suspend_(cd->dev);
#endif
			cancel_work_sync(&cd->resume_offload_work);
			cancel_work_sync(&cd->suspend_offload_work);
			cancel_work_sync(&cd->resume_work);
			cancel_work_sync(&cd->suspend_work);

			queue_work(cd->pt_workqueue, &cd->suspend_work);
			cd->fb_state = FB_OFF;
			pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
		} else {
			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n",
				__func__, notification->notif_type);
		}
	} else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) {
		pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__);
                if (notification->notif_data.early_trigger) {
#if defined(CONFIG_PM_SLEEP)
			pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", __func__);
			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
				pt_core_suspend_(cd->dev);
#endif
			cancel_work_sync(&cd->resume_offload_work);
			cancel_work_sync(&cd->suspend_offload_work);
			cancel_work_sync(&cd->resume_work);
			cancel_work_sync(&cd->suspend_work);

			queue_work(cd->pt_workqueue, &cd->suspend_work);
			cd->fb_state = FB_OFF;
			pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
		} else {
			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n",
                                __func__, notification->notif_type);
		}

	} else {
		pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n",
			__func__, notification->notif_type);
	}
exit:
	return;
}

/*******************************************************************************
 * FUNCTION: pt_setup_panel_event_notifier
 *
 * SUMMARY: Set up call back function into drm notifier.
 *
 * PARAMETERS:
 *  *cd   - pointer to core data
 ******************************************************************************/
static void pt_setup_panel_event_notifier(struct pt_core_data *cd)
{
	void *cookie = NULL;

	if (!active_panel)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Active panel not registered!\n", __func__);

	cd->pt_workqueue = create_singlethread_workqueue("ts_wq");
	if (!cd->pt_workqueue) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: worker thread creation failed !\n", __func__);
	}

	if (cd->pt_workqueue) {
		INIT_WORK(&cd->resume_work, pt_resume_work);
		INIT_WORK(&cd->suspend_work, pt_suspend_work);
	}


	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH,
			active_panel,&panel_event_notifier_callback, cd);

	if (active_panel && !cookie)
	{
		pt_debug(cd->dev, DL_ERROR,
				"%s: Register notifier failed!\n", __func__);
	}
	cd->entry = cookie;
}
#else

/*******************************************************************************
 * FUNCTION: drm_notifier_callback
 *
 * SUMMARY: Call back function for DRM notifier to allow to call
 * resume/suspend attention list.
 *
 * RETURN:
 *   0 = success
 *
 * PARAMETERS:
 *  *self   - pointer to notifier_block structure
 *   event  - event type of fb notifier
 *  *data   - pointer to fb_event structure
 ******************************************************************************/
static int drm_notifier_callback(struct notifier_block *self,
		unsigned long event, void *data)
{
	struct pt_core_data *cd =
		container_of(self, struct pt_core_data, fb_notifier);
	struct drm_panel_notifier *evdata = data;
	int *blank;

	pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__);

	if (!evdata)
		goto exit;

	if (!(event == DRM_PANEL_EARLY_EVENT_BLANK ||
		event == DRM_PANEL_EVENT_BLANK)) {
		pt_debug(cd->dev, DL_INFO, "%s: Event(%lu) do not need process\n",
			__func__, event);
		goto exit;
	}

	if (cd->quick_boot || cd->drv_debug_suspend)
		goto exit;

	blank = evdata->data;
	pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ",
		__func__, event, *blank, cd->fb_state, cd->sleep_state);
	pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ",
		__func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", cd->fb_state);

	if (*blank == DRM_PANEL_BLANK_UNBLANK) {
		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
		if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
			pt_debug(cd->dev, DL_INFO, "%s: resume: event = %lu, not care\n",
				__func__, event);
		} else if (event == DRM_PANEL_EVENT_BLANK) {
			if (cd->fb_state != FB_ON) {
				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
					__func__);
				cancel_work_sync(&cd->resume_offload_work);
				cancel_work_sync(&cd->suspend_offload_work);
				cancel_work_sync(&cd->resume_work);
				cancel_work_sync(&cd->suspend_work);

				queue_work(cd->pt_workqueue, &cd->resume_work);
#if defined(CONFIG_PM_SLEEP)
				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
					__func__);
				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
					pt_core_resume_(cd->dev);
#endif
				cd->fb_state = FB_ON;
				pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__);
			}
		}
	} else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) {
		pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__);
		if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
			if (cd->fb_state != FB_OFF) {
#if defined(CONFIG_PM_SLEEP)
				pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n",
					__func__);
				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
					pt_core_suspend_(cd->dev);
#endif
				cancel_work_sync(&cd->resume_offload_work);
				cancel_work_sync(&cd->suspend_offload_work);
				cancel_work_sync(&cd->resume_work);
				cancel_work_sync(&cd->suspend_work);

				queue_work(cd->pt_workqueue, &cd->suspend_work);
				cd->fb_state = FB_OFF;
				pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
			}

		} else if (event == DRM_PANEL_EVENT_BLANK) {
			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %lu, not care\n",
				__func__, event);
		}
	} else {
		pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n",
			__func__, *blank);
	}
exit:
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_setup_drm_notifier
 *
 * SUMMARY: Set up call back function into drm notifier.
 *
 * PARAMETERS:
 *  *cd   - pointer to core data
 ******************************************************************************/
static void pt_setup_drm_notifier(struct pt_core_data *cd)
{
	cd->fb_state = FB_NONE;
	cd->fb_notifier.notifier_call = drm_notifier_callback;
	pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__);

	if (!active_panel)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Active panel not registered!\n", __func__);

	cd->pt_workqueue = create_singlethread_workqueue("ts_wq");
	if (!cd->pt_workqueue) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: worker thread creation failed !\n", __func__);
	}

	if (cd->pt_workqueue) {
		INIT_WORK(&cd->resume_work, pt_resume_work);
		INIT_WORK(&cd->suspend_work, pt_suspend_work);
	}

	if (active_panel &&
		drm_panel_notifier_register(active_panel,
			&cd->fb_notifier) < 0)
		pt_debug(cd->dev, DL_ERROR,
			"%s: Register notifier failed!\n", __func__);
}
#endif
#elif defined(CONFIG_FB)
/*******************************************************************************
 * FUNCTION: fb_notifier_callback
 *
 * SUMMARY: Call back function for FrameBuffer notifier to allow to call
 * resume/suspend attention list.
 *
 * RETURN:
 *   0 = success
 *
 * PARAMETERS:
 *  *self   - pointer to notifier_block structure
 *   event  - event type of fb notifier
 *  *data   - pointer to fb_event structure
 ******************************************************************************/
static int fb_notifier_callback(struct notifier_block *self,
		unsigned long event, void *data)
{
	struct pt_core_data *cd =
		container_of(self, struct pt_core_data, fb_notifier);
	struct fb_event *evdata = data;
	int *blank;

	if (event != FB_EVENT_BLANK || !evdata)
		goto exit;

	blank = evdata->data;
	if (*blank == FB_BLANK_UNBLANK) {
		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
		if (cd->fb_state != FB_ON) {
			call_atten_cb(cd, PT_ATTEN_RESUME, 0);
#if defined(CONFIG_PM_SLEEP)
			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
				pt_core_resume_(cd->dev);
#endif
			cd->fb_state = FB_ON;
		}
	} else if (*blank == FB_BLANK_POWERDOWN) {
		pt_debug(cd->dev, DL_INFO, "%s: POWERDOWN!\n", __func__);
		if (cd->fb_state != FB_OFF) {
#if defined(CONFIG_PM_SLEEP)
			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
				pt_core_suspend_(cd->dev);
#endif
			call_atten_cb(cd, PT_ATTEN_SUSPEND, 0);
			cd->fb_state = FB_OFF;
		}
	}

exit:
	return 0;
}

/*******************************************************************************
 * FUNCTION: pt_setup_fb_notifier
 *
 * SUMMARY: Set up call back function into fb notifier.
 *
 * PARAMETERS:
 *  *cd   - pointer to core data
 ******************************************************************************/
static void pt_setup_fb_notifier(struct pt_core_data *cd)
{
	int rc = 0;

	cd->fb_state = FB_ON;
	cd->fb_notifier.notifier_call = fb_notifier_callback;

	rc = fb_register_client(&cd->fb_notifier);
	if (rc)
		pt_debug(cd->dev, DL_ERROR,
			"Unable to register fb_notifier: %d\n", rc);
}
#endif

/*******************************************************************************
 * FUNCTION: pt_watchdog_work
 *
 * SUMMARY: This is where the watchdog work is done except if the DUT is
 *	sleeping then this function simply returns. If the DUT is awake the
 *	first thing is to ensure the IRQ is not stuck asserted meaning that
 *	somehow a response is waiting on the DUT that has not been read. If
 *	this occurs the message is simply consumed. If or once the IRQ is
 *	cleared, a PIP PING message is sent to the DUT and if the response
 *	is received the watchdog succeeds and exits, if no response is seen
 *	a startup is queued unless the maximum number of startups have already
 *	been attempted, in that case a BL is attempted.
 *
 *	NOTE: pt_stop_wd_timer() cannot be called within the context of this
 *	      work thread
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *work - pointer to a work structure for the watchdog work queue
 ******************************************************************************/
static void pt_watchdog_work(struct work_struct *work)
{
	int rc = 0;
	struct pt_core_data *cd = container_of(work,
				struct pt_core_data, watchdog_work);
	/*
	 * if found the current sleep_state is SS_SLEEPING
	 * then no need to request_exclusive, directly return
	 */
	if (cd->sleep_state == SS_SLEEPING)
		return;

#ifdef TTDL_DIAGNOSTICS
	cd->watchdog_count++;
#endif /* TTDL_DIAGNOSTICS */

	/*
	 * The first WD interval was extended to allow DDI to come up.
	 * If the WD interval is not the default then adjust timer to the
	 * current setting. The user can override value with drv_debug sysfs.
	 */
	if (cd->watchdog_interval != PT_WATCHDOG_TIMEOUT) {
		mod_timer_pending(&cd->watchdog_timer, jiffies +
			msecs_to_jiffies(cd->watchdog_interval));
	}

	if (pt_check_irq_asserted(cd)) {
#ifdef TTDL_DIAGNOSTICS
		cd->watchdog_irq_stuck_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_IRQ_STUCK);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: TTDL WD found IRQ asserted, attempt to clear\n",
			__func__);
		pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
	}

	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail get exclusive ex=%p own=%p\n",
				__func__, cd->exclusive_dev, cd->dev);
		goto queue_startup;
	}

	rc = pt_pip_null_(cd);

	if (release_exclusive(cd, cd->dev) < 0)
		pt_debug(cd->dev, DL_ERROR,
				"%s: fail to release exclusive\n", __func__);

queue_startup:
	if (rc) {
#ifdef TTDL_DIAGNOSTICS
		cd->watchdog_failed_access_count++;
		pt_toggle_err_gpio(cd, PT_ERR_GPIO_EXCLUSIVE_ACCESS);
#endif /* TTDL_DIAGNOSTICS */
		pt_debug(cd->dev, DL_ERROR,
			"%s: failed to access device in WD, retry count=%d\n",
			__func__, cd->startup_retry_count);

		/* Already tried FW upgrade because of watchdog but failed */
		if (cd->startup_retry_count > PT_WATCHDOG_RETRY_COUNT)
			return;

		if (cd->startup_retry_count++ < PT_WATCHDOG_RETRY_COUNT) {
			/*
			 * Any wrapper function that trys to disable the
			 * WD killing this worker cannot be called here.
			 */
			rc = request_exclusive(cd, cd->dev,
				PT_REQUEST_EXCLUSIVE_TIMEOUT);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: fail get exclusive ex=%p own=%p\n",
					__func__, cd->exclusive_dev, cd->dev);
				goto exit;
			}

			cd->hw_detected = false;
			cd->startup_status = STARTUP_STATUS_START;
			pt_debug(cd->dev, DL_DEBUG,
					"%s: Startup Status Reset\n", __func__);
			rc = pt_dut_reset_and_wait(cd);
			if (release_exclusive(cd, cd->dev) < 0)
				pt_debug(cd->dev, DL_ERROR,
					"%s: fail to release exclusive\n",
					__func__);
			if (!rc) {
				cd->hw_detected = true;
				if (!cd->flashless_dut)
					pt_queue_enum(cd);
			}
#ifdef TTDL_DIAGNOSTICS
			cd->wd_xres_count++;
			pt_debug(cd->dev, DL_ERROR,
				"%s: Comm Failed - DUT reset [#%d]\n",
				__func__, cd->wd_xres_count);
#endif /* TTDL_DIAGNOSTICS */
		} else {
			/*
			 * After trying PT_WATCHDOG_RETRY_COUNT times to
			 * reset the part to regain communications, try to BL
			 */
			pt_debug(cd->dev, DL_ERROR,
				"%s: WD DUT access failure, Start FW Upgrade\n",
				__func__);
#ifdef TTDL_DIAGNOSTICS
			/*
			 * When diagnostics is enabled allow TTDL to keep
			 * trying to find the DUT. This allows the DUT to be
			 * hot swap-able while the host stays running. In
			 * production this may not be wanted as a customer
			 * may have several touch drivers and any driver
			 * that doesn't match the current DUT should give
			 * up trying and give up using the bus.
			 */
			pt_debug(cd->dev, DL_INFO,
				"%s: Resetting startup_retry_count\n",
				__func__);
			cd->startup_retry_count = 0;
#endif /* TTDL_DIAGNOSTICS */
			/*
			 * Since fw may be broken,reset sysinfo ready flag
			 * to let upgrade function work.
			 */
			mutex_lock(&cd->system_lock);
			cd->sysinfo.ready = false;
			mutex_unlock(&cd->system_lock);

			if (cd->active_dut_generation == DUT_UNKNOWN) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Queue Restart\n", __func__);
				pt_queue_restart(cd);
			} else
				kthread_run(start_fw_upgrade, cd, "pt_loader");
		}
	} else {
		cd->hw_detected = true;
		if (cd->startup_status <= (STARTUP_STATUS_FW_RESET_SENTINEL |
		    STARTUP_STATUS_BL_RESET_SENTINEL)) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: HW detected but not enumerated\n",
				__func__);
			pt_queue_enum(cd);
		}
	}

exit:
	pt_start_wd_timer(cd);
}

#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
/*******************************************************************************
 * FUNCTION: pt_watchdog_timer
 *
 * SUMMARY: The function that is called when the WD timer expires. If the
 *	watchdog work is not already busy schedule the watchdog work queue.
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      handle - Handle to the watchdog timer
 ******************************************************************************/
static void pt_watchdog_timer(unsigned long handle)
{
	struct pt_core_data *cd = (struct pt_core_data *)handle;
	if (!cd)
		return;

	pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n",
		__func__);

	if (!work_pending(&cd->watchdog_work))
		schedule_work(&cd->watchdog_work);
}
#else
/*******************************************************************************
 * FUNCTION: pt_watchdog_timer
 *
 * SUMMARY: The function that is called when the WD timer expires. If the
 *	watchdog work is not already busy schedule the watchdog work queue.
 *
 * RETURN: void
 *
 * PARAMETERS:
 *      *t - Pointer to timer list
 ******************************************************************************/
static void pt_watchdog_timer(struct timer_list *t)
{
	struct pt_core_data *cd = from_timer(cd, t, watchdog_timer);
	if (!cd)
		return;

	pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n",
		__func__);

	if (!work_pending(&cd->watchdog_work))
		schedule_work(&cd->watchdog_work);
}
#endif




/*******************************************************************************
 * Core sysfs show and store functions
 ******************************************************************************/

/*******************************************************************************
 * FUNCTION: pt_hw_version_show
 *
 * SUMMARY: Gets the HW version for either PIP1.x or PIP2.x DUTS
 *	Output data format: [SiliconID].[RevID FamilyID].[PanelID]
 *
 * RETURN: size of data written to sysfs node
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes structure
 *	*buf  - pointer to print output buffer
 ******************************************************************************/
static ssize_t pt_hw_version_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	_pt_request_hw_version(dev, cd->hw_version);
	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s\n", cd->hw_version);
}
static DEVICE_ATTR(hw_version, 0444, pt_hw_version_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_drv_version_show
 *
 * SUMMARY: Show method for the drv_version sysfs node that will show the
 *	TTDL version information
 *
 * RETURN: Char buffer with printed TTDL version information
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_drv_version_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
		"Driver: %s\nVersion: %s\nDate: %s\n",
		pt_driver_core_name, pt_driver_core_version,
		pt_driver_core_date);
}
static DEVICE_ATTR(drv_version, 0444, pt_drv_version_show, NULL);
static DEVICE_ATTR(drv_ver, 0444, pt_drv_version_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_fw_version_show
 *
 * SUMMARY: Show method for the fw_version sysfs node that will
 *	show the firmware, bootloader and PIP version information
 *
 * RETURN: Size of printed buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_fw_version_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_ttdata *ttdata;
	int rc = 0;

	if (cd->mode == PT_MODE_OPERATIONAL)
		rc = pt_hid_output_get_sysinfo_(cd);

	pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n",
		__func__, cd->mode, cd->sysinfo.ready);

	if (cd->sysinfo.ready)
		ttdata = &cd->sysinfo.ttdata;
	else
		rc = -ENODATA;

	if (!rc) {
		return scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"FW    : %d.%d.%d\n"
			"Config: %d\n"
			"BL    : %d.%d\n"
			"PIP   : %d.%d\n",
			rc,
			ttdata->fw_ver_major, ttdata->fw_ver_minor,
			ttdata->revctrl,
			ttdata->fw_ver_conf,
			ttdata->bl_ver_major, ttdata->bl_ver_minor,
			ttdata->pip_ver_major, ttdata->pip_ver_minor);
	} else {
		return scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"FW    : n/a\n"
			"Config: n/a\n"
			"BL    : n/a\n"
			"PIP   : n/a\n",
			rc);
	}
}
static DEVICE_ATTR(fw_version, 0444, pt_fw_version_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_sysinfo_show
 *
 * SUMMARY: Show method for the sysinfo sysfs node that will
 *	show all the information from get system information command.
 *
 * RETURN: Size of printed buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_sysinfo_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_sysinfo *si;
	struct pt_ttdata *ttdata = NULL;
	struct pt_sensing_conf_data *scd = NULL;
	int rc = 0;

	if (cd->mode == PT_MODE_OPERATIONAL) {
		rc = pt_hid_output_get_sysinfo_(cd);
		if (cd->sysinfo.ready) {
			si = &cd->sysinfo;
			ttdata = &si->ttdata;
			scd = &si->sensing_conf_data;
		} else
			rc = -ENODATA;
	} else
		rc = -EPERM;

	pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n",
		__func__, cd->mode, cd->sysinfo.ready);

	if (!rc && ttdata && scd) {
		return scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"pip_ver_major: 0x%02X\n"
			"pip_ver_minor: 0x%02X\n"
			"fw_pid       : 0x%04X\n"
			"fw_ver_major : 0x%02X\n"
			"fw_ver_minor : 0x%02X\n"
			"revctrl      : 0x%08X\n"
			"fw_ver_conf  : 0x%04X\n"
			"bl_ver_major : 0x%02X\n"
			"bl_ver_minor : 0x%02X\n"
			"jtag_id_h    : 0x%04X\n"
			"jtag_id_l    : 0x%04X\n"
			"mfg_id[0]    : 0x%02X\n"
			"mfg_id[1]    : 0x%02X\n"
			"mfg_id[2]    : 0x%02X\n"
			"mfg_id[3]    : 0x%02X\n"
			"mfg_id[4]    : 0x%02X\n"
			"mfg_id[5]    : 0x%02X\n"
			"mfg_id[6]    : 0x%02X\n"
			"mfg_id[7]    : 0x%02X\n"
			"post_code    : 0x%04X\n"
			"electrodes_x : 0x%02X\n"
			"electrodes_y : 0x%02X\n"
			"len_x        : 0x%04X\n"
			"len_y        : 0x%04X\n"
			"res_x        : 0x%04X\n"
			"res_y        : 0x%04X\n"
			"max_z        : 0x%04X\n"
			"origin_x     : 0x%02X\n"
			"origin_y     : 0x%02X\n"
			"panel_id     : 0x%02X\n"
			"btn          : 0x%02X\n"
			"scan_mode    : 0x%02X\n"
			"max_num_of_tch_per_refresh_cycle: 0x%02X\n",
			rc,
			ttdata->pip_ver_major,
			ttdata->pip_ver_minor,
			ttdata->fw_pid,
			ttdata->fw_ver_major,
			ttdata->fw_ver_minor,
			ttdata->revctrl,
			ttdata->fw_ver_conf,
			ttdata->bl_ver_major,
			ttdata->bl_ver_minor,
			ttdata->jtag_id_h,
			ttdata->jtag_id_l,
			ttdata->mfg_id[0],
			ttdata->mfg_id[1],
			ttdata->mfg_id[2],
			ttdata->mfg_id[3],
			ttdata->mfg_id[4],
			ttdata->mfg_id[5],
			ttdata->mfg_id[6],
			ttdata->mfg_id[7],
			ttdata->post_code,
			scd->electrodes_x,
			scd->electrodes_y,
			scd->len_x,
			scd->len_y,
			scd->res_x,
			scd->res_y,
			scd->max_z,
			scd->origin_x,
			scd->origin_y,
			scd->panel_id,
			scd->btn,
			scd->scan_mode,
			scd->max_tch);
	} else {
		return scnprintf(buf, strlen(buf),
			"Status: %d\n",
			rc);
	}
}
static DEVICE_ATTR(sysinfo, 0444, pt_sysinfo_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_hw_reset_show
 *
 * SUMMARY: The show method for the hw_reset sysfs node that does a hw reset
 *	by toggling the XRES line and then calls the startup function to
 *	allow TTDL to re-enumerate the DUT.
 *	The printed value reflects the status of the full reset/enum.
 *
 * PARAMETERS:
 *      *dev  - pointer to Device structure
 *      *attr - pointer to the device attribute structure
 *      *buf  - pointer to buffer to print
 ******************************************************************************/
static ssize_t pt_hw_reset_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int rc = 0;
	int time = 0;
	u8 reset_status = 0;
	int t;
	struct pt_hid_desc hid_desc;

	memset(&hid_desc, 0, sizeof(hid_desc));

	/* Only allow DUT reset if no active BL in progress */
	mutex_lock(&cd->firmware_class_lock);

	mutex_lock(&cd->system_lock);
	cd->startup_state = STARTUP_NONE;
	mutex_unlock(&(cd->system_lock));

	pt_stop_wd_timer(cd);

	/* ensure no left over exclusive access is still locked */
	release_exclusive(cd, cd->dev);

	rc = pt_dut_reset(cd, PT_CORE_CMD_PROTECTED);
	if (rc) {
		mutex_unlock(&cd->firmware_class_lock);
		pt_debug(cd->dev, DL_ERROR,
			"%s: HW reset failed rc = %d\n", __func__, rc);
		goto exit_hw_reset;
	}
	reset_status |= 0x01 << 0;

	if (cd->flashless_dut) {
		mutex_unlock(&cd->firmware_class_lock);

		t = wait_event_timeout(cd->wait_q, (cd->fw_updating == true),
			msecs_to_jiffies(200));
		if (IS_TMO(t)) {
			pt_debug(dev, DL_ERROR,
				"%s: Timeout waiting for FW update",
				__func__);
			rc = -ETIME;
			goto exit_hw_reset;
		} else {
			pt_debug(dev, DL_INFO,
				 "%s: ----- Wait FW Loading ----",
				 __func__);
			rc = _pt_request_wait_for_enum_state(
			    dev, 4000, STARTUP_STATUS_FW_RESET_SENTINEL);
			if (rc) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: No FW Sentinel detected rc = %d\n",
					__func__, rc);
				goto exit_hw_reset;
			}
			reset_status |= 0x01 << 1;
		}
	} else {
		/* Wait for any sentinel */
		rc = _pt_request_wait_for_enum_state(dev, 150,
			STARTUP_STATUS_BL_RESET_SENTINEL |
			STARTUP_STATUS_FW_RESET_SENTINEL);
		if (rc) {
			mutex_unlock(&cd->firmware_class_lock);
			pt_debug(cd->dev, DL_ERROR,
				"%s: No Sentinel detected rc = %d\n",
				__func__, rc);
			goto exit_hw_reset;
		}

		/* sleep needed to ensure no cmd is sent while DUT will NAK */
		msleep(30);

		if (cd->active_dut_generation == DUT_PIP1_ONLY) {
			rc = pt_get_hid_descriptor_(cd, &hid_desc);
			if (rc < 0) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: Error on getting HID descriptor r=%d\n",
					__func__, rc);
				goto exit_hw_reset;
			}

			cd->mode = pt_get_mode(cd, &hid_desc);
			if (cd->mode == PT_MODE_BOOTLOADER)
				rc = pt_hid_output_bl_launch_app_(cd);
		} else {
			if (cd->mode == PT_MODE_BOOTLOADER)
				rc = pt_pip2_launch_app(dev,
						PT_CORE_CMD_UNPROTECTED);
		}

		if (rc) {
			mutex_unlock(&cd->firmware_class_lock);
			pt_debug(cd->dev, DL_ERROR,
				"%s: PIP launch app failed rc = %d\n",
				__func__, rc);
			goto exit_hw_reset;
		}
		mutex_unlock(&cd->firmware_class_lock);

		reset_status |= 0x01 << 1;
		msleep(20);
		if ((cd->active_dut_generation == DUT_UNKNOWN) ||
			(cd->mode != PT_MODE_OPERATIONAL))
			pt_queue_restart(cd);
		else
			pt_queue_enum(cd);
	}

	while (!(cd->startup_status & STARTUP_STATUS_COMPLETE) && time < 2000) {
		msleep(50);
		pt_debug(cd->dev, DL_INFO,
			"%s: wait %dms for 0x%04X, current enum=0x%04X\n",
			__func__, time, STARTUP_STATUS_COMPLETE,
			cd->startup_status);
		time += 50;
	}
	if (!(cd->startup_status & STARTUP_STATUS_COMPLETE)) {
		rc = -ETIME;
		goto exit_hw_reset;
	}

	pt_debug(cd->dev, DL_INFO, "%s: HW Reset complete. enum=0x%04X\n",
		__func__, cd->startup_status);
	reset_status |= 0x01 << 2;
	pt_start_wd_timer(cd);

exit_hw_reset:
	return scnprintf(buf, strlen(buf),
		"Status: %d\n"
		"Reset Status: 0x%02X\n", rc, reset_status);
}
static DEVICE_ATTR(hw_reset, 0444, pt_hw_reset_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_pip2_cmd_rsp_store
 *
 * SUMMARY: This is the store method for the raw PIP2 cmd/rsp sysfs node. Any
 *	raw PIP2 command echo'd to this node will be sent directly to the DUT.
 *	Command byte order:
 *		Byte [0]   - PIP2 command ID
 *		Byte [1-n] - PIP2 command payload
 *
 * RETURN: Size of passed in buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_pip2_cmd_rsp_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u16 actual_read_len;
	u8  input_data[PT_MAX_PIP2_MSG_SIZE + 1];
	u8  read_buf[PT_MAX_PIP2_MSG_SIZE + 1];
	u8  pip2_cmd_id = 0x00;
	u8 *pip2_cmd_data = NULL;
	int data_len = 0;
	int length;
	int rc = 0;

	/* clear shared data */
	mutex_lock(&cd->sysfs_lock);
	cd->raw_cmd_status = 0;
	cd->cmd_rsp_buf_len = 0;
	memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf));
	mutex_unlock(&cd->sysfs_lock);

	length = _pt_ic_parse_input_hex(dev, buf, size,
			input_data, PT_MAX_PIP2_MSG_SIZE);
	if (length <= 0 || length > (PT_MAX_PIP2_MSG_SIZE - 2)) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	/* Send PIP2 command if enough data was provided */
	if (length >= 1) {
		pip2_cmd_id = input_data[0];
		pip2_cmd_data = &input_data[1];
		data_len = length - 1;
		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
			pip2_cmd_id, pip2_cmd_data, data_len,
			read_buf, &actual_read_len);
		cd->raw_cmd_status = rc;
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s: PIP2 cmd 0x%02x failed rc = %d\n",
				__func__, pip2_cmd_id, rc);
			goto exit;
		} else {
			cd->cmd_rsp_buf_len = actual_read_len;
			memcpy(cd->cmd_rsp_buf, read_buf, actual_read_len);
			pt_debug(dev, DL_ERROR,
				"%s: PIP2 actual_read_len = %d\n",
				__func__, actual_read_len);
		}
	} else {
		rc = -EINVAL;
		pt_debug(dev, DL_ERROR,
			"%s: Insufficient data provided for PIP2 cmd\n",
			__func__);
	}

exit:
	if (rc)
		return rc;
	return size;
}
/*******************************************************************************
 * FUNCTION: pt_pip2_cmd_rsp_show
 *
 * SUMMARY: The show method for the raw pip2_cmd_rsp sysfs node. Any PIP2
 *	response generated after using the store method of the pip2_cmd_rsp
 *	sysfs node, are available to be read here.
 *
 * PARAMETERS:
 *      *dev  - pointer to Device structure
 *      *attr - pointer to the device attribute structure
 *      *buf  - pointer to buffer to print
 ******************************************************************************/
static ssize_t pt_pip2_cmd_rsp_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int i;
	ssize_t data_len;
	int index;

	mutex_lock(&cd->sysfs_lock);
	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
		"Status: %d\n", cd->raw_cmd_status);
	if (cd->raw_cmd_status)
		goto error;

	/* Remove the CRC from the length of the response */
	data_len = cd->cmd_rsp_buf_len - 2;

	/* Start printing from the data payload */
	for (i = PIP1_RESP_COMMAND_ID_OFFSET; i < data_len; i++)
		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
			"%02X ", cd->cmd_rsp_buf[i]);

	if (data_len >= PIP1_RESP_COMMAND_ID_OFFSET) {
		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
			"\n(%zd bytes)\n",
			data_len - PIP1_RESP_COMMAND_ID_OFFSET);
	} else {
		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
			"\n(%zd bytes)\n", 0);
	}

error:
	mutex_unlock(&cd->sysfs_lock);
	return index;
}
static DEVICE_ATTR(pip2_cmd_rsp, 0644, pt_pip2_cmd_rsp_show,
	pt_pip2_cmd_rsp_store);

/*******************************************************************************
 * FUNCTION: pt_command_store
 *
 * SUMMARY: This is the store method for the raw PIP command sysfs node. Any
 *	raw PIP command echo'd to this node will be sent directly to the DUT.
 *	TTDL will not parse the command.
 *
 * RETURN: Size of passed in buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_command_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	unsigned short crc;
	u16 actual_read_len;
	u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1];
	int length;
	int len_field;
	int rc = 0;

	mutex_lock(&cd->sysfs_lock);
	cd->cmd_rsp_buf_len = 0;
	memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf));
	mutex_unlock(&cd->sysfs_lock);

	length = _pt_ic_parse_input_hex(dev, buf, size,
			input_data, PT_MAX_PIP2_MSG_SIZE);
	if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto pt_command_store_exit;
	}

	/* PIP2 messages begin with 01 01 */
	if (length >= 2 && input_data[0] == 0x01 && input_data[1] == 0x01) {
		cd->pip2_prot_active = 1;
		/* Override next seq tag with what was sent */
		cd->pip2_cmd_tag_seq = input_data[4] & 0x0F;
		/* For PIP2 cmd if length does not include crc, add it */
		len_field = (input_data[3] << 8) | input_data[2];
		if (len_field == length && length <= 254) {
			crc = crc_ccitt_calculate(&input_data[2],
				length - 2);
			pt_debug(dev, DL_ERROR, "%s: len=%d crc=0x%02X\n",
				__func__, length, crc);
			input_data[length] = (crc & 0xFF00) >> 8;
			input_data[length + 1] = crc & 0x00FF;
			length = length + 2;
		}
	}

	/* write PIP command to log */
	pt_pr_buf(dev, DL_INFO, input_data, length, "command_buf");

	pm_runtime_get_sync(dev);

	rc = pt_hid_output_user_cmd(cd, PT_MAX_INPUT, cd->cmd_rsp_buf,
		length, input_data, &actual_read_len);
	pm_runtime_put(dev);

	mutex_lock(&cd->sysfs_lock);
	cd->raw_cmd_status = rc;
	if (rc) {
		cd->cmd_rsp_buf_len = 0;
		pt_debug(dev, DL_ERROR, "%s: Failed to send command: %s\n",
			__func__, buf);
	} else {
		cd->cmd_rsp_buf_len = actual_read_len;
	}
	cd->pip2_prot_active = 0;
	mutex_unlock(&cd->sysfs_lock);

pt_command_store_exit:
	if (rc)
		return rc;
	return size;
}
static DEVICE_ATTR(command, 0220, NULL, pt_command_store);

/*******************************************************************************
 * FUNCTION: pt_response_show
 *
 * SUMMARY: The show method for the raw PIP response sysfs node. Any PIP
 *	response generated after using the pt_command_store sysfs node, are
 *	available to be read here.
 *
 * PARAMETERS:
 *      *dev  - pointer to Device structure
 *      *attr - pointer to the device attribute structure
 *      *buf  - pointer to buffer to print
 ******************************************************************************/
static ssize_t pt_response_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int i;
	ssize_t num_read;
	int index;

	mutex_lock(&cd->sysfs_lock);
	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
		"Status: %d\n", cd->raw_cmd_status);
	if (cd->raw_cmd_status)
		goto error;

	num_read = cd->cmd_rsp_buf_len;
	for (i = 0; i < num_read; i++)
		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
			"0x%02X\n", cd->cmd_rsp_buf[i]);

	index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
		"(%zd bytes)\n", num_read);

error:
	mutex_unlock(&cd->sysfs_lock);
	return index;
}
static DEVICE_ATTR(response, 0444, pt_response_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_dut_debug_show
 *
 * SUMMARY: Show method for the dut_debug sysfs node. Shows what parameters
 *	are available for the store method.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_dut_debug_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t ret;

	ret = scnprintf(buf, strlen(buf),
		"Status: 0\n"
		"dut_debug sends the following commands to the DUT:\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		,
		PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY, "", "BL Verify APP",
		PT_DUT_DBG_HID_RESET, "", "HID Reset",
		PT_DUT_DBG_HID_SET_POWER_ON, "", "HID SET_POWER ON",
		PT_DUT_DBG_HID_SET_POWER_SLEEP, "", "HID SET_POWER SLEEP",
		PT_DUT_DBG_HID_SET_POWER_STANDBY, "", "HID SET_POWER STANDBY",
		PIP1_BL_CMD_ID_GET_INFO, "", "BL Get Info",
		PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY, "", "BL Program & Verify",
		PIP1_BL_CMD_ID_LAUNCH_APP, "", "BL Launch APP",
		PIP1_BL_CMD_ID_INITIATE_BL, "", "BL Initiate BL",
		PT_DUT_DBG_PIP_SOFT_RESET, "", "PIP Soft Reset",
		PT_DUT_DBG_RESET, "", "Toggle the TP_XRES GPIO",
		PT_DUT_DBG_PIP_NULL, "", "PIP NULL (PING)",
		PT_DUT_DBG_PIP_ENTER_BL, "", "PIP enter BL",
		PT_DUT_DBG_HID_SYSINFO, "", "HID system info",
		PT_DUT_DBG_PIP_SUSPEND_SCAN, "", "Suspend Scan",
		PT_DUT_DBG_PIP_RESUME_SCAN, "", "Resume Scan",
		PT_DUT_DBG_HID_DESC, "", "Get HID Desc"
	);

	return ret;
}
/*******************************************************************************
 * FUNCTION: pt_drv_debug_show
 *
 * SUMMARY: Show method for the drv_debug sysfs node. Shows what parameters
 *	are available for the store method.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_drv_debug_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t ret;

	ret = scnprintf(buf, strlen(buf),
		"Status: 0\n"
		"drv_debug supports the following values:\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s - %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
#ifdef TTDL_DIAGNOSTICS
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
#endif /* TTDL_DIAGNOSTICS */
		"%d %s \t- %s\n"
#ifdef TTDL_DIAGNOSTICS
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
		"%d %s \t- %s\n"
#endif /* TTDL_DIAGNOSTICS */
		,
		PT_DRV_DBG_SUSPEND, "       ", "Suspend TTDL responding to INT",
		PT_DRV_DBG_RESUME,  "       ", "Resume TTDL responding to INT",
		PT_DRV_DBG_STOP_WD, "     ", "Stop TTDL WD",
		PT_DRV_DBG_START_WD, "     ", "Start TTDL WD",
		PT_DRV_DBG_TTHE_TUNER_EXIT, "     ", "Exit TTHE Tuner Logging",
		PT_DRV_DBG_TTHE_BUF_CLEAN, "     ", "Clear TTHE Tuner buffer",
		PT_DRV_DBG_CLEAR_PARM_LIST, "     ", "Clear RAM Param list",
		PT_DRV_DBG_FORCE_BUS_READ, "     ", "Force bus read",
		PT_DRV_DBG_CLEAR_CAL_DATA, "     ", "Clear CAL Cache",
		PT_DRV_DBG_REPORT_LEVEL, "[0|1|2|3|4]", "Set TTDL Debug Level",
		PT_DRV_DBG_WATCHDOG_INTERVAL, "[n] ", "TTDL WD Interval in ms",
		PT_DRV_DBG_SHOW_TIMESTAMP, "[0|1]", "Show Timestamps"
#ifdef TTDL_DIAGNOSTICS
		, PT_DRV_DBG_SETUP_PWR, "[0|1]", "Power DUT up/down",
		PT_DRV_DBG_GET_PUT_SYNC, "[0|1]", "Get/Put Linux Sleep",
		PT_DRV_DBG_SET_TT_DATA, "[0|1]", "Display TT_DATA"
#endif /* TTDL_DIAGNOSTICS */
		, PT_DRV_DBG_SET_GENERATION, "[0|1|2]", "Set DUT generation"
#ifdef TTDL_DIAGNOSTICS
		, PT_DRV_DBG_SET_BRIDGE_MODE, "[0|1]", "On/Off Bridge Mode",
		PT_DRV_DBG_SET_I2C_ADDRESS, "[0-127]", "I2C DUT Address",
		PT_DRV_DBG_SET_FLASHLESS_DUT, "[0|1]", "Flashless DUT yes/no",
		PT_DRV_DBG_SET_FORCE_SEQ, "[8-15]", "Force PIP2 Sequence #",
		PT_DRV_DBG_BL_WITH_NO_INT, "[0|1]", "BL with no INT",
		PT_DRV_DBG_CAL_CACHE_IN_HOST, "[0|1]", "CAL Cache in host",
		PT_DRV_DBG_MULTI_CHIP, "[0|1]", "Multi Chip Support",
		PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)",
		PT_DRV_DBG_TTHE_HID_USB_FORMAT, "[0|1]",
			"TTHE_TUNER HID USB Format"
#endif /* TTDL_DIAGNOSTICS */
	);

	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_drv_debug_store
 *
 * SUMMARY: Currently the store method for both sysfs nodes: drv_debug and
 *	dut_debug. Drv_debug will contain all functionality that can be run
 *	without a DUT preset and is available anytime TTDL is running.
 *	Dut_debug requires a DUT to be available and will only be created after
 *	a DUT has been detected.
 *	This function will eventually be split into two but until the overlap
 *	has been depricated this function contains all commands that can be
 *	used for TTDL/DUT debugging status and control.
 *	All commands require at least one value to be passed in *buf with some
 *	requiring two.
 *
 * RETURN: Size of passed in buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_drv_debug_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	unsigned long value;
	int rc = 0;
	u8 return_data[8];
	static u8 wd_disabled;
	u32 input_data[3];
	int length;
#ifdef TTDL_DIAGNOSTICS
	struct i2c_client *client = to_i2c_client(dev);
	unsigned short crc = 0;
	u16 cal_size;
#endif
	input_data[0] = 0;
	input_data[1] = 0;

	/* Maximmum input is two elements */
	length = _pt_ic_parse_input(dev, buf, size,
			input_data, ARRAY_SIZE(input_data));
	if (length < 1 || length > 2) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto pt_drv_debug_store_exit;
	}
	value = input_data[0];

	if (length == 1) {
		pt_debug(dev, DL_DEBUG,
			"%s: Debug Cmd Received (id=%d)\n",
			__func__, input_data[0]);
	} else if (length == 2) {
		pt_debug(dev, DL_DEBUG,
			"%s: Debug Cmd Received (id=%d, data=%d)\n",
			__func__, input_data[0], input_data[1]);
	} else {
		pt_debug(dev, DL_DEBUG,
			"%s: Invalid arguments received\n", __func__);
		rc = -EINVAL;
		goto pt_drv_debug_store_exit;
	}

	/* Start watchdog timer command */
	if (value == PT_DRV_DBG_START_WD) {
		pt_debug(dev, DL_INFO, "%s: Cmd: Start Watchdog\n", __func__);
		wd_disabled = 0;
		cd->watchdog_force_stop = false;
		pt_start_wd_timer(cd);
		goto pt_drv_debug_store_exit;
	}

	/* Stop watchdog timer temporarily */
	pt_stop_wd_timer(cd);

	if (value == PT_DRV_DBG_STOP_WD) {
		pt_debug(dev, DL_INFO, "%s: Cmd: Stop Watchdog\n", __func__);
		wd_disabled = 1;
		cd->watchdog_force_stop = true;
		goto pt_drv_debug_store_exit;
	}

	switch (value) {
	case PT_DRV_DBG_SUSPEND:			/* 4 */
		pt_debug(dev, DL_INFO, "%s: TTDL: Core Sleep\n", __func__);
		wd_disabled = 1;
		rc = pt_core_suspend_(cd->dev);
		if (rc)
			pt_debug(dev, DL_ERROR, "%s: Suspend failed rc=%d\n",
				__func__, rc);
		else {
			pt_debug(dev, DL_INFO, "%s: Suspend succeeded\n",
				__func__);
			cd->drv_debug_suspend = true;
			pt_debug(dev, DL_INFO, "%s: Debugfs flag set:\n", __func__);
		}
		break;

	case PT_DRV_DBG_RESUME:				/* 5 */
		pt_debug(dev, DL_INFO, "%s: TTDL: Wake\n", __func__);
		rc = pt_core_resume_(cd->dev);
		if (rc)
			pt_debug(dev, DL_ERROR, "%s: Resume failed rc=%d\n",
				__func__, rc);
		else {
			pt_debug(dev, DL_INFO, "%s: Resume succeeded\n",
				__func__);
			cd->drv_debug_suspend = false;
			pt_debug(dev, DL_INFO, "%s: Debugfs flag reset:\n", __func__);
		}
		break;
	case PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY:	/* BL - 49 */
		pt_debug(dev, DL_INFO, "%s: Cmd: verify app integ\n", __func__);
		pt_hid_output_bl_verify_app_integrity(cd, &return_data[0]);
		break;
	case PT_DUT_DBG_HID_RESET:			/* 50 */
		pt_debug(dev, DL_INFO, "%s: Cmd: hid_reset\n", __func__);
		pt_hid_cmd_reset(cd);
		break;
	case PT_DUT_DBG_HID_SET_POWER_ON:		/* 53 */
		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_on\n", __func__);
		pt_hid_cmd_set_power(cd, HID_POWER_ON);
		wd_disabled = 0;
		break;
	case PT_DUT_DBG_HID_SET_POWER_SLEEP:		/* 54 */
		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_off\n",
			 __func__);
		wd_disabled = 1;
		pt_hid_cmd_set_power(cd, HID_POWER_SLEEP);
		break;
	case PT_DUT_DBG_HID_SET_POWER_STANDBY:		/* 55 */
		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_standby\n",
			 __func__);
		wd_disabled = 1;
		pt_hid_cmd_set_power(cd, HID_POWER_STANDBY);
		break;
	case PIP1_BL_CMD_ID_GET_INFO:			/* BL - 56 */
		pt_debug(dev, DL_INFO, "%s: Cmd: bl get info\n", __func__);
		pt_hid_output_bl_get_information(cd, return_data);
		break;
	case PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY:		/* BL - 57 */
		pt_debug(dev, DL_INFO, "%s: Cmd: program and verify\n",
			__func__);
		pt_hid_output_bl_program_and_verify(cd, 0, NULL);
		break;
	case PIP1_BL_CMD_ID_LAUNCH_APP:			/* BL - 59 */
		pt_debug(dev, DL_INFO, "%s: Cmd: launch app\n", __func__);
		pt_hid_output_bl_launch_app(cd);
		break;
	case PIP1_BL_CMD_ID_INITIATE_BL:		/* BL - 72 */
		pt_debug(dev, DL_INFO, "%s: Cmd: initiate bl\n", __func__);
		pt_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL);
		break;
	case PT_DUT_DBG_PIP_SOFT_RESET:			/* 97 */
		pt_debug(dev, DL_INFO, "%s: Cmd: Soft Reset\n", __func__);
		rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED);
		break;
	case PT_DUT_DBG_RESET:				/* 98 */
		pt_debug(dev, DL_INFO, "%s: Cmd: Hard Reset\n", __func__);
		rc = pt_hw_hard_reset(cd);
		break;
	case PT_DUT_DBG_PIP_NULL:			/* 100 */
		pt_debug(dev, DL_INFO, "%s: Cmd: Ping (null)\n", __func__);
		pt_pip_null(cd);
		break;
	case PT_DUT_DBG_PIP_ENTER_BL:			/* 101 */
		pt_debug(dev, DL_INFO, "%s: Cmd: start_bootloader\n", __func__);
		rc = pt_pip_start_bootloader(cd);
		if (!rc) {
			cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL;
			cd->mode = PT_MODE_BOOTLOADER;
		}
		break;
	case PT_DUT_DBG_HID_SYSINFO:			/* 102 */
		pt_debug(dev, DL_INFO, "%s: Cmd: get_sysinfo\n", __func__);
		pt_hid_output_get_sysinfo(cd);
		break;
	case PT_DUT_DBG_PIP_SUSPEND_SCAN:		/* 103 */
		pt_debug(dev, DL_INFO, "%s: Cmd: suspend_scanning\n", __func__);
		pt_pip_suspend_scanning(cd);
		break;
	case PT_DUT_DBG_PIP_RESUME_SCAN:		/* 104 */
		pt_debug(dev, DL_INFO, "%s: Cmd: resume_scanning\n", __func__);
		pt_pip_resume_scanning(cd);
		break;
#ifdef TTHE_TUNER_SUPPORT
	case PT_DRV_DBG_TTHE_TUNER_EXIT:		/* 107 */
		cd->tthe_exit = 1;
		wake_up(&cd->wait_q);
		kfree(cd->tthe_buf);
		cd->tthe_buf = NULL;
		cd->tthe_exit = 0;
		break;
	case PT_DRV_DBG_TTHE_BUF_CLEAN:			/* 108 */
		if (cd->tthe_buf)
			memset(cd->tthe_buf, 0, PT_MAX_PRBUF_SIZE);
		else
			pt_debug(dev, DL_INFO, "%s : tthe_buf not existed\n",
				__func__);
		break;
#endif
#ifdef TTDL_DIAGNOSTICS
	case PT_DUT_DBG_HID_DESC:			/* 109 */
		pt_debug(dev, DL_INFO, "%s: Cmd: get_hid_desc\n", __func__);
		pt_get_hid_descriptor(cd, &cd->hid_desc);
		break;
	case PT_DRV_DBG_CLEAR_PARM_LIST:		/* 110 */
		pt_debug(dev, DL_INFO, "%s: TTDL: Clear Parameter List\n",
			__func__);
		pt_erase_parameter_list(cd);
		break;
	case PT_DRV_DBG_FORCE_BUS_READ:			/* 111 */
		rc = pt_read_input(cd);
		if (!rc)
			pt_parse_input(cd);
		break;
	case PT_DRV_DBG_CLEAR_CAL_DATA:			/* 112 */
		rc = _pt_manage_local_cal_data(dev, PT_CAL_DATA_CLEAR,
			&cal_size, &crc);
		if (rc)
			pt_debug(dev, DL_ERROR,
				"%s: CAL Data clear failed rc=%d\n",
				__func__, rc);
		else
			pt_debug(dev, DL_INFO,
				"%s: CAL Cleared, Chip ID=0x%04X size=%d\n",
				__func__, crc, size);
		break;

	case PT_DRV_DBG_REPORT_LEVEL:			/* 200 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] >= 0 && input_data[1] < DL_MAX) {
			cd->debug_level = input_data[1];
			pt_debug(dev, DL_INFO, "%s: Set debug_level: %d\n",
				__func__, cd->debug_level);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid debug_level: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
#endif
	case PT_DRV_DBG_WATCHDOG_INTERVAL:		/* 201 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] > 100) {
			cd->watchdog_interval = input_data[1];
			pt_debug(dev, DL_INFO,
				"%s: Set watchdog_ interval to: %d\n",
				__func__, cd->watchdog_interval);
			pt_start_wd_timer(cd);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR,
				"%s: Invalid watchdog interval: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
#ifdef TTDL_DIAGNOSTICS
	case PT_DRV_DBG_SHOW_TIMESTAMP:			/* 202 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->show_timestamp = 0;
			pt_debug(dev, DL_INFO, "%s: Disable show_timestamp\n",
				__func__);
		} else if (input_data[1] == 1) {
			cd->show_timestamp = 1;
			pt_debug(dev, DL_INFO, "%s: Enable show_timestamp\n",
				__func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR,
				"%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SETUP_PWR:			/* 205 */
		if (input_data[1] == 0) {
			cd->cpdata->setup_power(cd->cpdata,
				PT_MT_POWER_OFF, cd->dev);
			pt_debug(dev, DL_INFO,
				"%s: Initiate Power Off\n", __func__);
		} else if (input_data[1] == 1) {
			cd->cpdata->setup_power(cd->cpdata,
				PT_MT_POWER_ON, cd->dev);
			pt_debug(dev, DL_INFO,
				"%s: Initiate Power On\n", __func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR,
				"%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		break;
	case PT_DRV_DBG_GET_PUT_SYNC:			/* 206 */
		if (input_data[1] == 0) {
			pm_runtime_put(dev);
			pt_debug(dev, DL_ERROR,
				"%s: Force call pm_runtime_put()\n", __func__);
		} else if (input_data[1] == 1) {
			pm_runtime_get_sync(dev);
			pt_debug(dev, DL_ERROR,
				"%s: Force call pm_runtime_get_sync()\n",
				__func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR,
				"%s: WARNING: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		break;
	case PT_DRV_DBG_SET_TT_DATA:			/* 208 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->show_tt_data = false;
			pt_debug(dev, DL_INFO,
				"%s: Disable TT_DATA\n", __func__);
		} else if (input_data[1] == 1) {
			cd->show_tt_data = true;
			pt_debug(dev, DL_INFO,
				"%s: Enable TT_DATA\n", __func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR,
				"%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SET_GENERATION:			/* 210 */
		if (input_data[1] == cd->active_dut_generation) {
			mutex_lock(&cd->system_lock);
			cd->set_dut_generation = true;
			mutex_unlock(&(cd->system_lock));
		} else {
			mutex_lock(&cd->system_lock);
			if (input_data[1] == 0) {
				cd->active_dut_generation = DUT_UNKNOWN;
				cd->set_dut_generation = false;
			} else if (input_data[1] == 1) {
				cd->active_dut_generation = DUT_PIP1_ONLY;
				cd->set_dut_generation = true;
			} else if (input_data[1] == 2) {
				cd->active_dut_generation = DUT_PIP2_CAPABLE;
				cd->set_dut_generation = true;
			} else {
				rc = -EINVAL;
				pt_debug(dev, DL_ERROR,
					"%s: Invalid parameter: %d\n",
					__func__, input_data[1]);
				break;
			}
			cd->startup_status = STARTUP_STATUS_START;
			pt_debug(cd->dev, DL_DEBUG,
				"%s: Startup Status Reset\n", __func__);
			mutex_unlock(&(cd->system_lock));

			pt_debug(dev, DL_INFO,
				"%s: Active DUT Generation Set to: %d\n",
				__func__, cd->active_dut_generation);

			/* Changing DUT generations full restart needed */
			pt_queue_restart(cd);
		}
		break;
	case PT_DRV_DBG_SET_BRIDGE_MODE:		/* 211 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->bridge_mode = false;
			pt_debug(dev, DL_INFO,
				"%s: Disable Bridge Mode\n", __func__);
		} else if (input_data[1] == 1) {
			cd->bridge_mode = true;
			pt_debug(dev, DL_INFO,
				"%s: Enable Bridge Mode\n", __func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SET_I2C_ADDRESS:		/* 212 */
		mutex_lock(&cd->system_lock);
		/* Only a 7bit address is valid */
		if (input_data[1] >= 0 && input_data[1] <= 0x7F) {
			client->addr = input_data[1];
			pt_debug(dev, DL_INFO,
				"%s: Set I2C Address: 0x%2X\n",
				__func__, client->addr);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid I2C Address %d\n",
				__func__, input_data[1]);
			client->addr = 0x24;
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SET_FLASHLESS_DUT:		/* 213 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->flashless_dut = 0;
			cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
			pt_debug(dev, DL_INFO, "%s: Disable FLAHLESS DUT\n",
			__func__);
		} else if (input_data[1] == 1) {
			cd->flashless_dut = 1;
			cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
			pt_debug(dev, DL_INFO, "%s: Enable FLAHLESS DUT\n",
			__func__);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SET_FORCE_SEQ:			/* 214 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] >= 0x8 && input_data[1] <= 0xF) {
			cd->force_pip2_seq = input_data[1];
			cd->pip2_cmd_tag_seq = input_data[1];
			pt_debug(dev, DL_INFO,
				"%s: Force PIP2 Seq to: 0x%02X\n",
				__func__, input_data[1]);
		} else {
			cd->force_pip2_seq = 0;
			pt_debug(dev, DL_INFO,
				"%s: Clear Forced PIP2 Seq\n", __func__);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_BL_WITH_NO_INT:			/* 215 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->bl_with_no_int = 0;
			pt_debug(dev, DL_INFO, "%s: BL using IRQ\n", __func__);
		} else if (input_data[1] == 1) {
			cd->bl_with_no_int = 1;
			pt_debug(dev, DL_INFO, "%s: BL using Polling\n",
				__func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_CAL_CACHE_IN_HOST:		/* 216 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->cal_cache_in_host = PT_FEATURE_DISABLE;
			pt_debug(dev, DL_INFO,
				"%s: Disable Calibration cache in host\n",
				__func__);
		} else if (input_data[1] == 1) {
			cd->cal_cache_in_host = PT_FEATURE_ENABLE;
			pt_debug(dev, DL_INFO,
				"%s: Enable Calibration cache in host\n",
				__func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_MULTI_CHIP:			/* 217 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->multi_chip = PT_FEATURE_DISABLE;
			cd->ttdl_bist_select = 0x07;
			pt_debug(dev, DL_INFO,
				"%s: Disable Multi-chip support\n", __func__);
		} else if (input_data[1] == 1) {
			cd->multi_chip = PT_FEATURE_ENABLE;
			cd->ttdl_bist_select = 0x3F;
			pt_debug(dev, DL_INFO,
				"%s: Enable Multi-chip support\n", __func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_SET_PANEL_ID_TYPE:		/* 218 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] <= 0x07) {
			cd->panel_id_support = input_data[1];
			pt_debug(dev, DL_INFO,
				"%s: Set panel_id_support to %d\n",
				__func__, cd->panel_id_support);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_PIP_TIMEOUT:			/* 219 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] >= 100 && input_data[1] <= 7000) {
			/*
			 * The timeout is changed for some cases so the
			 * pip_cmd_timeout_default is used to retore back to
			 * what the user requested as the new timeout.
			 */
			cd->pip_cmd_timeout_default = input_data[1];
			cd->pip_cmd_timeout = input_data[1];
			pt_debug(dev, DL_INFO,
				"%s: PIP Timeout = %d\n", __func__,
				cd->pip_cmd_timeout_default);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
	case PT_DRV_DBG_TTHE_HID_USB_FORMAT:		/* 220 */
		mutex_lock(&cd->system_lock);
		if (input_data[1] == 0) {
			cd->tthe_hid_usb_format = PT_FEATURE_DISABLE;
			pt_debug(dev, DL_INFO,
				"%s: Disable tthe_tuner HID-USB format\n",
				__func__);
		} else if (input_data[1] == 1) {
			cd->tthe_hid_usb_format = PT_FEATURE_ENABLE;
			pt_debug(dev, DL_INFO,
				"%s: Enable tthe_tuner HID-USB format\n",
				__func__);
		} else {
			rc = -EINVAL;
			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
				__func__, input_data[1]);
		}
		mutex_unlock(&(cd->system_lock));
		break;
#endif /* TTDL_DIAGNOSTICS */
	default:
		rc = -EINVAL;
		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
	}

	/* Enable watchdog timer */
	if (!wd_disabled)
		pt_start_wd_timer(cd);

pt_drv_debug_store_exit:
	if (rc)
		return rc;
	return size;
}
static DEVICE_ATTR(drv_debug, 0644, pt_drv_debug_show,
	pt_drv_debug_store);

/*******************************************************************************
 * FUNCTION: pt_sleep_status_show
 *
 * SUMMARY: Show method for the sleep_status sysfs node that will show the
 *	sleep status as either ON or OFF
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_sleep_status_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;

	mutex_lock(&cd->system_lock);
	if (cd->sleep_state == SS_SLEEP_ON)
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "off\n");
	else
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "on\n");
	mutex_unlock(&cd->system_lock);

	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_panel_id_show
 *
 * SUMMARY: Show method for the panel_id sysfs node that will show the
 *	detected panel ID from the DUT
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_panel_id_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;
	u8 pid = PANEL_ID_NOT_ENABLED;
	int rc = 0;

	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
		/*
		 * The DUT should report the same panel ID from both the BL and
		 * the FW unless the panel_id feature is set to only
		 * PT_PANEL_ID_BY_SYS_INFO, in which case the BL is not able
		 * to retrieve the panel_id.
		 */
		if (cd->mode == PT_MODE_BOOTLOADER) {
			/*
			 * Return the stored value if PT_PANEL_ID_BY_BL
			 * is not supported while other feature exits.
			 */
			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
				rc = pt_hid_output_bl_get_panel_id_(cd, &pid);
				if (rc) {
					pt_debug(dev, DL_ERROR, "%s: %s %s\n",
						 "Failed to retrieve Panel ID. ",
						 "Using cached value\n",
						 __func__);
				}
			}
		} else if (cd->mode == PT_MODE_OPERATIONAL) {
			if (cd->panel_id_support &
			    (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
				/* For all systems sysinfo has the panel_id */
				rc = pt_hid_output_get_sysinfo(cd);
				if (!rc)
					pid =
					 cd->sysinfo.sensing_conf_data.panel_id;
				pt_debug(dev, DL_ERROR,
					 "%s: Gen6 FW mode rc=%d PID=0x%02X\n",
					 __func__, rc, pid);
			}
		} else {
			pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n",
				__func__);
			rc = -EPERM;
		}
	} else if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
		if (cd->mode == PT_MODE_BOOTLOADER) {
			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
				rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid);
				if (rc) {
					pt_debug(dev, DL_ERROR,
						"%s: BL get panel ID failed rc=%d\n",
						__func__, rc);
				}
			}
		} else if (cd->mode == PT_MODE_OPERATIONAL) {
			if (cd->panel_id_support &
			    (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
				rc = pt_hid_output_get_sysinfo(cd);
				if (!rc)
					pid =
					 cd->sysinfo.sensing_conf_data.panel_id;
				pt_debug(dev, DL_ERROR,
					 "%s: TT/TC FW mode rc=%d PID=0x%02X\n",
					 __func__, rc, pid);
			}
		} else {
			pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n",
				__func__);
			rc = -EPERM;
		}
	} else {
		pt_debug(dev, DL_ERROR, "%s: Dut generation is unknown\n",
				__func__);
		rc = -EPERM;
	}

	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n0x%02X\n",
			rc, pid);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_get_param_store
 *
 * SUMMARY: Store method for the get_param sysfs node. Stores what parameter
 *	ID to retrieve with the show method.
 *
 * NOTE: This sysfs node is only available after a DUT has been enumerated
 *
 * RETURN: Size of passed in buffer if successful
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_get_param_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	cd->get_param_id = input_data[0];
	mutex_unlock(&(cd->system_lock));

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_get_param_show
 *
 * SUMMARY: Show method for the get_param sysfs node. Retrieves the
 *	parameter data from the DUT based on the ID stored in the core
 *	data variable "get_param_id". If the ID is invalid, the DUT cannot
 *	communicate or some other error occures, an error status is returned
 *	with no value following.
 *	Output is in the form:
 *	   Status: x
 *	   0xyyyyyyyy
 *	The 32bit data will only follow the status code if the status == 0
 *
 * NOTE: This sysfs node is only available after a DUT has been enumerated
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_get_param_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct  pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret = 0;
	int     status;
	u32     value = 0;

	status = pt_pip_get_param(cd, cd->get_param_id, &value);

	if (status) {
		pt_debug(dev, DL_ERROR, "%s: %s Failed, status = %d\n",
			__func__, "pt_get_param", status);
		ret = scnprintf(buf, strlen(buf),
			"%s %d\n",
			"Status:", status);
	} else {
		pt_debug(dev, DL_DEBUG, "%s: Param [%d] = 0x%04X\n",
			__func__, cd->get_param_id, value);
		ret = scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"0x%04X\n",
			status, value);
	}
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_ttdl_restart_show
 *
 * SUMMARY: Show method for ttdl_restart sysfs node. This node releases all
 *	probed modules, calls startup() and then re-probes modules.
 *
 * RETURN: size of data written to sysfs node
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes structure
 *	*buf  - pointer to print output buffer
 ******************************************************************************/
static ssize_t pt_ttdl_restart_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	int t;
	int rc = 0;

	mutex_lock(&cd->system_lock);
	cd->startup_state = STARTUP_NONE;
	mutex_unlock(&(cd->system_lock));
	/* ensure no left over exclusive access is still locked */
	release_exclusive(cd, cd->dev);

	pt_queue_restart(cd);

	t = wait_event_timeout(cd->wait_q,
		(cd->startup_status >= STARTUP_STATUS_COMPLETE),
		msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT));
	if (IS_TMO(t)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: TMO waiting for FW sentinel\n", __func__);
		rc = -ETIME;
	}

	return scnprintf(buf, strlen(buf),
		"Status: %d\n"
		"Enum Status: 0x%04X\n", rc, cd->startup_status);
}
static DEVICE_ATTR(ttdl_restart, 0444, pt_ttdl_restart_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_pip2_gpio_read_show
 *
 * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and prints the
 *	contents of the response to the passed in output buffer.
 *
 * RETURN: size of data written to sysfs node
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes structure
 *	*buf  - pointer to print output buffer
 ******************************************************************************/
static ssize_t pt_pip2_gpio_read_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 status = 0;
	u32 gpio_value = 0;
	int rc = 0;

	/* This functionality is only available in the BL */
	if (cd->mode == PT_MODE_BOOTLOADER)
		rc = pt_pip2_read_gpio(dev, &status, &gpio_value);
	else
		rc = -EPERM;

	if (!rc) {
		if (status == 0)
			return scnprintf(buf, PT_MAX_PRBUF_SIZE,
					"Status: %d\n"
					"DUT GPIO Reg: 0x%08X\n",
					rc, gpio_value);
		else
			return scnprintf(buf, PT_MAX_PRBUF_SIZE,
					"Status: %d\n"
					"DUT GPIO Reg: n/a\n",
					status);
	} else
		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
				"Status: %d\n"
				"DUT GPIO Reg: n/a\n",
				rc);
}

/*******************************************************************************
 * FUNCTION: pt_device_exit
 *
 * SUMMARY: Remove functon for the I2C module
 *
 * PARAMETERS:
 *      *client - pointer to i2c client structure
 ******************************************************************************/
#ifdef PT_AMBIENT_MODE
static int pt_device_exit(struct i2c_client *client)
{

		struct pt_core_data *cd = i2c_get_clientdata(client);
		struct device *dev = cd->dev;

		void *glink_pt_send_msg;
		int glink_touch_enter = TOUCH_ENTER;

		pt_debug(dev, DL_INFO,"%s: Start pt_device_exit\n", __func__);

		glink_pt_send_msg = &glink_touch_enter;
		pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %0x\n", glink_pt_send_msg);
		glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE);

		if (active_panel)
			panel_event_notifier_unregister(cd->entry);
		pt_core_state = STATE_SUSPEND;

		pm_runtime_suspend(dev);
		pm_runtime_disable(dev);

		pt_stop_wd_timer(cd);
		call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
		cancel_work_sync(&cd->ttdl_restart_work);
		cancel_work_sync(&cd->enum_work);
		cancel_work_sync(&cd->resume_offload_work);
		cancel_work_sync(&cd->suspend_offload_work);
		cancel_work_sync(&cd->resume_work);
		cancel_work_sync(&cd->suspend_work);

		pt_stop_wd_timer(cd);
		device_init_wakeup(dev, 0);
		disable_irq_nosync(cd->irq);

		if (cd->cpdata->setup_irq)
			cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);

		if (cd->cpdata->init)
			cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);

		if (cd->cpdata->setup_power)
			cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);

		pt_debug(dev, DL_INFO,"%s: End pt_device_exit \n", __func__);
		return 0;
}
#endif

/*******************************************************************************
 * FUNCTION: pt_touch_offload_store
 *
 * SUMMARY: The store method for the touch_offload sysfs node that allows the TTDL
 * 			 to be enabled/disabled.
 *
 * RETURN: Size of passed in buffer
 *
 * PARAMETERS:
 * 			*dev  - pointer to device structure
 *			*attr - pointer to device attributes
 *			*buf  - pointer to buffer that hold the command parameters
 *			size - size of buf
 ******************************************************************************/
static ssize_t pt_touch_offload_store(struct device *dev,
			struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct i2c_client *client = to_i2c_client(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data));
	if (length != 1) {
			pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__);
			rc = -EINVAL;
			goto exit;
	}

	switch (input_data[0]) {
	case 0:
		pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload OFF\n", __func__);
		cd->touch_offload = true;
		rc = pt_device_exit(client);
		if (rc)
			pt_debug(dev, DL_ERROR, "%s: Power off error detected rc=%d\n",
					__func__, rc);
		else {
			cd->touch_offload = true;
			pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE EXIT flag set:\n",
				__func__);
		}
	break;

	case 1:
		pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload ON\n", __func__);
		rc = pt_device_entry(&client->dev, client->irq, PT_DATA_SIZE);
		if (rc)
			pt_debug(dev, DL_ERROR, "%s: Power on error detected rc=%d\n",
				__func__, rc);
		else {
			cd->touch_offload = false;
			pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE ENTRY flag set:\n",
				__func__);
		}
	break;

	default:
		rc = -EINVAL;
		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
	}

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_touch_offload_show
 *
 * SUMMARY: The show method for the touch_offload sysfs node that allows the TTDL
 * 			 to verify touch offload enable or disabled.
 *
 * RETURN: size of data written to sysfs node
 *
 * PARAMETERS:
 * 			*dev  - pointer to device structure
 *			*attr - pointer to device attributes
 *			*buf  - pointer to print output buffer
 ******************************************************************************/
static ssize_t pt_touch_offload_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
			"Touch offload   : %s\n",
			(cd->touch_offload)? "Enabled" : "Disabled");
}

/*******************************************************************************
 * FUNCTION: pt_pip2_version_show
 *
 * SUMMARY: Sends a PIP2 VERSION command to the DUT and prints the
 *	contents of the response to the passed in output buffer.
 *
 * RETURN: size of data written to sysfs node
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes structure
 *	*buf  - pointer to print output buffer
 ******************************************************************************/
static ssize_t pt_pip2_version_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	int rc = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;

	rc = pt_pip2_get_version(cd);
	if (!rc) {
		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
			"PIP VERSION   : %02X.%02X\n"
			"BL VERSION    : %02X.%02X\n"
			"FW VERSION    : %02X.%02X\n"
			"SILICON ID    : %04X.%04X\n"
			"UID           : 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
			ttdata->pip_ver_major, ttdata->pip_ver_minor,
			ttdata->bl_ver_major,  ttdata->bl_ver_minor,
			ttdata->fw_ver_major,  ttdata->fw_ver_minor,
			ttdata->chip_id,       ttdata->chip_rev,
			ttdata->uid[0], ttdata->uid[1],
			ttdata->uid[2], ttdata->uid[3],
			ttdata->uid[4], ttdata->uid[5],
			ttdata->uid[6], ttdata->uid[7],
			ttdata->uid[8], ttdata->uid[9],
			ttdata->uid[10], ttdata->uid[11]);
	} else {
		pt_debug(dev, DL_ERROR,
			"%s: Failed to retriev PIP2 VERSION data\n", __func__);

		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
			"PIP VERSION   : -\n"
			"BL VERSION    : -\n"
			"FW VERSION    : -\n"
			"SILICON ID    : -\n"
			"UID           : -\n");
	}
}

#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
 * FUNCTION: pt_ttdl_status_show
 *
 * SUMMARY: Show method for the ttdl_status sysfs node. Displays TTDL internal
 *	variable states and GPIO levels. Additional information printed when
 *	TTDL_DIAGNOSTICS is enabled.
 *
 *	NOTE: All counters will be reset to 0 when this function is called.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_ttdl_status_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_platform_data *pdata = dev_get_platdata(dev);
	struct i2c_client *client = to_i2c_client(dev);
	ssize_t ret;
	u16 cal_size = 0;
	unsigned short crc = 0;

	if (cd->cal_cache_in_host)
		_pt_manage_local_cal_data(dev,
			PT_CAL_DATA_INFO, &cal_size, &crc);
	ret = scnprintf(buf, strlen(buf),
		"%s: 0x%04X\n"
		"%s: %d\n"
		"%s: %s\n"
		"%s: %s %s\n"
		"%s: %s\n"
		"%s: 0x%02X\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %d\n"
#ifdef TTDL_DIAGNOSTICS
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %d\n"
		"%s: 0x%04X\n"
		"%s: %s\n"
#endif /* TTDL_DIAGNOSTICS */
		,
		"Startup Status            ", cd->startup_status,
		"TTDL Debug Level          ", cd->debug_level,
		"Mode                      ",
			cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ?
			"Operational" : "BL") : "Unknown",
		"DUT Generation            ",
			cd->active_dut_generation ?
			(cd->active_dut_generation == DUT_PIP2_CAPABLE ?
			"PT TC/TT" : "Gen5/6") : "Unknown",
			cd->active_dut_generation ?
			(cd->set_dut_generation == true ?
			"(Set)" : "(Detected)") : "",
		"HW Detected               ",
			cd->hw_detected ? "True" : "False",
		"I2C Address               ",
			cd->bus_ops->bustype == BUS_I2C ? client->addr : 0,
		"Active Bus Module         ",
			cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI",
		"Flashless Mode            ",
			cd->flashless_dut == 1 ? "Yes" : "No",
		"GPIO state - IRQ          ",
			cd->cpdata->irq_stat ?
			(cd->cpdata->irq_stat(cd->cpdata, dev) ?
			"High" : "Low") : "not defined",
		"GPIO state - TP_XRES      ",
			pdata->core_pdata->rst_gpio ?
			(gpio_get_value(pdata->core_pdata->rst_gpio) ?
			"High" : "Low") : "not defined",
		"RAM Parm restore list     ", pt_count_parameter_list(cd),
		"Startup Retry Count       ", cd->startup_retry_count,
		"WD - Manual Force Stop    ",
			cd->watchdog_force_stop ? "True" : "False",
		"WD - Enabled              ",
			cd->watchdog_enabled ? "True" : "False",
		"WD - Interval (ms)        ", cd->watchdog_interval
#ifdef TTDL_DIAGNOSTICS
		, "WD - Triggered Count      ", cd->watchdog_count,
		"WD - IRQ Stuck low count  ", cd->watchdog_irq_stuck_count,
		"WD - Device Access Errors ", cd->watchdog_failed_access_count,
		"WD - XRES Count           ", cd->wd_xres_count,
		"IRQ Triggered Count       ", cd->irq_count,
		"BL Packet Retry Count     ", cd->bl_retry_packet_count,
		"PIP2 CRC Error Count      ", cd->pip2_crc_error_count,
		"Bus Transmit Error Count  ", cd->bus_transmit_error_count,
		"File Erase Timeout Count  ", cd->file_erase_timeout_count,
		"Error GPIO trigger type   ", cd->err_gpio_type,
		"Exclusive Access Lock     ", cd->exclusive_dev ? "Set":"Free",
		"Suppress No-Flash Auto BL ",
			cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ?
			"Yes" : "No",
		"Calibration Cache on host ",
			cd->cal_cache_in_host == PT_FEATURE_ENABLE ?
			"Yes" : "No",
		"Calibration Cache size    ", cal_size,
		"Calibration Cache chip ID ", crc,
		"Multi-Chip Support        ",
			cd->multi_chip == PT_FEATURE_ENABLE ? "Yes" : "No"
#endif /* TTDL_DIAGNOSTICS */
	);

#ifdef TTDL_DIAGNOSTICS
	/* Reset all diagnostic counters */
	cd->watchdog_count               = 0;
	cd->watchdog_irq_stuck_count     = 0;
	cd->watchdog_failed_access_count = 0;
	cd->wd_xres_count                = 0;
	cd->irq_count                    = 0;
	cd->bl_retry_packet_count        = 0;
	cd->pip2_crc_error_count         = 0;
	cd->bus_transmit_error_count     = 0;
#endif

	return ret;
}
static DEVICE_ATTR(ttdl_status,  0444, pt_ttdl_status_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_pip2_enter_bl_show
 *
 * SUMMARY: Show method for the pip2_enter_bl sysfs node that will force
 *	the DUT into the BL and show the success or failure of entering the BL
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_pip2_enter_bl_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t ret = 0;
	int rc = 0;
	int result = 0;
	u8 mode = PT_MODE_UNKNOWN;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	/* Turn off the TTDL WD before enter bootloader */
	pt_stop_wd_timer(cd);

	/* Ensure NO enumeration work is queued or will be queued */
	cancel_work_sync(&cd->enum_work);
	mutex_lock(&cd->system_lock);
	cd->bridge_mode = true;
	mutex_unlock(&cd->system_lock);

	/* set mode to operational to avoid any extra PIP traffic */
	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
	switch (result) {
	case PT_ENTER_BL_PASS:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
			"Status: %d\nEntered BL\n", PT_ENTER_BL_PASS);
		break;
	case PT_ENTER_BL_ERROR:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
			rc,
			"  Unknown Error");
		break;
	case PT_ENTER_BL_RESET_FAIL:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
			rc,
			"  Soft Reset Failed");
		break;
	case PT_ENTER_BL_HID_START_BL_FAIL:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
			rc,
			"  PIP Start BL Cmd Failed");
		break;
	case PT_ENTER_BL_CONFIRM_FAIL:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
			rc,
			"  Error confirming DUT entered BL");
		break;
	default:
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
			rc, "  Unknown Error");
		break;
	};

	/* Allow enumeration work to be queued again */
	cd->bridge_mode = false;

	return ret;
}
static DEVICE_ATTR(pip2_enter_bl,  0444, pt_pip2_enter_bl_show, NULL);

/*******************************************************************************
 * FUNCTION: pt_pip2_exit_bl_show
 *
 * SUMMARY: Show method for the pip2_exit_bl sysfs node that will attempt to
 *	launch the APP and put the DUT Application mode
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_pip2_exit_bl_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret = 0;
	int rc = 0;
	u8 status_str[PT_STATUS_STR_LEN];

	rc = pt_pip2_exit_bl_(cd, status_str, PT_STATUS_STR_LEN);
	/*
	 * Perform enum if startup_status doesn't reach to
	 * STARTUP_STATUS_FW_OUT_OF_BOOT.
	 */
	if (!rc && (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT))) {
		rc = pt_enum_with_dut(cd, false, &cd->startup_status);
		if (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT)) {
			strlcpy(status_str,
			       "Already in APP mode - FW stuck in Boot mode", sizeof(status_str));
		}
	}
	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
		rc, status_str);
	return ret;
}
static DEVICE_ATTR(pip2_exit_bl,  0444, pt_pip2_exit_bl_show, NULL);
#endif

#ifdef EASYWAKE_TSG6
/*******************************************************************************
 * FUNCTION: pt_easy_wakeup_gesture_show
 *
 * SUMMARY: Show method for the easy_wakeup_gesture sysfs node that will show
 *	current easy wakeup gesture
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_easy_wakeup_gesture_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;

	mutex_lock(&cd->system_lock);
	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "0x%02X\n",
			cd->easy_wakeup_gesture);
	mutex_unlock(&cd->system_lock);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_easy_wakeup_gesture_store
 *
 * SUMMARY: The store method for the easy_wakeup_gesture sysfs node that
 *	allows the wake gesture to be set to a custom value.
 *
 * RETURN: Size of passed in buffer is success
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_easy_wakeup_gesture_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	pt_debug(dev, DL_INFO, "%s: features.easywake = 0x%02X\n",
		__func__, cd->features.easywake);

	if (!cd->features.easywake || input_data[0] > 0xFF) {
		rc = -EINVAL;
		goto exit;
	}


	pm_runtime_get_sync(dev);
	mutex_lock(&cd->system_lock);

	if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) {
		cd->easy_wakeup_gesture = (u8)input_data[0];
		pt_debug(dev, DL_INFO,
			"%s: Updated easy_wakeup_gesture = 0x%02X\n",
			__func__, cd->easy_wakeup_gesture);
	} else
		rc = -ENODEV;

	mutex_unlock(&cd->system_lock);
	pm_runtime_put(dev);

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_easy_wakeup_gesture_id_show
 *
 * SUMMARY: Show method for the easy_wakeup_gesture_id sysfs node that will
 *	show the TSG6 easywake gesture ID
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_easy_wakeup_gesture_id_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;

	mutex_lock(&cd->system_lock);
	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n0x%02X\n",
			cd->gesture_id);
	mutex_unlock(&cd->system_lock);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_easy_wakeup_gesture_data_show
 *
 * SUMMARY: Show method for the easy_wakeup_gesture_data sysfs node that will
 *	show the TSG6 easywake gesture data in the following format:
 *	x1(LSB),x1(MSB), y1(LSB),y1(MSB), x2(LSB),x2(MSB), y2(LSB),y2(MSB),...
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_easy_wakeup_gesture_data_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret = 0;
	int i;

	mutex_lock(&cd->system_lock);
	ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0);
	for (i = 0; i < cd->gesture_data_length; i++)
		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
				"0x%02X\n", cd->gesture_data[i]);

	ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
			"(%d bytes)\n", cd->gesture_data_length);

	mutex_unlock(&cd->system_lock);
	return ret;
}
#endif /* EASYWAKE_TSG6 */

#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
 * FUNCTION: pt_err_gpio_show
 *
 * SUMMARY: Show method for the err_gpio sysfs node that will show if
 *	setting up the gpio was successful
 *
 * RETURN: Char buffer with printed GPIO creation state
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_err_gpio_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	return scnprintf(buf, strlen(buf), "Status: 0\n"
		"Err GPIO (%d)     : %s\n"
		"Err GPIO trig type: %d\n",
		cd->err_gpio,
		(cd->err_gpio ? (gpio_get_value(cd->err_gpio) ?
		"HIGH" : "low") : "not defined"),
		cd->err_gpio_type);
}

/*******************************************************************************
 * FUNCTION: pt_err_gpio_store
 *
 * SUMMARY: The store method for the err_gpio sysfs node that allows any
 *	available host GPIO to be used to trigger when TTDL detects a PIP
 *	command/response timeout.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_err_gpio_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	unsigned long gpio;
	int rc = 0;
	u32 input_data[3];
	int length;
	u8 err_type;

	input_data[0] = 0;
	input_data[1] = 0;
	/* Maximmum input is two elements */
	length = _pt_ic_parse_input(dev, buf, size,
			input_data, ARRAY_SIZE(input_data));
	if (length < 1 || length > 2) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	gpio = input_data[0];
	err_type = (u8)input_data[1];
	if (err_type < 0 || err_type > PT_ERR_GPIO_MAX_TYPE) {
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	gpio_free(gpio);
	rc = gpio_request(gpio, NULL);
	if (rc) {
		pt_debug(dev, DL_ERROR, "error requesting gpio %lu\n", gpio);
		rc = -ENODEV;
	} else {
		cd->err_gpio = gpio;
		cd->err_gpio_type = err_type;
		gpio_direction_output(gpio, 0);
	}
	mutex_unlock(&cd->system_lock);

exit:
	if (rc)
		return rc;
	return size;
}
static DEVICE_ATTR(err_gpio, 0644, pt_err_gpio_show,
	pt_err_gpio_store);

/*******************************************************************************
 * FUNCTION: pt_drv_irq_show
 *
 * SUMMARY: Show method for the drv_irq sysfs node that will show if the
 *	TTDL interrupt is enabled/disabled
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_drv_irq_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret = 0;

	mutex_lock(&cd->system_lock);
	ret += scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0);
	if (cd->irq_enabled)
		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
			"Driver interrupt: ENABLED\n");
	else
		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
			"Driver interrupt: DISABLED\n");
	mutex_unlock(&cd->system_lock);

	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_drv_irq_store
 *
 * SUMMARY: The store method for the drv_irq sysfs node that allows the TTDL
 *	IRQ to be enabled/disabled.
 *
 * RETURN: Size of passed in buffer
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_drv_irq_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	switch (input_data[0]) {
	case 0:
		if (cd->irq_enabled) {
			cd->irq_enabled = false;
			/* Disable IRQ has no return value to check */
			disable_irq_nosync(cd->irq);
			pt_debug(dev, DL_INFO,
				"%s: Driver IRQ now disabled\n",
				__func__);
		} else
			pt_debug(dev, DL_INFO,
				"%s: Driver IRQ already disabled\n",
				__func__);
		break;

	case 1:
		if (cd->irq_enabled == false) {
			cd->irq_enabled = true;
			enable_irq(cd->irq);
			pt_debug(dev, DL_INFO,
				"%s: Driver IRQ now enabled\n",
				__func__);
		} else
			pt_debug(dev, DL_INFO,
				"%s: Driver IRQ already enabled\n",
				__func__);
		break;

	default:
		rc = -EINVAL;
		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
	}
	mutex_unlock(&(cd->system_lock));

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_pip2_bin_hdr_show
 *
 * SUMMARY: Show method for the pip2_bin_hdr sysfs node that will read
 *	the bin file header from flash and show each field
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_pip2_bin_hdr_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t ret = 0;
	struct pt_bin_file_hdr hdr = {0};
	int rc;

	rc = _pt_request_pip2_bin_hdr(dev, &hdr);

	ret = scnprintf(buf, strlen(buf),
		"%s: %d\n"
		"%s: %d\n"
		"%s: 0x%04X\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: 0x%08X\n"
		"%s: 0x%04X\n"
		"%s: 0x%04X\n"
		"%s: %d\n"
		"%s: %d\n",
		"Status", rc,
		"Header Length  ", hdr.length,
		"TTPID          ", hdr.ttpid,
		"FW Major Ver   ", hdr.fw_major,
		"FW Minor Ver   ", hdr.fw_minor,
		"FW Rev Control ", hdr.fw_rev_ctrl,
		"FW CRC         ", hdr.fw_crc,
		"Silicon Rev    ", hdr.si_rev,
		"Silicon ID     ", hdr.si_id,
		"Config Ver     ", hdr.config_ver,
		"HEX File Size  ", hdr.hex_file_size
	);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_platform_data_show
 *
 * SUMMARY: Show method for the platform_data sysfs node that will show the
 *	active platform data including: GPIOs, Vendor and Product IDs,
 *	Virtual Key coordinates, Core/MT/Loader flags, Level trigger delay,
 *	HID registers, and Easy wake gesture
 *
 * RETURN: Size of printed data
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_platform_data_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_platform_data *pdata = dev_get_platdata(dev);
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;

	ret = scnprintf(buf, strlen(buf),
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %d\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: %d\n",
		"Status", 0,
		"Interrupt GPIO           ", pdata->core_pdata->irq_gpio,
		"Interrupt GPIO Value     ",
		pdata->core_pdata->irq_gpio ?
			gpio_get_value(pdata->core_pdata->irq_gpio) : 0,
		"Reset GPIO               ", pdata->core_pdata->rst_gpio,
		"Reset GPIO Value         ",
		pdata->core_pdata->rst_gpio ?
			gpio_get_value(pdata->core_pdata->rst_gpio) : 0,
		"DDI Reset GPIO           ", pdata->core_pdata->ddi_rst_gpio,
		"DDI Reset GPIO Value     ",
		pdata->core_pdata->ddi_rst_gpio ?
			gpio_get_value(pdata->core_pdata->ddi_rst_gpio) : 0,
		"VDDI GPIO                ", pdata->core_pdata->vddi_gpio,
		"VDDI GPIO Value          ",
		pdata->core_pdata->vddi_gpio ?
			gpio_get_value(pdata->core_pdata->vddi_gpio) : 0,
		"VCC GPIO                 ", pdata->core_pdata->vcc_gpio,
		"VCC GPIO Value           ",
		pdata->core_pdata->vcc_gpio ?
			gpio_get_value(pdata->core_pdata->vcc_gpio) : 0,
		"AVDD GPIO                ", pdata->core_pdata->avdd_gpio,
		"AVDD GPIO Value          ",
		pdata->core_pdata->avdd_gpio ?
			gpio_get_value(pdata->core_pdata->avdd_gpio) : 0,
		"AVEE GPIO                ", pdata->core_pdata->avee_gpio,
		"AVEE GPIO Value          ",
		pdata->core_pdata->avee_gpio ?
			gpio_get_value(pdata->core_pdata->avee_gpio) : 0,
		"Vendor ID                ", pdata->core_pdata->vendor_id,
		"Product ID               ", pdata->core_pdata->product_id,
		"Vkeys x                  ", pdata->mt_pdata->vkeys_x,
		"Vkeys y                  ", pdata->mt_pdata->vkeys_y,
		"Core data flags          ", pdata->core_pdata->flags,
		"MT data flags            ", pdata->mt_pdata->flags,
		"Loader data flags        ", pdata->loader_pdata->flags,
		"Level trigger delay (us) ",
			pdata->core_pdata->level_irq_udelay,
		"HID Descriptor Register  ",
			pdata->core_pdata->hid_desc_register,
		"HID Output Register      ",
			cd->hid_desc.output_register,
		"HID Command Register     ",
			cd->hid_desc.command_register,
		"Easy wakeup gesture      ",
			pdata->core_pdata->easy_wakeup_gesture,
		"Config DUT generation    ",
			pdata->core_pdata->config_dut_generation ?
			(pdata->core_pdata->config_dut_generation ==
				CONFIG_DUT_PIP2_CAPABLE ?
			"PT TC/TT" : "Gen5/6") : "Auto",
		"Watchdog Force Stop      ",
			pdata->core_pdata->watchdog_force_stop ?
			"True" : "False",
		"Panel ID Support         ",
			pdata->core_pdata->panel_id_support);
	return ret;
}

#define PT_ERR_STR_SIZE 64
/*******************************************************************************
 * FUNCTION: pt_bist_bus_test
 *
 * SUMMARY: Tests the connectivity of the active bus pins:
 *	I2C - SDA and SCL
 *	SPI - MOSI, MISO, CLK
 *
 *	Disable TTDL interrupts, send a PIP cmd and then manually read the
 *	bus. If any data is read we know the I2C/SPI pins are connected
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev         - pointer to device structure
 *	*net_toggled - pointer to where to store if bus toggled
 *	*err_str     - pointer to error string buffer
 ******************************************************************************/
static int pt_bist_bus_test(struct device *dev, u8 *net_toggled, u8 *err_str)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 ver_cmd[8] = {0x01, 0x01, 0x06, 0x00, 0x0E, 0x07, 0xF0, 0xB1};
	u8 *read_buf = NULL;
	int bytes_read = 0;
	int rc = 0;

	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
	if (read_buf == NULL) {
		rc = -ENOMEM;
		goto exit;
	}

	bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);

	pt_debug(dev, DL_INFO, "%s: TTDL Core Suspend\n", __func__);
	disable_irq(cd->irq);
	mutex_lock(&cd->system_lock);
	cd->irq_disabled = true;
	mutex_unlock(&cd->system_lock);

	/*
	 * Sleep >4ms to allow any pending TTDL IRQ to finish. Without this
	 * the disable_irq_nosync() could cause the IRQ to get stuck asserted
	 */
	usleep_range(5000, 6000);

	pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] VERSION\n",
		__func__, (int)sizeof(ver_cmd));

	pt_pr_buf(cd->dev, DL_DEBUG, ver_cmd, (int)sizeof(ver_cmd),
		">>> User CMD");
	rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL);
	if (rc) {
		pt_debug(dev, DL_ERROR,
			"%s: BUS Test - Failed to send VER cmd\n", __func__);
		*net_toggled = 0;
		strlcpy(err_str,
			"- Write failed, bus open or shorted or DUT in reset", PT_ERR_STR_SIZE);
		goto exit_enable_irq;
	}
	usleep_range(4000, 5000);
	bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, read_buf);
	pt_debug(dev, DL_INFO, "%s: BUS Test - %d bytes forced read\n",
		__func__, bytes_read);
	if (bytes_read == 0) {
		*net_toggled = 0;
		pt_debug(dev, DL_INFO, "%s: BUS Read Failed, 0 bytes read\n",
			__func__);
		strlcpy(err_str,
			"- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
		rc = -EIO;
		goto exit_enable_irq;
	} else {
		if (cd->bus_ops->bustype == BUS_I2C)
			*net_toggled = 1;
		else {
			if ((bytes_read > 3) &&
			    (read_buf[3] & PIP2_CMD_COMMAND_ID_MASK) ==
			     PIP2_CMD_ID_VERSION)
				*net_toggled = 1;
			else {
				*net_toggled = 0;
				pt_debug(dev, DL_INFO,
					"%s: BUS Read Failed, %d bytes read\n",
					__func__, bytes_read);
				strlcpy(err_str,
					"- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
			}
		}
	}

exit_enable_irq:
	enable_irq(cd->irq);
	usleep_range(5000, 6000);
	mutex_lock(&cd->system_lock);
	cd->irq_disabled = false;
	mutex_unlock(&cd->system_lock);
	pt_debug(dev, DL_INFO, "%s: TTDL Core Resumed\n", __func__);

exit:
	kfree(read_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_bist_irq_test
 *
 * SUMMARY: Tests the connectivity of the IRQ net
 *
 *	This test will ensure there is a good connection between the host
 *	and the DUT on the irq pin. First determine if the IRQ is stuck
 *	asserted and if so keep reading messages off of the bus until
 *	it de-asserts. Possible outcomes:
 *	 - IRQ was already de-asserted: Send a PIP command and if an
 *	interrupt is generated the test passes.
 *	 - IRQ was asserted: Reading off the bus de-assertes the IRQ,
 *	test passes.
 *	 - IRQ stays asserted: After reading the bus multiple times
 *	the IRQ stays asserted. Likely open or shorted to GND
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev          - pointer to device structure
 *	*bus_toggled  - pointer to where to store if bus toggled
 *	*irq_toggled  - pointer to where to store if IRQ toggled
 *	*xres_toggled - pointer to where to store if XRES toggled
 *	*err_str      - pointer to error string buffer
 ******************************************************************************/
static int pt_bist_irq_test(struct device *dev,
	u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 *read_buf = NULL;
	u8 mode = PT_MODE_UNKNOWN;
	u16 actual_read_len;
	int bytes_read  = 0;
	int count       = 0;
	int rc          = 0;

	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
	if (read_buf == NULL) {
		rc = -ENOMEM;
		goto exit;
	}

	/* Clear IRQ triggered count, and re-evaluate at the end of test */
	cd->irq_count = 0;

	/*
	 * Check if IRQ is stuck asserted, if so try a non forced flush of
	 * the bus based on the 2 byte initial length read. Try up to 5x.
	 */
	while (pt_check_irq_asserted(cd) && count < 5) {
		count++;
		bytes_read += pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
	}

	if (count > 1 && count < 5 && bytes_read > 0) {
		/*
		 * IRQ was stuck but data was successfully read from the
		 * bus releasing the IRQ line.
		 */
		pt_debug(dev, DL_INFO, "%s: count=%d bytes_read=%d\n",
			__func__, count, bytes_read);
		*bus_toggled = 1;
		*irq_toggled = 1;
		goto exit;
	}

	if (count == 5 && bytes_read == 0) {
		/*
		 * Looped 5x and read nothing off the bus yet the IRQ is still
		 * asserted. Possible conditions:
		 * - IRQ open circuit
		 * - IRQ shorted to GND
		 * - I2C/SPI bus is disconnected
		 * - FW holding the pin low
		 * Try entering the BL to see if communication works there.
		 */
		mode = PT_MODE_IGNORE;
		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s Failed to enter BL\n", __func__);
			strlcpy(err_str,
				"- likely shorted to GND or FW holding it.", PT_ERR_STR_SIZE);
			*irq_toggled = 0;
			goto exit;
		}
		/*
		 * If original mode was operational and we successfully
		 * entered the BL, then the XRES net must have toggled
		 */
		if (mode == PT_MODE_OPERATIONAL)
			*xres_toggled = 1;

		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
			PIP2_CMD_ID_VERSION, NULL, 0, read_buf,
			&actual_read_len);
		if (rc) {
			/*
			 * Could not communicate to DUT in BL mode. Save the
			 * error string, slim chance but the XRES test below may
			 * show the IRQ is actually working.
			 */
			strlcpy(err_str, "- likely shorted to GND.", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: %s, count=%d bytes_read=%d\n",
				__func__, err_str, count, bytes_read);
			*irq_toggled = 0;
			rc = pt_pip2_exit_bl_(cd, NULL, 0);
			goto exit;
		} else {
			*bus_toggled = 1;
			*irq_toggled = 1;
			goto exit;
		}
	}
	if (pt_check_irq_asserted(cd)) {
		strlcpy(err_str, "- likely shorted to GND", PT_ERR_STR_SIZE);
		rc = -EIO;
		*irq_toggled = 0;
		goto exit;
	}

	/* Try sending a PIP command to see if we get a response */
	rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
		PIP2_CMD_ID_VERSION, NULL, 0, read_buf, &actual_read_len);
	if (rc) {
		/*
		 * Potential IRQ issue, no communication in App mode, attempt
		 * the same command in the BL
		 */
		mode = PT_MODE_IGNORE;
		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s Failed to enter BL\n", __func__);
			*irq_toggled = 0;
			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
			goto exit;
		}
		/*
		 * If original mode was operational and we successfully
		 * entered the BL, this will be useful info for the tp_xres
		 * test below.
		 */
		if (mode == PT_MODE_OPERATIONAL)
			*xres_toggled = 1;

		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
			PIP2_CMD_ID_VERSION, NULL, 0, read_buf,
			&actual_read_len);
		if (rc) {
			/*
			 * Could not communicate in FW mode or BL mode. Save the
			 * error string, slim chance but the XRES test below may
			 * show the IRQ is actually working.
			 */
			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: request_active_pip_prot failed\n",
				__func__);
			*irq_toggled = 0;
			goto exit;
		}
	}

	if (cd->irq_count > 0) {
		pt_debug(dev, DL_INFO, "%s: irq_count=%d\n", __func__,
			cd->irq_count);
		*bus_toggled = 1;
		*irq_toggled = 1;
		goto exit;
	}

exit:
	kfree(read_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_bist_xres_test
 *
 * SUMMARY: Tests the connectivity of the TP_XRES net
 *
 *	This test will ensure there is a good connection between the host
 *	and the DUT on the tp_xres pin. The pin will be toggled to
 *	generate a TP reset which will cause the DUT to output a reset
 *	sentinel. If the reset sentinel is seen the test passes. If it is
 *	not seen the test will attempt to send a soft reset to simply gain
 *	some additional information on the failure:
 *	 - soft reset fails to send: XRES and IRQ likely open
 *	 - soft reset passes: XRES likely open or stuck de-asserted
 *	 - soft reset fails: XRES likely stuck asserted
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev          - pointer to device structure
 *	*bus_toggled  - pointer to where to store if bus toggled
 *	*irq_toggled  - pointer to where to store if IRQ toggled
 *	*xres_toggled - pointer to where to store if XRES toggled
 *	*err_str      - pointer to error string buffer
 ******************************************************************************/
static int pt_bist_xres_test(struct device *dev,
	u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	struct pt_platform_data *pdata = dev_get_platdata(dev);
	u8 *read_buf = NULL;
	u8 mode = PT_MODE_UNKNOWN;
	int rc         = 0;
	int t          = 0;
	int timeout    = 300;

	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
	if (read_buf == NULL) {
		rc = -ENOMEM;
		goto exit;
	}

	/* Clear the startup bit mask, reset and enum will re-populate it */
	cd->startup_status = STARTUP_STATUS_START;
	pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);

	if ((!pdata->core_pdata->rst_gpio) || (!pdata->core_pdata->xres)) {
		strlcpy(err_str, "- Net not configured or available", PT_ERR_STR_SIZE);
		rc = -ENODEV;
		goto exit;
	}


	/* Ensure we have nothing pending on active bus */
	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);

	/* Perform a hard XRES toggle and wait for reset sentinel */
	mutex_lock(&cd->system_lock);
	cd->hid_reset_cmd_state = 1;
	mutex_unlock(&cd->system_lock);
	pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__);
	rc = pt_hw_hard_reset(cd);

	/* Set timeout to 1s for the flashless case where a BL could be done */
	if (cd->flashless_dut)
		timeout = 1000;

	/*
	 * To avoid the case that next PIP command can be confused by BL/FW
	 * sentinel's "wakeup" event, chekcing hid_reset_cmd_state  which is
	 * followed by "wakeup event" function can lower the failure rate.
	 */
	t = wait_event_timeout(cd->wait_q,
		((cd->startup_status != STARTUP_STATUS_START)
		 && (cd->hid_reset_cmd_state == 0)),
		msecs_to_jiffies(timeout));
	if (IS_TMO(t)) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: TMO waiting for sentinel\n", __func__);
		*xres_toggled = 0;
		strlcpy(err_str, "- likely open. (No Reset Sentinel)", PT_ERR_STR_SIZE);

		/*
		 * Possibly bad FW, Try entering BL and wait for reset sentinel.
		 * To enter the BL we need to generate an XRES so first try to
		 * launch the applicaiton
		 */
		if (cd->mode == PT_MODE_BOOTLOADER)
			pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED);
		pt_debug(dev, DL_INFO, "%s: Enter BL with a hard reset\n",
			__func__);
		mode = PT_MODE_IGNORE;
		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
		if (rc) {
			pt_debug(dev, DL_ERROR, "%s Failed to enter BL\n",
				__func__);
			*xres_toggled = 0;
			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
			goto exit;
		} else {
			/* Wait for the BL sentinel */
			t = wait_event_timeout(cd->wait_q,
				(cd->startup_status != STARTUP_STATUS_START),
				msecs_to_jiffies(500));
			if (IS_TMO(t)) {
				pt_debug(cd->dev, DL_ERROR,
					"%s: TMO waiting for BL sentinel\n",
					__func__);
				*xres_toggled = 0;
				strlcpy(err_str,
					"- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
				rc = -ETIME;
				goto exit;
			}
		}
	}
	mutex_lock(&cd->system_lock);
	cd->hid_reset_cmd_state = 0;
	mutex_unlock(&cd->system_lock);

	/* Look for BL or FW reset sentinels */
	if (!rc && ((cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL) ||
	    (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL))) {
		pt_debug(dev, DL_INFO, "%s: hard XRES pass\n", __func__);
		/* If a sentinel was seen, all nets are working */
		*xres_toggled = 1;
		*irq_toggled  = 1;
		/*
		 * For SPI test, bus read result can be confused as FW sentinel
		 * if MISO(slave) is connected to MISO(host).
		 */
		if (cd->bus_ops->bustype == BUS_I2C)
			*bus_toggled = 1;
	} else {
		/*
		 * Hard reset failed, however some additional information
		 * could be determined. Try a soft reset to see if DUT resets
		 * with the possible outcomes:
		 * - if it resets the line is not stuck asserted
		 * - if it does not reset the line could be stuck asserted
		 */
		*xres_toggled = 0;
		rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED);
		msleep(30);
		pt_debug(dev, DL_INFO, "%s: TP_XRES BIST soft reset rc=%d",
			__func__, rc);
		if (rc) {
			strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: Hard reset failed, soft reset failed %s\n",
				__func__, err_str);
			goto exit;
		}
		if (cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL ||
		    cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) {
			strlcpy(err_str,
				"- likely open or stuck high, soft reset OK", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: Hard reset failed, soft reset passed-%s\n",
				__func__, err_str);
		} else if (cd->startup_status == 0) {
			strlcpy(err_str, "- likely stuck high.", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: Hard reset failed, soft reset failed-%s\n",
				__func__, err_str);
		} else {
			strlcpy(err_str, "- open or stuck.", PT_ERR_STR_SIZE);
			pt_debug(dev, DL_ERROR,
				"%s: Hard and Soft reset failed - %s\n",
				__func__, err_str);
		}
	}

exit:
	kfree(read_buf);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_bist_slave_irq_test
 *
 * SUMMARY: Tests the connectivity of the Master/Slave IRQ net
 *
 *	This test will ensure there is a good IRQ connection between the master
 *	DUT and the slave DUT(s). After power up the STATUS command is sent
 *	and the 'Slave Detect' bits are verified to ensure the master DUT
 *	saw each slave trigger the IRQ with it's reset sentinel.
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev                - pointer to device structure
 *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
 *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
 *	*err_str            - pointer to error string buffer
 *	*slave_detect       - pointer to slave detect buffer
 *	*boot_err           - pointer to boot_err  buffer
 ******************************************************************************/
static int pt_bist_slave_irq_test(struct device *dev,
	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str,
	u8 *slave_detect, u8 *boot_err)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 mode = PT_MODE_UNKNOWN;
	u8 status;
	u8 boot;
	u8 read_buf[12];
	u8 detected = 0;
	u8 last_err = -1;
	u16 actual_read_len;
	int result = 0;
	int rc = 0;

	/*
	 * Ensure DUT is in the BL where the STATUS cmd will report the slave
	 * detect bits. If the DUT was in FW, entering the BL will cause an
	 * XRES signal which will inadvertently test the XRES net as well
	 */
	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n",
			__func__, rc);
		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
		goto exit;
	}

	/* Use the STATUS command to retrieve the slave detect bit(s) */
	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
		PIP2_CMD_ID_STATUS, NULL, 0, read_buf,
		&actual_read_len);
	if (!rc) {
		pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len,
			"PIP2 STATUS");
		status = read_buf[PIP2_RESP_STATUS_OFFSET];
		boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;

		/* Slave detect is only valid if status ok and in boot exec */
		if (status == PIP2_RSP_ERR_NONE &&
		    boot == PIP2_STATUS_BOOT_EXEC) {
			detected = read_buf[PIP2_RESP_BODY_OFFSET + 2] &
					SLAVE_DETECT_MASK;
		} else {
			strlcpy(err_str, "- State could not be determined", PT_ERR_STR_SIZE);
			rc = -EPERM;
		}
	} else {
		pt_debug(cd->dev, DL_ERROR, "%s: STATUS cmd failure\n",
			__func__);
		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
		goto exit;
	}

	/*
	 * Retrieve boot error regardless of the state of the slave detect
	 * bit because the IRQ could have been stuck high or low.
	 */
	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
		PIP2_CMD_ID_GET_LAST_ERRNO, NULL, 0,
		read_buf, &actual_read_len);
	if (!rc) {
		pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len,
			"PIP2 GET_LAST_ERRNO");
		status = read_buf[PIP2_RESP_STATUS_OFFSET];
		last_err = read_buf[PIP2_RESP_BODY_OFFSET];
		if (last_err) {
			pt_debug(cd->dev, DL_ERROR,
				"%s: Master Boot Last Err = 0x%02X\n",
				__func__, last_err);
		}
	} else {
		pt_debug(cd->dev, DL_ERROR,
			"%s: GET_LAST_ERRNO cmd failure\n", __func__);
		strlcpy(err_str, "- stuck, likely shorted to GND.", PT_ERR_STR_SIZE);
	}

exit:
	pt_debug(cd->dev, DL_INFO,
		"%s: rc=%d detected=0x%02X boot_err=0x%02X\n",
		__func__, rc, detected, last_err);
	if (err_str && last_err) {
		if (detected)
			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
				"- Likely stuck low. Boot Error:",
				last_err);
		else
			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
				"- Likely stuck high. Boot Error:",
				last_err);
	}

	/* Ignore an invalid image error as BIST doesn't need valid FW */
	if (last_err == PIP2_RSP_ERR_INVALID_IMAGE)
		last_err = PIP2_RSP_ERR_NONE;

	if (slave_irq_toggled)
		*slave_irq_toggled = (detected && !last_err) ? true : false;
	if (slave_bus_toggled) {
		/* Leave as UNTEST if slave not detected */
		if (detected)
			*slave_bus_toggled = !last_err ? true : false;
	}
	if (slave_detect)
		*slave_detect = detected;
	if (boot_err)
		*boot_err = last_err;

	if (slave_irq_toggled && slave_bus_toggled)
		pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n",
				__func__,
				"Detected", detected,
				"slave_irq_toggled", *slave_irq_toggled,
				"slave_bus_toggled", *slave_bus_toggled);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_bist_slave_xres_test
 *
 * SUMMARY: Tests the connectivity of the Master/Slave TP_XRES net
 *
 *	This test will ensure there is a good TP_XRES connection between the
 *	master DUT and the slave DUT(s). After toggling the XRES pin to the
 *	master, the STATUS command is sent and the 'Slave Detect' bits are
 *	verified to ensure the master DUT saw each slave trigger the IRQ with
 *	it's reset sentinel.
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev                - pointer to device structure
 *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
 *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
 *	*slave_xres_toggled - pointer to where to store if slave XRES toggled
 *	*err_str            - pointer to error string buffer
 ******************************************************************************/
static int pt_bist_slave_xres_test(struct device *dev,
	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *slave_xres_toggled,
	u8 *err_str)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 slave_detect = 0;
	u8 boot_err = 0;
	int rc = 0;

	/* Force a reset to force the 'slave detect' bits to be re-acquired */
	mutex_lock(&cd->system_lock);
	cd->hid_reset_cmd_state = 1;
	mutex_unlock(&cd->system_lock);
	pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__);
	pt_hw_hard_reset(cd);
	msleep(100);

	rc = pt_bist_slave_irq_test(dev, slave_irq_toggled,
		slave_bus_toggled, err_str, &slave_detect, &boot_err);
	pt_debug(dev, DL_INFO, "%s: IRQ test rc = %d\n", __func__, rc);

	if (!rc && *slave_irq_toggled == false) {
		/*
		 * If the slave IRQ did not toggle, either the slave_detect
		 * bit was not set or we had a boot error. If the slave
		 * detect was not set the slave did not reset causing a boot
		 * error.
		 */
		if (!slave_detect)
			strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE);
		else
			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
				"- likely open or an IRQ issue. Boot Error:",
				boot_err);
	}
	if (slave_xres_toggled) {
		if (!rc)
			*slave_xres_toggled = *slave_irq_toggled ? true : false;
		else
			*slave_xres_toggled = false;

	}

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_bist_slave_bus_test
 *
 * SUMMARY: Tests the connectivity of the Master/Slave SPI bus net
 *
 *	This test will ensure a good SPI bus connection between the
 *	master DUT and the slave DUT(s). This bus connection is ensured by
 *	opening file 0 (SRAM loader). If there is a slave and the open fails
 *	then there is a master/slave communication issue. Opening file 0 on
 *	the master will open it on the slave as well if the slave was detected.
 *
 * RETURN:
 *	 0 = Success
 *	!0 = Failure
 *
 * PARAMETERS:
 *	*dev                - pointer to device structure
 *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
 *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
 *	*err_str            - pointer to error string buffer
 ******************************************************************************/
static int pt_bist_slave_bus_test(struct device *dev,
	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u8 mode = PT_MODE_UNKNOWN;
	u8 bus_toggled = false;
	u8 file_handle;
	int result = 0;
	int rc = 0;

	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n",
			__func__, rc);
		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
		goto exit;
	}

	pt_debug(dev, DL_INFO, "%s Attempt open file 0\n", __func__);
	file_handle = _pt_pip2_file_open(dev, PIP2_RAM_FILE);
	if (file_handle != PIP2_RAM_FILE) {
		rc = -ENOENT;
		bus_toggled = false;
		pt_debug(dev, DL_ERROR,
			"%s Failed to open bin file\n", __func__);
		strlcpy(err_str, "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
		goto exit;
	} else {
		bus_toggled = true;
		if (file_handle != _pt_pip2_file_close(dev, file_handle)) {
			pt_debug(dev, DL_ERROR,
				"%s: File Close failed, file_handle=%d\n",
				__func__, file_handle);
		}
	}

exit:
	/* If the master was able to send/recv a PIP msg, the IRQ must be ok */
	if (slave_irq_toggled)
		*slave_irq_toggled = bus_toggled;
	if (slave_bus_toggled)
		*slave_bus_toggled = bus_toggled;

	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_ttdl_bist_show
 *
 * SUMMARY: Show method for the ttdl_bist sysfs node. This built in self test
 *	will test that I2C/SPI, IRQ and TP_XRES pins are operational.
 *
 *	NOTE: This function will reset the DUT and the startup_status bit
 *	mask. A pt_enum will be queued after completion.
 *
 *	NOTE: The order of the net tests is done to optimize the time it takes
 *	to run. The first test is capable of verifying all nets, each subsequent
 *	test is only run if the previous was not able to see all nets toggle.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_ttdl_bist_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;
	char *bus_err_str        = NULL;
	char *irq_err_str        = NULL;
	char *xres_err_str       = NULL;
	char *slave_bus_err_str  = NULL;
	char *slave_irq_err_str  = NULL;
	char *slave_xres_err_str = NULL;
	u8 tests;
	int rc                = 0;
	int num_tests         = 0;
	int status            = 1;    /* 0 = Pass, !0 = fail */
	u8 bus_toggled        = 0x0F; /* default to untested */
	u8 i2c_toggled        = 0x0F; /* default to untested */
	u8 spi_toggled        = 0x0F; /* default to untested */
	u8 irq_toggled        = 0x0F; /* default to untested */
	u8 xres_toggled       = 0x0F; /* default to untested */
	u8 slave_bus_toggled  = 0x0F; /* default to untested */
	u8 slave_irq_toggled  = 0x0F; /* default to untested */
	u8 slave_xres_toggled = 0x0F; /* default to untested */

	bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
	irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
	xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
	if (!bus_err_str || !irq_err_str || !xres_err_str)
		goto print_results;

	memset(xres_err_str, 0, PT_ERR_STR_SIZE);
	memset(irq_err_str, 0, PT_ERR_STR_SIZE);
	memset(bus_err_str, 0, PT_ERR_STR_SIZE);

	if (cd->multi_chip) {
		slave_bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
		slave_irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
		slave_xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
		if (!slave_bus_err_str ||
		    !slave_irq_err_str ||
		    !slave_xres_err_str)
			goto print_results;
		memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE);
		memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE);
		memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE);
	}

	/* Turn off the TTDL WD during the test */
	pt_stop_wd_timer(cd);

	/* Shorten default PIP cmd timeout while running BIST */
	cd->pip_cmd_timeout = 200;

	/* Count the number of tests to run */
	tests = cd->ttdl_bist_select;
	while (tests) {
		num_tests += tests & 1;
		tests >>= 1;
	}
	pt_debug(dev, DL_ERROR, "%s: BIST select = 0x%02X, run %d tests\n",
		__func__, cd->ttdl_bist_select, num_tests);

	/* Suppress auto BL to avoid loader thread sending PIP during xres */
	if (cd->flashless_dut) {
		pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - SUPPRESS\n",
			__func__);
		mutex_lock(&cd->system_lock);
		cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
		mutex_unlock(&cd->system_lock);
	}

	/* --------------- TP_XRES BIST TEST --------------- */
	if ((cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start TP_XRES BIST -----", __func__);
		rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled,
			&xres_toggled, xres_err_str);
		/* Done if the rest of all nets toggled */
		if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1)
			goto host_nets_complete;
	}

	/* Flush bus in case a PIP response is waiting from previous test */
	pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);

	/* --------------- IRQ BIST TEST --------------- */
	if ((cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start IRQ BIST -----", __func__);
		bus_toggled = 0xFF;
		irq_toggled = 0xFF;
		rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled,
			&xres_toggled, irq_err_str);
		/* If this net failed clear results from previous net */
		if (irq_toggled != 1) {
			xres_toggled = 0x0F;
			memset(xres_err_str, 0, PT_ERR_STR_SIZE);
		}
		if (bus_toggled == 1 && irq_toggled == 1)
			goto host_nets_complete;
	}

	/* Flush bus in case a PIP response is waiting from previous test */
	pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);

	/* --------------- BUS BIST TEST --------------- */
	if ((cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start BUS BIST -----", __func__);
		bus_toggled = 0xFF;
		rc = pt_bist_bus_test(dev, &bus_toggled, bus_err_str);
		/* If this net failed clear results from previous net */
		if (bus_toggled == 0) {
			irq_toggled = 0x0F;
			memset(irq_err_str, 0, PT_ERR_STR_SIZE);
		}
	}

host_nets_complete:
	/* --------------- SLAVE XRES BIST TEST --------------- */
	if (cd->multi_chip &&
	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start Slave XRES BIST -----", __func__);
		slave_xres_toggled = 0xFF;
		rc = pt_bist_slave_xres_test(dev, &slave_irq_toggled,
			&slave_bus_toggled, &slave_xres_toggled,
			slave_xres_err_str);
		if ((slave_bus_toggled == 1 && slave_irq_toggled == 1 &&
		    slave_xres_toggled == 1) || slave_xres_toggled == 0)
			goto print_results;
	}

	/* --------------- SLAVE IRQ BIST TEST --------------- */
	if (cd->multi_chip &&
	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start Slave IRQ BIST -----", __func__);
		slave_irq_toggled = 0xFF;
		rc = pt_bist_slave_irq_test(dev, &slave_irq_toggled,
			&slave_bus_toggled, slave_irq_err_str, NULL, NULL);
		pt_debug(dev, DL_INFO, "%s: slave_irq_toggled = 0x%02X\n",
			__func__, slave_irq_toggled);
		if (slave_irq_toggled == 1) {
			slave_bus_toggled = 1;
			goto print_results;
		}
	}

	/* --------------- SLAVE BUS BIST TEST --------------- */
	if (cd->multi_chip &&
	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) != 0) {
		pt_debug(dev, DL_INFO,
			"%s: ----- Start Slave BUS BIST -----", __func__);
		slave_bus_toggled = 0xFF;
		rc = pt_bist_slave_bus_test(dev, &slave_irq_toggled,
			&slave_bus_toggled, slave_bus_err_str);
	}

print_results:
	/* Restore PIP command timeout */
	cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;

	/*
	 * We're done! - Perform a hard XRES toggle, allowing BL
	 * to load FW if there is any in Flash
	 */
	mutex_lock(&cd->system_lock);
	cd->hid_reset_cmd_state = 0;
	mutex_unlock(&cd->system_lock);

	pt_debug(dev, DL_INFO,
		"%s: TTDL BIST Complete - Final reset\n", __func__);

	if (cd->flashless_dut) {
		/*
		 * For Flashless solution, FW update is triggered after BL is
		 * seen that several miliseconds delay is needed.
		 */
		pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - ALLOW\n",
			__func__);
		mutex_lock(&cd->system_lock);
		cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
		mutex_unlock(&cd->system_lock);

		/* Reset DUT and wait 100ms to see if loader started */
		pt_hw_hard_reset(cd);
		msleep(100);
		if (cd->fw_updating) {
			pt_debug(dev, DL_INFO,
				 "%s: ----- BIST Wait FW Loading ----",
				 __func__);
			rc = _pt_request_wait_for_enum_state(
			    dev, 4000, STARTUP_STATUS_COMPLETE);
		}
	} else {
		if (cd->mode == PT_MODE_BOOTLOADER) {
			rc = pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED);
			if (rc) {
				pt_debug(dev, DL_ERROR,
					"%s Failed to launch app\n", __func__);
				rc = pt_hw_hard_reset(cd);
			}
		}

		/*
		 * If FW exists the BL may have just started or will start soon,
		 * so the FW sentinel may be on it's way but with no FW it will
		 * not arrive, wait for it before deciding if we need to queue
		 * an enum.
		 */
		rc = _pt_request_wait_for_enum_state(
		    dev, 400, STARTUP_STATUS_FW_RESET_SENTINEL);
		if ((cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) &&
		    (cd->startup_status & STARTUP_STATUS_COMPLETE) == 0) {
			pt_debug(dev, DL_INFO, "%s: ----- BIST Enum ----",
				 __func__);
			pt_queue_enum(cd);
			rc = _pt_request_wait_for_enum_state(
			    dev, 2000, STARTUP_STATUS_COMPLETE);
		}
	}
	msleep(20);

	/* --------------- PRINT OUT BIST RESULTS ---------------*/
	pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__);
	pt_start_wd_timer(cd);

	/* Canned print if any memory allocation issues */
	if (!bus_err_str || !irq_err_str || !xres_err_str) {
		ret = scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"I2C (SDA,SCL):              [UNTEST]\n"
			"SPI (MISO,MOSI,CS,CLK):     [UNTEST]\n"
			"IRQ connection:             [UNTEST]\n"
			"TP_XRES connection:         [UNTEST]\n", -ENOMEM);
		if (cd->multi_chip) {
			ret += scnprintf(buf + ret, strlen(buf),
				"I/P SPI (MISO,MOSI,CS,CLK): [UNTEST]\n"
				"I/P IRQ connection:         [UNTEST]\n"
				"I/P TP_XRES connection:     [UNTEST]\n");
		}
	} else {
		status = 0;
		if (bus_toggled == 1)
			memset(bus_err_str, 0, PT_ERR_STR_SIZE);
		if (irq_toggled == 1)
			memset(irq_err_str, 0, PT_ERR_STR_SIZE);
		if (xres_toggled == 1)
			memset(xres_err_str, 0, PT_ERR_STR_SIZE);

		if (cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST)
			status += bus_toggled;
		if (cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST)
			status += irq_toggled;
		if (cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST)
			status += xres_toggled;
		pt_debug(dev, DL_ERROR, "%s: status = %d (%d,%d,%d)\n",
			__func__, status, bus_toggled, irq_toggled,
			xres_toggled);

		if (cd->multi_chip) {
			if (slave_irq_toggled == 1)
				memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE);
			if (slave_xres_toggled == 1)
				memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE);
			if (slave_bus_toggled == 1)
				memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE);

			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST)
				status += slave_bus_toggled;
			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST)
				status += slave_irq_toggled;
			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST)
				status += slave_xres_toggled;
			pt_debug(dev, DL_ERROR,
				"%s: status = %d (%d,%d,%d,%d,%d,%d)\n",
				__func__, status, bus_toggled, irq_toggled,
				xres_toggled, slave_bus_toggled,
				slave_irq_toggled, slave_xres_toggled);
		}

		if (cd->bus_ops->bustype == BUS_I2C)
			i2c_toggled = bus_toggled;
		else
			spi_toggled = bus_toggled;

		ret = scnprintf(buf, strlen(buf),
			"Status: %d\n"
			"I2C (SDA,SCL):               %s %s\n"
			"SPI (MISO,MOSI,CS,CLK):      %s %s\n"
			"IRQ connection:              %s %s\n"
			"TP_XRES connection:          %s %s\n",
			status == num_tests ? 0 : 1,
			i2c_toggled == 0x0F ? "[UNTEST]" :
				i2c_toggled == 1 ? "[  OK  ]" : "[FAILED]",
			i2c_toggled == 0x0F ? "" : bus_err_str,
			spi_toggled == 0x0F ? "[UNTEST]" :
				spi_toggled == 1 ? "[  OK  ]" : "[FAILED]",
			spi_toggled == 0x0F ? "" : bus_err_str,
			irq_toggled == 0x0F ? "[UNTEST]" :
				irq_toggled == 1 ? "[  OK  ]" : "[FAILED]",
			irq_err_str,
			xres_toggled == 0x0F ? "[UNTEST]" :
				xres_toggled == 1 ? "[  OK  ]" : "[FAILED]",
			xres_err_str);

		if (cd->multi_chip) {
			ret += scnprintf(buf + ret, strlen(buf),
				"I/P SPI (MISO,MOSI,CS,CLK):  %s %s\n"
				"I/P IRQ connection:          %s %s\n"
				"I/P TP_XRES connection:      %s %s\n",
				slave_bus_toggled == 0x0F ? "[UNTEST]" :
					slave_bus_toggled == 1 ? "[  OK  ]" :
						"[FAILED]", slave_bus_err_str,
				slave_irq_toggled == 0x0F ? "[UNTEST]" :
					slave_irq_toggled == 1 ? "[  OK  ]" :
						"[FAILED]", slave_irq_err_str,
				slave_xres_toggled == 0x0F ? "[UNTEST]" :
					slave_xres_toggled == 1 ? "[  OK  ]" :
						"[FAILED]", slave_xres_err_str);
		}
	}

	/* Put TTDL back into a known state, issue a ttdl enum if needed */
	pt_debug(dev, DL_INFO, "%s: Startup_status = 0x%04X\n",
		__func__, cd->startup_status);
	kfree(bus_err_str);
	kfree(irq_err_str);
	kfree(xres_err_str);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_ttdl_bist_store
 *
 * SUMMARY: Store method for the ttdl_bist sysfs node.
 *
 * RETURN: Size of passed in buffer if successful
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to command buffer
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_ttdl_bist_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2] = {0};
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	} else {
		mutex_lock(&cd->system_lock);
		cd->ttdl_bist_select = input_data[0];
		mutex_unlock(&cd->system_lock);
	}

exit:
	if (rc)
		return rc;
	return size;
}
static DEVICE_ATTR(ttdl_bist,  0644, pt_ttdl_bist_show,
	pt_ttdl_bist_store);

/*******************************************************************************
 * FUNCTION: pt_flush_bus_store
 *
 * SUMMARY: Store method for the flush_bus sysfs node.
 *
 * RETURN: Size of passed in buffer if successful
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to command buffer
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_flush_bus_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2] = {0};
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	if (input_data[0] == 0)
		cd->flush_bus_type = PT_FLUSH_BUS_BASED_ON_LEN;
	else if (input_data[0] == 1)
		cd->flush_bus_type = PT_FLUSH_BUS_FULL_256_READ;
	else
		rc = -EINVAL;
	mutex_unlock(&cd->system_lock);

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_flush_bus_show
 *
 * SUMMARY: Show method for the flush_bus sysfs node that flushes the active bus
 *      based on either the size of the first two bytes or a blind 256 bytes.
 *
 * RETURN:
 *       0 = success
 *      !0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to device structure
 *      *attr - pointer to device attributes
 *      *buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_flush_bus_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret = 0;
	ssize_t bytes = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	mutex_lock(&cd->system_lock);
	bytes = pt_flush_bus(cd, cd->flush_bus_type, NULL);

	ret = scnprintf(buf, strlen(buf),
		"Status: 0\n"
		"%s: %zd\n",
		"Bytes flushed", bytes);

	mutex_unlock(&cd->system_lock);
	return ret;
}
static DEVICE_ATTR(flush_bus, 0644, pt_flush_bus_show,
	pt_flush_bus_store);

/*******************************************************************************
 * FUNCTION: pt_pip2_ping_test_store
 *
 * SUMMARY: Store method for the pip2_ping_test sysfs node.
 *
 * NOTE: The max PIP2 packet size is 255 (payload for PING 247) however
 *	someone may want to test sending invalid packet lengths so any values
 *	up to 255 are allowed.
 *
 * RETURN: Size of passed in buffer if successful
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to command buffer
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_pip2_ping_test_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	if (input_data[1] >= 0 && input_data[0] <= PT_MAX_PIP2_MSG_SIZE)
		cd->ping_test_size = input_data[0];
	else
		rc = -EINVAL;
	mutex_unlock(&cd->system_lock);

exit:
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_ping_test_show
 *
 * SUMMARY: Show method for the ping_test_show sysfs node that sends the PIP2
 *      PING command and ramps up the optional payload from 0 to
 *	ping_test_size.
 *	The max payload size is 247:
 *		(255 - 2 byte reg address - 4 byte header - 2 byte CRC)
 *
 * RETURN:
 *       0 = success
 *      !0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to device structure
 *      *attr - pointer to device attributes
 *      *buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_pip2_ping_test_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;
	int last_packet_size;
	int rc = 0;

	rc = pt_pip2_ping_test(dev, cd->ping_test_size, &last_packet_size);

	if (rc) {
		ret = scnprintf(buf, strlen(buf), "Status: %d\n", rc);
		return ret;
	}
	ret = scnprintf(buf, strlen(buf),
		"Status: %d\n"
		"PING payload test passed with packet sizes 0 - %d\n",
		(last_packet_size == cd->ping_test_size ? 0 : 1),
		last_packet_size);

	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_t_refresh_store
 *
 * SUMMARY: Store method for the t-refresh sysfs node that will takes a passed
 *	in integer as the number of interrupts to count. A timer is started to
 *	calculate the total time it takes to see that number of interrupts.
 *
 * RETURN: Size of passed in buffer if successful
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to buffer that hold the command parameters
 *	 size - size of buf
 ******************************************************************************/
static ssize_t pt_t_refresh_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 input_data[2];
	int length;
	int rc = 0;

	/* Maximum input of one value */
	length = _pt_ic_parse_input(dev, buf, size, input_data,
		ARRAY_SIZE(input_data));
	if (length != 1) {
		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
			__func__);
		rc = -EINVAL;
		goto exit;
	}

	mutex_lock(&cd->system_lock);
	pt_debug(dev, DL_INFO, "%s: Input value: %d\n", __func__,
		input_data[0]);
	if (input_data[0] >= 0 && input_data[0] <= 1000) {
		cd->t_refresh_total = input_data[0];
		cd->t_refresh_count  = 0;
		cd->t_refresh_active = 1;
	} else {
		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
		rc = -EINVAL;
	}
	mutex_unlock(&cd->system_lock);

exit:
	pt_debug(dev, DL_ERROR, "%s: rc = %d\n", __func__, rc);
	if (rc)
		return rc;
	return size;
}

/*******************************************************************************
 * FUNCTION: pt_t_refresh_show
 *
 * SUMMARY: Show method for the t-refresh sysfs node that will show the results
 *	of the T-Refresh timer counting the time it takes to see a user defined
 *	number of interrupts.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_t_refresh_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	ssize_t ret = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);
	u32 whole;
	u16 fraction;

	mutex_lock(&cd->system_lock);

	/* Check if we have counted the number requested */
	if (cd->t_refresh_count != cd->t_refresh_total) {
		ret = scnprintf(buf, strlen(buf),
			"Status: 0\n"
			"%s: %d\n",
			"Still counting... current IRQ count",
			cd->t_refresh_count);
	} else {
		/* Ensure T-Refresh is de-activated */
		cd->t_refresh_active = 0;

		whole = cd->t_refresh_time / cd->t_refresh_count;
		fraction = cd->t_refresh_time % cd->t_refresh_count;
		fraction = fraction * 1000 / cd->t_refresh_count;

		ret = scnprintf(buf, strlen(buf),
			"Status: 0\n"
			"%s: %d\n"
			"%s: %d\n"
			"%s: %d\n"
			"%s: %d.%02d\n",
			"Requested IRQ Count     ", cd->t_refresh_total,
			"IRQ Counted             ", cd->t_refresh_count,
			"Total Time Elapsed (ms) ", (int)cd->t_refresh_time,
			"Average T-Refresh  (ms) ", whole, fraction);
	}
	mutex_unlock(&cd->system_lock);
	return ret;
}

/*******************************************************************************
 * FUNCTION: pt_dut_status_show
 *
 * SUMMARY: Show method for DUT status sysfs node. Display DUT's scan state, and
 * more items such as operation mode,easywake state are added in the future.
 *
 * RETURN: Char buffer with printed scan status information
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 *	*attr - pointer to device attributes
 *	*buf  - pointer to output buffer
 ******************************************************************************/
static ssize_t pt_dut_status_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
	u8 mode = PT_MODE_UNKNOWN;
	char *outputstring[7] = {"BOOT", "SCANNING", "DEEP_SLEEP",
				 "TEST", "DEEP_STANDBY", "UNDEFINED", "n/a"};
	struct pt_core_data *cd = dev_get_drvdata(dev);
	ssize_t ret;
	u16 calculated_crc = 0;
	u16 stored_crc = 0;
	u8 status;
	int rc = 0;

	/* In STANDBY the DUT will not repond to any PIP cmd */
	if (cd->fw_sys_mode_in_standby_state) {
		mode = PT_MODE_OPERATIONAL;
		sys_mode = FW_SYS_MODE_DEEP_STANDBY;
		goto print_limited_results;
	}

	/* Retrieve mode and FW system mode which can only be 0-4 */
	rc = pt_get_fw_sys_mode(cd, &sys_mode, &mode);
	if (rc || mode == PT_MODE_UNKNOWN) {
		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
			"%s: %d\n"
			"%s: n/a\n"
			"%s: n/a\n"
			"%s: n/a\n"
			"%s: n/a\n",
			"Status", rc,
			"Active Exec    ",
			"FW System Mode ",
			"Stored CRC     ",
			"Calculated CRC ");
		return ret;
	} else {
		if (mode == PT_MODE_OPERATIONAL) {
			if (sys_mode > FW_SYS_MODE_MAX)
				sys_mode = FW_SYS_MODE_UNDEFINED;
			if (sys_mode != FW_SYS_MODE_TEST)
				goto print_limited_results;

			rc = pt_pip_verify_config_block_crc_(cd,
				PT_TCH_PARM_EBID, &status,
				&calculated_crc, &stored_crc);
			if (rc)
				goto print_limited_results;

			ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
				"%s: %d\n"
				"%s: %s\n"
				"%s: %s\n"
				"%s: 0x%04X\n"
				"%s: 0x%04X\n",
				"Status", rc,
				"Active Exec    ", "FW",
				"FW System Mode ", outputstring[sys_mode],
				"Stored CRC     ", stored_crc,
				"Calculated CRC ", calculated_crc);
			return ret;
		} else {
			/* When in BL or unknon mode Active Exec is "n/a" */
			sys_mode = FW_SYS_MODE_UNDEFINED + 1;
		}
	}

print_limited_results:
	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
		"%s: %d\n"
		"%s: %s\n"
		"%s: %s\n"
		"%s: n/a\n"
		"%s: n/a\n",
		"Status", rc,
		"Active Exec    ",
			mode == PT_MODE_OPERATIONAL ? "FW" : "BL",
		"FW System Mode ", outputstring[sys_mode],
		"Stored CRC     ",
		"Calculated CRC ");

	return ret;
}
#endif /* TTDL_DIAGNOSTICS */


/*******************************************************************************
 * Structures of sysfs attributes for all DUT dependent sysfs node
 ******************************************************************************/
static struct attribute *early_attrs[] = {
	&dev_attr_hw_version.attr,
	&dev_attr_drv_version.attr,
	&dev_attr_drv_ver.attr,
	&dev_attr_fw_version.attr,
	&dev_attr_sysinfo.attr,
	&dev_attr_pip2_cmd_rsp.attr,
	&dev_attr_command.attr,
	&dev_attr_drv_debug.attr,
	&dev_attr_hw_reset.attr,
	&dev_attr_response.attr,
	&dev_attr_ttdl_restart.attr,
#ifdef TTDL_DIAGNOSTICS
	&dev_attr_ttdl_status.attr,
	&dev_attr_pip2_enter_bl.attr,
	&dev_attr_pip2_exit_bl.attr,
	&dev_attr_err_gpio.attr,
	&dev_attr_flush_bus.attr,
	&dev_attr_ttdl_bist.attr,
#endif /* TTDL_DIAGNOSTICS */
	NULL,
};

static struct attribute_group early_attr_group = {
	.attrs	= early_attrs,
};

static struct device_attribute pip2_attributes[] = {
	__ATTR(pip2_version, 0444, pt_pip2_version_show, NULL),
	__ATTR(pip2_gpio_read, 0444, pt_pip2_gpio_read_show, NULL),
#ifdef TTDL_DIAGNOSTICS
	__ATTR(pip2_bin_hdr,  0444, pt_pip2_bin_hdr_show, NULL),
	__ATTR(pip2_ping_test, 0644, pt_pip2_ping_test_show,
		pt_pip2_ping_test_store),
#endif
};

static struct device_attribute attributes[] = {
	__ATTR(dut_debug, 0644,
		pt_dut_debug_show, pt_drv_debug_store),
	__ATTR(sleep_status, 0444, pt_sleep_status_show, NULL),
	__ATTR(panel_id, 0444, pt_panel_id_show, NULL),
	__ATTR(get_param, 0644,
		pt_get_param_show, pt_get_param_store),
	__ATTR(pt_touch_offload, 0644,
		pt_touch_offload_show, pt_touch_offload_store),
#ifdef EASYWAKE_TSG6
	__ATTR(easy_wakeup_gesture, 0644, pt_easy_wakeup_gesture_show,
		pt_easy_wakeup_gesture_store),
	__ATTR(easy_wakeup_gesture_id, 0444,
		pt_easy_wakeup_gesture_id_show, NULL),
	__ATTR(easy_wakeup_gesture_data, 0444,
		pt_easy_wakeup_gesture_data_show, NULL),
#endif
#ifdef TTDL_DIAGNOSTICS
	__ATTR(platform_data, 0444, pt_platform_data_show, NULL),
	__ATTR(drv_irq, 0644, pt_drv_irq_show, pt_drv_irq_store),
	__ATTR(dut_status, 0444, pt_dut_status_show, NULL),
	__ATTR(t_refresh, 0644, pt_t_refresh_show, pt_t_refresh_store),
#endif /* TTDL_DIAGNOSTICS */
};

/*******************************************************************************
 * FUNCTION: add_sysfs_interfaces
 *
 * SUMMARY: Creates all DUT dependent sysfs nodes owned by the core
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 ******************************************************************************/
static int add_sysfs_interfaces(struct device *dev)
{
	int i;
	int j = 0;
	struct pt_core_data *cd = dev_get_drvdata(dev);

	for (i = 0; i < ARRAY_SIZE(attributes); i++) {
		if (device_create_file(dev, attributes + i))
			goto undo;
	}

	pt_debug(dev, DL_INFO, "%s: Active DUT Generation: %d",
		__func__, cd->active_dut_generation);
	if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
		for (j = 0; j < ARRAY_SIZE(pip2_attributes); j++) {
			if (device_create_file(dev, pip2_attributes + j))
				goto undo;
		}
	}
	return 0;
undo:
	for (i--; i >= 0; i--)
		device_remove_file(dev, attributes + i);
	for (j--; j >= 0; j--)
		device_remove_file(dev, pip2_attributes + j);
	pt_debug(dev, DL_ERROR, "%s: failed to create sysfs interface\n",
		__func__);
	return -ENODEV;
}

/*******************************************************************************
 * FUNCTION: remove_sysfs_interfaces
 *
 * SUMMARY: Removes all DUT dependent sysfs nodes owned by the core
 *
 * RETURN: void
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 ******************************************************************************/
static void remove_sysfs_interfaces(struct device *dev)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(attributes); i++)
		device_remove_file(dev, attributes + i);
	for (i = 0; i < ARRAY_SIZE(pip2_attributes); i++)
		device_remove_file(dev, pip2_attributes + i);
}

/*******************************************************************************
 * FUNCTION: remove_sysfs_and_modules
 *
 * SUMMARY: Removes all DUT dependent sysfs nodes and modules
 *
 * RETURN: void
 *
 * PARAMETERS:
 *	*dev  - pointer to device structure
 ******************************************************************************/
static void remove_sysfs_and_modules(struct device *dev)
{
	struct pt_core_data *cd = dev_get_drvdata(dev);

	/* Queued work should be removed before to release loader module */
	call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
	pt_release_modules(cd);
	pt_btn_release(dev);
	pt_mt_release(dev);
	remove_sysfs_interfaces(dev);
}

static int pt_ts_pinctrl_init(struct pt_core_data *cd)
{
	int retval;

	/* Get pinctrl if target uses pinctrl */
	cd->ts_pinctrl = devm_pinctrl_get(cd->dev);
	if (IS_ERR_OR_NULL(cd->ts_pinctrl)) {
		retval = PTR_ERR(cd->ts_pinctrl);
		dev_dbg(cd->dev,
			"Target does not use pinctrl %d\n", retval);
		goto err_pinctrl_get;
	}

	cd->pinctrl_state_active
		= pinctrl_lookup_state(cd->ts_pinctrl,
				PINCTRL_STATE_ACTIVE);
	if (IS_ERR_OR_NULL(cd->pinctrl_state_active)) {
		retval = PTR_ERR(cd->pinctrl_state_active);
		dev_err(cd->dev,
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_ACTIVE, retval);
		goto err_pinctrl_lookup;
	}

	cd->pinctrl_state_suspend
		= pinctrl_lookup_state(cd->ts_pinctrl,
			PINCTRL_STATE_SUSPEND);
	if (IS_ERR_OR_NULL(cd->pinctrl_state_suspend)) {
		retval = PTR_ERR(cd->pinctrl_state_suspend);
		dev_err(cd->dev,
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_SUSPEND, retval);
		goto err_pinctrl_lookup;
	}

	cd->pinctrl_state_release
		= pinctrl_lookup_state(cd->ts_pinctrl,
			PINCTRL_STATE_RELEASE);
	if (IS_ERR_OR_NULL(cd->pinctrl_state_release)) {
		retval = PTR_ERR(cd->pinctrl_state_release);
		dev_dbg(cd->dev,
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_RELEASE, retval);
	}

	return 0;

err_pinctrl_lookup:
	devm_pinctrl_put(cd->ts_pinctrl);
err_pinctrl_get:
	cd->ts_pinctrl = NULL;
	return retval;
}

void touch_notify_glink_pt_channel_state(bool state)
{
	pr_info("%s:[touch] touch_notify_glink\n", __func__);
}

void glink_touch_pt_rx_msg(void *data, int len)
{
	int rc = 0;
	pr_info("%s: TOUCH_RX_MSG Start:\n", __func__);

	if (len > TOUCH_GLINK_INTENT_SIZE) {
		pr_err("Invalid TOUCH glink intent size\n");
		return;
	}
	/* check SLATE response */
	pt_slate_resp_ack = *(uint32_t *)&data[8];
	if (pt_slate_resp_ack == 0x01) {
			pr_err("Bad SLATE response\n");
			rc = -EINVAL;
			goto err_ret;
	}
	pr_info("%s: TOUCH_RX_MSG End:\n", __func__);
err_ret:
return;
}

/*******************************************************************************
 *******************************************************************************
 * FUNCTION: pt_probe
 *
 * SUMMARY: Probe of the core module.
 *
 *	NOTE: For the Parade Technologies development platform (PtSBC) the
 *	probe functionality is split into two functions; pt_probe() and
 *	pt_probe_complete(). the initial setup is done in this function which
 *	then creates a WORK task which runs after the probe timer expires. This
 *	ensures the I2C/SPI is up on the PtSBC in time for TTDL.
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*ops           - pointer to the bus
 *	*dev           - pointer to the device structure
 *	 irq           - IRQ
 *	 xfer_buf_size - size of the buffer
 ******************************************************************************/
int pt_probe(const struct pt_bus_ops *ops, struct device *dev,
		u16 irq, size_t xfer_buf_size)
{
	struct pt_core_data *cd;
	struct pt_platform_data *pdata = dev_get_platdata(dev);
	enum pt_atten_type type;
	struct i2c_client *client = to_i2c_client(dev);
	int rc = 0;
	u8 pip_ver_major;
	u8 pip_ver_minor;
	u32 status = STARTUP_STATUS_START;

	if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) {
		pt_debug(dev, DL_ERROR, "%s: Missing platform data\n",
				__func__);
		rc = -ENODEV;
		goto error_no_pdata;
	}

	if (pdata->core_pdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
		if (!pdata->core_pdata->power) {
			pt_debug(dev, DL_ERROR,
					"%s: Missing platform data function\n",
					__func__);
			rc = -ENODEV;
			goto error_no_pdata;
		}
	}
	/* get context and debug print buffers */
	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
	if (!cd) {
		rc = -ENOMEM;
		goto error_no_pdata;
	}
	/* Initialize device info */
	cd->dev                        = dev;
	cd->pdata                      = pdata;
	cd->cpdata                     = pdata->core_pdata;
	cd->bus_ops                    = ops;
	cd->debug_level                = PT_INITIAL_DEBUG_LEVEL;
	cd->show_timestamp             = PT_INITIAL_SHOW_TIME_STAMP;
	scnprintf(cd->core_id, 20, "%s%d", PT_CORE_NAME, core_number++);
	cd->hw_detected                = false;
	cd->pip2_prot_active           = false;
	cd->pip2_send_user_cmd         = false;
	cd->bl_pip_ver_ready           = false;
	cd->app_pip_ver_ready          = false;
	cd->pip2_cmd_tag_seq           = 0x08; /* PIP2 TAG=1 and 3 bit SEQ=0 */
	cd->get_param_id               = 0;
	cd->watchdog_enabled           = 0;
	cd->startup_retry_count        = 0;
	cd->core_probe_complete        = 0;
	cd->fw_system_mode             = FW_SYS_MODE_BOOT;
	cd->pip_cmd_timeout            = PT_PIP_CMD_DEFAULT_TIMEOUT;
	cd->pip_cmd_timeout_default    = PT_PIP_CMD_DEFAULT_TIMEOUT;
	cd->flashless_dut              = 0;
	cd->flashless_auto_bl          = PT_SUPPRESS_AUTO_BL;
	cd->bl_with_no_int             = 0;
	cd->cal_cache_in_host          = PT_FEATURE_DISABLE;
	cd->multi_chip                 = PT_FEATURE_DISABLE;
	cd->tthe_hid_usb_format        = PT_FEATURE_DISABLE;
	cd->sleep_state			= SS_SLEEP_NONE;
	cd->quick_boot			= false;
	cd->drv_debug_suspend          = false;
	cd->touch_offload = false;

	if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) {
		cd->set_dut_generation = true;
		cd->active_dut_generation = DUT_PIP2_CAPABLE;
	} else if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP1_ONLY) {
		cd->set_dut_generation = true;
		cd->active_dut_generation = DUT_PIP1_ONLY;
	} else {
		cd->set_dut_generation = false;
		cd->active_dut_generation = DUT_UNKNOWN;
	}
	/* Initialize with platform data */
	cd->watchdog_force_stop        = cd->cpdata->watchdog_force_stop;
	cd->watchdog_interval          = PT_WATCHDOG_TIMEOUT;
	cd->hid_cmd_state                 = 1;
	cd->fw_updating                   = false;
	cd->multi_chip                    = 0;

#ifdef TTDL_DIAGNOSTICS
	cd->t_refresh_active              = 0;
	cd->t_refresh_count               = 0;
	cd->pip2_crc_error_count          = 0;
	cd->wd_xres_count                 = 0;
	cd->bl_retry_packet_count         = 0;
	cd->file_erase_timeout_count      = 0;
	cd->show_tt_data                  = false;
	cd->flush_bus_type                = PT_FLUSH_BUS_BASED_ON_LEN;
	cd->err_gpio                      = 0;
	cd->err_gpio_type                 = PT_ERR_GPIO_NONE;
	cd->ttdl_bist_select              = 0x07;
	cd->force_pip2_seq                = 0;
#endif /* TTDL_DIAGNOSTICS */

	memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE);
	memcpy(cd->pip2_us_file_path, PT_PIP2_BIN_FILE_PATH,
		sizeof(PT_PIP2_BIN_FILE_PATH));
	pt_init_hid_descriptor(&cd->hid_desc);
	/* Read and store the descriptor lengths */
	cd->hid_core.hid_report_desc_len =
	    le16_to_cpu(cd->hid_desc.report_desc_len);
	cd->hid_core.hid_max_input_len =
	    le16_to_cpu(cd->hid_desc.max_input_len);
	cd->hid_core.hid_max_output_len =
	    le16_to_cpu(cd->hid_desc.max_output_len);
	/* Initialize mutexes and spinlocks */
	mutex_init(&cd->module_list_lock);
	mutex_init(&cd->system_lock);
	mutex_init(&cd->sysfs_lock);
	mutex_init(&cd->ttdl_restart_lock);
	mutex_init(&cd->firmware_class_lock);
	spin_lock_init(&cd->spinlock);

	/* Initialize module list */
	INIT_LIST_HEAD(&cd->module_list);

	/* Initialize attention lists */
	for (type = 0; type < PT_ATTEN_NUM_ATTEN; type++)
		INIT_LIST_HEAD(&cd->atten_list[type]);

	/* Initialize parameter list */
	INIT_LIST_HEAD(&cd->param_list);

	/* Initialize wait queue */
	init_waitqueue_head(&cd->wait_q);
	rc = pt_ts_pinctrl_init(cd);
	if (!rc && cd->ts_pinctrl) {
		/*
		 * Pinctrl handle is optional. If pinctrl handle is found
		 * let pins to be configured in active state. If not
		 * found continue further without error.
		 */
		rc = pinctrl_select_state(cd->ts_pinctrl,
					cd->pinctrl_state_active);
		if (rc < 0)
			dev_err(&client->dev, "failed to select pin to active state\n");
	}
	rc = pt_get_regulator(cd, true);
	if (rc) {
		dev_err(&client->dev, "Failed to get voltage regulators\n");
		goto error_alloc_data;
	}

	rc = pt_enable_regulator(cd, true);
	if (rc) {
		dev_err(dev, "Failed to enable regulators: rc=%d\n", rc);
		goto error_get_regulator;
	}

	/* Initialize works */
	INIT_WORK(&cd->enum_work, pt_enum_work_function);
	INIT_WORK(&cd->ttdl_restart_work, pt_restart_work_function);
	INIT_WORK(&cd->watchdog_work, pt_watchdog_work);

	/* Initialize HID specific data */
	cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ?
		cd->cpdata->vendor_id : HID_VENDOR_ID;
	cd->hid_core.hid_product_id = (cd->cpdata->product_id) ?
		cd->cpdata->product_id : HID_APP_PRODUCT_ID;
	cd->hid_core.hid_desc_register =
		cpu_to_le16(cd->cpdata->hid_desc_register);

	/* Set platform easywake value */
	cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture;

#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
	/* Setup active dsi panel */
	active_panel = cd->cpdata->active_panel;
#endif

	/* Set platform panel_id value */
	cd->panel_id_support = cd->cpdata->panel_id_support;

	if (cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO)
		/* Set Panel ID to default to 0 */
		cd->pid_for_loader = PT_PANEL_ID_DEFAULT;
	else
		/* Set Panel ID to Not Enabled */
		cd->pid_for_loader = PANEL_ID_NOT_ENABLED;
	/* Initialize hw_version default to FFFF.FFFF.FF */
	snprintf(cd->hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");

	dev_set_drvdata(dev, cd);
	/* PtSBC builds will call this function in pt_probe_complete() */
	pt_add_core(dev);

	rc = sysfs_create_group(&dev->kobj, &early_attr_group);
	if (rc) {
		pt_debug(cd->dev, DL_ERROR, "%s:create early attrs failed\n",
			__func__);
		goto error_enable_regulator;
	}
	/*
	 * Save the pointer to a global value, which will be used
	 * in ttdl_restart function
	 */
	cd->bus_ops = ops;

	glink_touch_channel_init(&touch_notify_glink_pt_channel_state, &glink_touch_pt_rx_msg);

	/*
	 * When the IRQ GPIO is not direclty accessible and no function is
	 * defined to get the IRQ status, the IRQ passed in must be assigned
	 * directly as the gpio_to_irq will not work. e.g. CHROMEOS
	 */
	if (!cd->cpdata->irq_stat) {
		cd->irq = irq;
		pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n",
			__func__, cd->irq);
	}
	/* Call platform init function before setting up the GPIO's */
	if (cd->cpdata->init) {
		pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__);
		rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev);
	} else {
		pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n",
			__func__);
		rc = 0;
	}
	if (rc < 0) {
		pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n",
			__func__, rc);
	}
	/* Power on any needed regulator(s) */
	if (cd->cpdata->setup_power) {
		pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__);
		rc = cd->cpdata->setup_power(cd->cpdata,
			PT_MT_POWER_ON, cd->dev);
	} else {
		pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n",
			__func__);
		rc = 0;
	}
	if (rc < 0)
		pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n",
			__func__, rc);

#ifdef TTDL_DIAGNOSTICS
	cd->watchdog_irq_stuck_count = 0;
	cd->bus_transmit_error_count = 0;
#endif /* TTDL_DIAGNOSTICS */
		if (cd->cpdata->detect) {
			pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__);
			rc = cd->cpdata->detect(cd->cpdata, cd->dev,
					pt_platform_detect_read);
			if (!rc) {
				cd->hw_detected = true;
				pt_debug(cd->dev, DL_INFO,
					"%s: HW detected\n", __func__);
			} else {
				cd->hw_detected = false;
				pt_debug(cd->dev, DL_INFO,
					"%s: No HW detected\n", __func__);
				rc = -ENODEV;
				goto error_detect;
			}
		} else {
			pt_debug(dev, DL_ERROR,
				"%s: PARADE No HW detect function pointer\n",
				__func__);
			/*
			 * "hw_reset" is not needed in the "if" statement,
			 * because "hw_reset" is already included in "hw_detect"
			 * function.
			 */
			rc = pt_hw_hard_reset(cd);
			if (rc)
				pt_debug(cd->dev, DL_ERROR,
					"%s: FAILED to execute HARD reset\n",
					__func__);
		}

	if (cd->cpdata->setup_irq) {
		pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__);
		rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev);
		if (rc) {
			pt_debug(dev, DL_ERROR,
				"%s: Error, couldn't setup IRQ\n", __func__);
			goto error_setup_irq;
		}
	} else {
		pt_debug(dev, DL_ERROR,
				"%s: IRQ function pointer not setup\n",
				__func__);
		goto error_setup_irq;
	}

#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
	setup_timer(&cd->watchdog_timer, pt_watchdog_timer,
			(unsigned long)cd);
#else
	timer_setup(&cd->watchdog_timer, pt_watchdog_timer, 0);
#endif
	pt_stop_wd_timer(cd);
#ifdef TTHE_TUNER_SUPPORT
	mutex_init(&cd->tthe_lock);
	cd->tthe_debugfs = debugfs_create_file(PT_TTHE_TUNER_FILE_NAME,
			0644, NULL, cd, &tthe_debugfs_fops);
#endif
	rc = device_init_wakeup(dev, 1);
	if (rc < 0)
		pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n",
			__func__, rc);

	if (!enable_irq_wake(cd->irq)) {
		cd->irq_wake = 1;
		pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__);
	}
	pm_runtime_get_noresume(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);
	/* If IRQ asserted, read out all from buffer to release INT pin */
	if (cd->cpdata->irq_stat) {
		pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
	} else {
		/* Force a read in case the reset sentinel already arrived */
		rc = pt_read_input(cd);
		if (!rc)
			pt_parse_input(cd);
	}
	/* Without sleep DUT is not ready and will NAK the first write */
	msleep(150);
	/* Attempt to set the DUT generation if not yet set */
	if (cd->active_dut_generation == DUT_UNKNOWN) {
		if (cd->bl_pip_ver_ready ||
			(cd->app_pip_ver_ready &&
			IS_PIP_VER_GE(&cd->sysinfo, 1, 12))) {
			cd->active_dut_generation = DUT_PIP2_CAPABLE;
			pt_debug(dev, DL_INFO, "%s: dut generation is %d\n",
				__func__, cd->active_dut_generation);
		} else {
			rc = _pt_detect_dut_generation(cd->dev,
				&status, &cd->active_dut_generation,
				&cd->mode);
			if ((cd->active_dut_generation == DUT_UNKNOWN)
				|| rc) {
				pt_debug(cd->dev, DL_ERROR,
					" === DUT Gen unknown, Skip Enum ===\n");
				goto skip_enum;
			}
		}
	}
	_pt_request_active_pip_protocol(cd->dev, PT_CORE_CMD_PROTECTED,
		&pip_ver_major, &pip_ver_minor);
	if (pip_ver_major == 2) {
		cd->bl_pip_ver_ready = true;
		pt_debug(dev, DL_ERROR,
			" === PIP2.%d Detected, Attempt to launch APP ===\n",
			pip_ver_minor);
		cd->hw_detected = true;
	} else if (pip_ver_major == 1) {
		cd->app_pip_ver_ready = true;
		pt_debug(dev, DL_ERROR,
			" === PIP1.%d Detected ===\n", pip_ver_minor);
		cd->hw_detected = true;
	} else {
		cd->sysinfo.ttdata.pip_ver_major = 0;
		cd->sysinfo.ttdata.pip_ver_minor = 0;
		cd->app_pip_ver_ready = false;
		cd->hw_detected = false;
		pt_debug(dev, DL_ERROR,
			" === PIP Version Not Detected, Skip Enum ===\n");
		/* For legacy DUTS proceed, enum will attempt to launch app */
		if (cd->active_dut_generation != DUT_PIP1_ONLY)
			goto skip_enum;
	}
	rc = pt_enum_with_dut(cd, false, &status);
	pt_debug(dev, DL_ERROR, "%s: cd->startup_status=0x%04X status=0x%04X\n",
		__func__, cd->startup_status, status);
	if (rc == -ENODEV) {
		pt_debug(cd->dev, DL_ERROR,
			"%s: Enumeration Failed r=%d\n", __func__, rc);
		/* For PtSBC don't error out, allow TTDL to stay up */
		rc = -EPROBE_DEFER;
		goto error_after_startup;
	}
	/* Suspend scanning until probe is complete to avoid asyc touches */
	pt_pip_suspend_scanning_(cd);

	if (cd->hw_detected) {
		pt_debug(dev, DL_INFO, "%s: Add sysfs interfaces\n",
			__func__);
		rc = add_sysfs_interfaces(dev);
		if (rc < 0) {
			pt_debug(dev, DL_ERROR,
				"%s: Error, fail sysfs init\n", __func__);
			goto error_after_startup;
		}
	} else {
		pt_debug(dev, DL_ERROR,
			"%s: No HW detected, sysfs interfaces not added\n",
			__func__);
	}
skip_enum:
	pm_runtime_put_sync(dev);

	pt_debug(dev, DL_INFO, "%s: Probe: MT, BTN\n", __func__);
	rc = pt_mt_probe(dev);
	if (rc < 0) {
		pt_debug(dev, DL_ERROR, "%s: Error, fail mt probe\n",
			__func__);
		goto error_after_sysfs_create;
	}
	rc = pt_btn_probe(dev);
	if (rc < 0) {
		pt_debug(dev, DL_ERROR, "%s: Error, fail btn probe\n",
			__func__);
		goto error_after_startup_mt;
	}
	pt_probe_modules(cd);

#ifdef CONFIG_HAS_EARLYSUSPEND
	pt_setup_early_suspend(cd);
#elif defined(CONFIG_PANEL_NOTIFIER)
	pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__);
	pt_setup_panel_event_notifier(cd);
	INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work);
	INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work);
#elif defined(CONFIG_DRM)
	pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__);
	pt_setup_drm_notifier(cd);
	INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work);
	INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work);
#elif defined(CONFIG_FB)
	pt_setup_fb_notifier(cd);
#endif

#ifdef NEED_SUSPEND_NOTIFIER
	cd->pm_notifier.notifier_call = pt_pm_notifier;
	register_pm_notifier(&cd->pm_notifier);
#endif
	pt_pip_resume_scanning_(cd);
	mutex_lock(&cd->system_lock);
	cd->startup_status |= status;
	cd->core_probe_complete = 1;
	mutex_unlock(&cd->system_lock);

	pt_core_state = STATE_RESUME;
	pt_debug(dev, DL_ERROR, "%s: TTDL Core Probe Completed Successfully\n",
		__func__);
	return 0;


error_after_startup_mt:
	pr_err("%s PARADE error_after_startup_mt\n", __func__);
	pt_mt_release(dev);
error_after_sysfs_create:
	pr_err("%s PARADE error_after_sysfs_create\n", __func__);
	pm_runtime_disable(dev);
#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
	device_wakeup_disable(dev);
#endif
	device_init_wakeup(dev, 0);
	cancel_work_sync(&cd->enum_work);
	pt_stop_wd_timer(cd);
	pt_free_si_ptrs(cd);
	remove_sysfs_interfaces(dev);

error_after_startup:
	pr_err("%s PARADE error_after_startup\n", __func__);
	del_timer(&cd->watchdog_timer);
	if (cd->cpdata->setup_irq)
		cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);
error_setup_irq:
error_detect:
	if (cd->cpdata->init)
		cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
	if (cd->cpdata->setup_power)
		cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
	sysfs_remove_group(&dev->kobj, &early_attr_group);
error_enable_regulator:
	pt_del_core(dev);
	dev_set_drvdata(dev, NULL);
	pt_enable_regulator(cd, false);
error_get_regulator:
	pt_get_regulator(cd, false);
error_alloc_data:
	kfree(cd);
error_no_pdata:
	pr_err("%s failed.\n", __func__);
	return rc;
}

/*******************************************************************************
 * FUNCTION: pt_device_entry
 *
 * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being
 *  woke up or put to sleep based on Kernel power state even when the display
 *  is off based on the check of TTDL core platform flag.
 *
 * RETURN:
 *        0 = success
 *       !0 = failure
 *
 * PARAMETERS:
 *      *dev  - pointer to core device
 ******************************************************************************/
#ifdef PT_AMBIENT_MODE
int pt_device_entry(struct device *dev,
				u16 irq, size_t xfer_buf_size)
{
		struct pt_core_data *cd = dev_get_drvdata(dev);
		struct pt_platform_data *pdata = dev_get_platdata(dev);
		struct i2c_client *client = to_i2c_client(dev);
		int rc = 0;
		void *glink_pt_send_msg;
		int glink_touch_exit = TOUCH_EXIT;

		pt_debug(dev, DL_INFO, "%s: Start pt_device_entry\n", __func__);

		cd->dev  	= dev;
		cd->pdata   = pdata;
		cd->cpdata  = pdata->core_pdata;

		glink_pt_send_msg = &glink_touch_exit;
		pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %d\n", glink_pt_send_msg);
		glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE);

		msleep(150);

		if (!rc && cd->ts_pinctrl) {
				/*
				 * Pinctrl handle is optional. If pinctrl handle is found
				 * let pins to be configured in active state. If not
				 * found continue further without error.
				 */
			rc = pinctrl_select_state(cd->ts_pinctrl, cd->pinctrl_state_active);
			if (rc < 0)
				dev_err(&client->dev, "failed to select pin to active state\n");
		}

		/* Set platform easywake value */
		cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture;

	   /*
		* When the IRQ GPIO is not direclty accessible and no function is
		* defined to get the IRQ status, the IRQ passed in must be assigned
		* directly as the gpio_to_irq will not work. e.g. CHROMEOS
		*/

		if (!cd->cpdata->irq_stat) {
			cd->irq = irq;
			pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n", __func__, cd->irq);
		}

		/* Call platform init function before setting up the GPIO's */
		if (cd->cpdata->init) {
			pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__);
			rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev);
		} else {
			pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n", __func__);
			rc = 0;
		}
		if (rc < 0) {
			pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n", __func__, rc);
		}

		/* Power on any needed regulator(s) */
		if (cd->cpdata->setup_power) {
				pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__);
				rc = cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_ON, cd->dev);
		} else {
				pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n", __func__);
				rc = 0;
		}
		if (rc < 0)
			pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n", __func__, rc);

		if (cd->cpdata->detect) {
			pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__);
			rc = cd->cpdata->detect(cd->cpdata, cd->dev, pt_platform_detect_read);
			if (!rc) {
					cd->hw_detected = true;
					pt_debug(cd->dev, DL_INFO, "%s: HW detected\n", __func__);
			} else {
					cd->hw_detected = false;
					pt_debug(cd->dev, DL_INFO, "%s: No HW detected\n", __func__);
					rc = -ENODEV;
					goto pt_error_detect;
			}
		} else {
				pt_debug(dev, DL_ERROR,	"%s: PARADE No HW detect function pointer\n", __func__);
				/*
				 * "hw_reset" is not needed in the "if" statement,
				 * because "hw_reset" is already included in "hw_detect"
				 * function.
				 */
				rc = pt_hw_hard_reset(cd);
				if (rc)
					pt_debug(cd->dev, DL_ERROR,	"%s: FAILED to execute HARD reset\n", __func__);
		}

			if (cd->cpdata->setup_irq) {
				pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__);
				rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev);
				if (rc) {
					pt_debug(dev, DL_ERROR,	"%s: Error, couldn't setup IRQ\n", __func__);
					goto pt_error_setup_irq;
				}
			} else {
				pt_debug(dev, DL_ERROR,	"%s: IRQ function pointer not setup\n", __func__);
				goto pt_error_setup_irq;
			}

			rc = device_init_wakeup(dev, 1);
			if (rc < 0)
				pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n", __func__, rc);

			if (!enable_irq_wake(cd->irq)) {
				cd->irq_wake = 1;
				pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__);
			}

			pm_runtime_get_noresume(dev);
			pm_runtime_set_active(dev);
			pm_runtime_enable(dev);

			/* Without sleep DUT is not ready and will NAK the first write */
			msleep(150);

			pm_runtime_put_sync(dev);

#if defined(CONFIG_PANEL_NOTIFIER)
		/* Setup active dsi panel */
		active_panel = cd->cpdata->active_panel;


		pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__);
		pt_setup_panel_event_notifier(cd);
#endif

		mutex_lock(&cd->system_lock);
		cd->core_probe_complete = 1;
		mutex_unlock(&cd->system_lock);

		pt_debug(dev, DL_INFO, "%s: ####TTDL Core Device Probe Completed Successfully\n", __func__);
		pt_core_state = STATE_RESUME;
		return 0;

pt_error_setup_irq:
		device_init_wakeup(dev, 0);
pt_error_detect:
		if (cd->cpdata->init)
			cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
		if (cd->cpdata->setup_power)
			cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
		return rc;
}
#endif

EXPORT_SYMBOL_GPL(pt_probe);

/*******************************************************************************
 * FUNCTION: pt_release
 *
 * SUMMARY: This function does the following cleanup:
 *	- Releases all probed modules
 *	- Stops the watchdog
 *	- Cancels all pending work tasks
 *	- Removes all created sysfs nodes
 *	- Removes all created debugfs nodes
 *	- Frees the IRQ
 *	- Deletes the core
 *	- Frees all pointers and HID reports
 *
 * RETURN:
 *	 0 = success
 *	!0 = failure
 *
 * PARAMETERS:
 *	*cd - pointer to the core data structure
 ******************************************************************************/
int pt_release(struct pt_core_data *cd)
{
	struct device *dev = cd->dev;

	/*
	 * Suspend the device before freeing the startup_work and stopping
	 * the watchdog since sleep function restarts watchdog on failure
	 */
	pm_runtime_suspend(dev);
	pm_runtime_disable(dev);

	/*
	 * Any 'work' that can trigger a new thread should be canceled first.
	 * The watchdog is also stopped again because another thread could have
	 * restarted it. The irq_work is cancelled last because it is used for
	 * all I2C/SPI communication.
	 */
	pt_stop_wd_timer(cd);
	call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
	cancel_work_sync(&cd->ttdl_restart_work);
	cancel_work_sync(&cd->enum_work);
	cancel_work_sync(&cd->resume_offload_work);
	cancel_work_sync(&cd->suspend_offload_work);
	cancel_work_sync(&cd->resume_work);
	cancel_work_sync(&cd->suspend_work);
	destroy_workqueue(cd->pt_workqueue);

	pt_stop_wd_timer(cd);

	pt_release_modules(cd);
	pt_proximity_release(dev);
	pt_btn_release(dev);
	pt_mt_release(dev);

#ifdef CONFIG_HAS_EARLYSUSPEND
	unregister_early_suspend(&cd->es);
#elif defined(CONFIG_PANEL_NOTIFIER)
	if (active_panel)
		panel_event_notifier_unregister(cd->entry);
#elif defined(CONFIG_DRM)
	if (active_panel)
		drm_panel_notifier_unregister(active_panel, &cd->fb_notifier);
#elif defined(CONFIG_FB)
	fb_unregister_client(&cd->fb_notifier);
#endif

#ifdef NEED_SUSPEND_NOTIFIER
	unregister_pm_notifier(&cd->pm_notifier);
#endif

#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
	device_wakeup_disable(dev);
#endif
	device_init_wakeup(dev, 0);

#ifdef TTHE_TUNER_SUPPORT
	mutex_lock(&cd->tthe_lock);
	cd->tthe_exit = 1;
	wake_up(&cd->wait_q);
	mutex_unlock(&cd->tthe_lock);
	debugfs_remove(cd->tthe_debugfs);
#endif

	sysfs_remove_group(&dev->kobj, &early_attr_group);

	remove_sysfs_interfaces(dev);

	disable_irq_nosync(cd->irq);
	if (cd->cpdata->setup_irq)
		cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);
	if (cd->cpdata->init)
		cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
	if (cd->cpdata->setup_power)
		cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
	dev_set_drvdata(dev, NULL);
	pt_del_core(dev);
	if (cd->vcc_i2c)
		regulator_set_load(cd->vcc_i2c, 0);

	if (cd->vdd)
		regulator_set_load(cd->vdd, 0);
	pt_enable_regulator(cd, false);
	pt_get_regulator(cd, false);
	pt_free_si_ptrs(cd);
	kfree(cd);
	return 0;
}
EXPORT_SYMBOL_GPL(pt_release);


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver");
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");